summaryrefslogtreecommitdiffstats
path: root/src/libnr
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/libnr
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/libnr')
-rw-r--r--src/libnr/.cvsignore13
-rw-r--r--src/libnr/Makefile_insert167
-rw-r--r--src/libnr/have_mmx.S47
-rw-r--r--src/libnr/in-svg-plane-test.cpp58
-rw-r--r--src/libnr/in-svg-plane-test.h81
-rw-r--r--src/libnr/in-svg-plane.h33
-rw-r--r--src/libnr/libnr.def89
-rw-r--r--src/libnr/makefile.in17
-rw-r--r--src/libnr/n-art-bpath.h61
-rw-r--r--src/libnr/nr-blit.cpp300
-rw-r--r--src/libnr/nr-blit.h32
-rw-r--r--src/libnr/nr-compose-transform.cpp360
-rw-r--r--src/libnr/nr-compose-transform.h43
-rw-r--r--src/libnr/nr-compose.cpp986
-rw-r--r--src/libnr/nr-compose.h69
-rw-r--r--src/libnr/nr-convex-hull-ops.h29
-rw-r--r--src/libnr/nr-convex-hull.h49
-rw-r--r--src/libnr/nr-coord.h29
-rw-r--r--src/libnr/nr-dim2.h22
-rw-r--r--src/libnr/nr-forward.h42
-rw-r--r--src/libnr/nr-gradient.cpp317
-rw-r--r--src/libnr/nr-gradient.h47
-rw-r--r--src/libnr/nr-i-coord.h25
-rw-r--r--src/libnr/nr-macros.h71
-rw-r--r--src/libnr/nr-matrix-div.cpp22
-rw-r--r--src/libnr/nr-matrix-div.h21
-rw-r--r--src/libnr/nr-matrix-fns.cpp54
-rw-r--r--src/libnr/nr-matrix-fns.h50
-rw-r--r--src/libnr/nr-matrix-ops.h45
-rw-r--r--src/libnr/nr-matrix-rotate-ops.cpp18
-rw-r--r--src/libnr/nr-matrix-rotate-ops.h20
-rw-r--r--src/libnr/nr-matrix-scale-ops.cpp37
-rw-r--r--src/libnr/nr-matrix-scale-ops.h14
-rw-r--r--src/libnr/nr-matrix-test.cpp197
-rw-r--r--src/libnr/nr-matrix-test.h221
-rw-r--r--src/libnr/nr-matrix-translate-ops.cpp26
-rw-r--r--src/libnr/nr-matrix-translate-ops.h36
-rw-r--r--src/libnr/nr-matrix.cpp607
-rw-r--r--src/libnr/nr-matrix.h453
-rw-r--r--src/libnr/nr-maybe.h140
-rw-r--r--src/libnr/nr-object.cpp295
-rw-r--r--src/libnr/nr-object.h157
-rw-r--r--src/libnr/nr-path-code.h28
-rw-r--r--src/libnr/nr-path.cpp461
-rw-r--r--src/libnr/nr-path.h62
-rw-r--r--src/libnr/nr-pixblock-line.cpp92
-rw-r--r--src/libnr/nr-pixblock-line.h28
-rw-r--r--src/libnr/nr-pixblock-pattern.cpp126
-rw-r--r--src/libnr/nr-pixblock-pattern.h28
-rw-r--r--src/libnr/nr-pixblock-pixel.cpp230
-rw-r--r--src/libnr/nr-pixblock-pixel.h28
-rw-r--r--src/libnr/nr-pixblock.cpp411
-rw-r--r--src/libnr/nr-pixblock.h95
-rw-r--r--src/libnr/nr-pixops.h46
-rw-r--r--src/libnr/nr-point-fns-test.cpp107
-rw-r--r--src/libnr/nr-point-fns-test.h141
-rw-r--r--src/libnr/nr-point-fns.cpp74
-rw-r--r--src/libnr/nr-point-fns.h104
-rw-r--r--src/libnr/nr-point-l.h95
-rw-r--r--src/libnr/nr-point-matrix-ops.h47
-rw-r--r--src/libnr/nr-point-ops.h88
-rw-r--r--src/libnr/nr-point.h159
-rw-r--r--src/libnr/nr-rect-l.cpp22
-rw-r--r--src/libnr/nr-rect-l.h131
-rw-r--r--src/libnr/nr-rect-ops.h51
-rw-r--r--src/libnr/nr-rect.cpp233
-rw-r--r--src/libnr/nr-rect.h255
-rw-r--r--src/libnr/nr-render.h25
-rw-r--r--src/libnr/nr-rotate-fns-test.cpp43
-rw-r--r--src/libnr/nr-rotate-fns-test.h54
-rw-r--r--src/libnr/nr-rotate-fns.cpp66
-rw-r--r--src/libnr/nr-rotate-fns.h29
-rw-r--r--src/libnr/nr-rotate-matrix-ops.cpp19
-rw-r--r--src/libnr/nr-rotate-matrix-ops.h21
-rw-r--r--src/libnr/nr-rotate-ops.h43
-rw-r--r--src/libnr/nr-rotate-test.cpp89
-rw-r--r--src/libnr/nr-rotate-test.h112
-rw-r--r--src/libnr/nr-rotate.h66
-rw-r--r--src/libnr/nr-scale-matrix-ops.cpp26
-rw-r--r--src/libnr/nr-scale-matrix-ops.h13
-rw-r--r--src/libnr/nr-scale-ops.h40
-rw-r--r--src/libnr/nr-scale-test.cpp71
-rw-r--r--src/libnr/nr-scale-test.h92
-rw-r--r--src/libnr/nr-scale-translate-ops.cpp19
-rw-r--r--src/libnr/nr-scale-translate-ops.h20
-rw-r--r--src/libnr/nr-scale.h50
-rw-r--r--src/libnr/nr-svp-private.h30
-rw-r--r--src/libnr/nr-svp-render.cpp615
-rw-r--r--src/libnr/nr-svp-render.h30
-rw-r--r--src/libnr/nr-svp.cpp180
-rw-r--r--src/libnr/nr-svp.h65
-rw-r--r--src/libnr/nr-translate-matrix-ops.cpp27
-rw-r--r--src/libnr/nr-translate-matrix-ops.h22
-rw-r--r--src/libnr/nr-translate-ops.h43
-rw-r--r--src/libnr/nr-translate-rotate-ops.cpp20
-rw-r--r--src/libnr/nr-translate-rotate-ops.h21
-rw-r--r--src/libnr/nr-translate-scale-ops.cpp25
-rw-r--r--src/libnr/nr-translate-scale-ops.h20
-rw-r--r--src/libnr/nr-translate-test.cpp65
-rw-r--r--src/libnr/nr-translate-test.h87
-rw-r--r--src/libnr/nr-translate.h34
-rw-r--r--src/libnr/nr-types-test.cpp105
-rw-r--r--src/libnr/nr-types-test.h145
-rw-r--r--src/libnr/nr-types.cpp68
-rw-r--r--src/libnr/nr-types.h47
-rw-r--r--src/libnr/nr-values.cpp23
-rw-r--r--src/libnr/nr-values.h45
-rw-r--r--src/libnr/nr_config.h.mingw12
-rw-r--r--src/libnr/nr_config.h.win3214
-rw-r--r--src/libnr/nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP.S125
-rw-r--r--src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP.S231
-rw-r--r--src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S414
-rw-r--r--src/libnr/nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P.S227
-rw-r--r--src/libnr/testnr.cpp92
114 files changed, 12272 insertions, 0 deletions
diff --git a/src/libnr/.cvsignore b/src/libnr/.cvsignore
new file mode 100644
index 000000000..4191391ae
--- /dev/null
+++ b/src/libnr/.cvsignore
@@ -0,0 +1,13 @@
+Makefile
+Makefile.in
+.deps
+.libs
+nr_config.h
+testnr
+gen_nr_config
+makefile
+.dirstamp
+*-test
+test-nr
+test-nr.cpp
+test-nr-main.cpp
diff --git a/src/libnr/Makefile_insert b/src/libnr/Makefile_insert
new file mode 100644
index 000000000..541432193
--- /dev/null
+++ b/src/libnr/Makefile_insert
@@ -0,0 +1,167 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+libnr/all: libnr/libnr.a
+
+libnr/clean:
+ rm -f libnr/libnr.a libnr/libtest-nr.a $(libnr_libnr_a_OBJECTS) $(libnr_libtest_nr_a_OBJECTS)
+
+if USE_MMX
+libnr_mmx_sources = \
+ libnr/have_mmx.S \
+ libnr/nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP.S \
+ libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP.S \
+ libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S \
+ libnr/nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P.S
+endif
+
+libnr_libnr_a_SOURCES = \
+ libnr/in-svg-plane.h \
+ libnr/n-art-bpath.h \
+ libnr/nr-blit.cpp \
+ libnr/nr-blit.h \
+ libnr/nr-compose-transform.cpp \
+ libnr/nr-compose-transform.h \
+ libnr/nr-compose.cpp \
+ libnr/nr-compose.h \
+ libnr/nr-convex-hull.h \
+ libnr/nr-coord.h \
+ libnr/nr-dim2.h \
+ libnr/nr-forward.h \
+ libnr/nr-gradient.cpp \
+ libnr/nr-gradient.h \
+ libnr/nr-i-coord.h \
+ libnr/nr-macros.h \
+ libnr/nr-matrix-div.cpp \
+ libnr/nr-matrix-div.h \
+ libnr/nr-matrix-fns.cpp \
+ libnr/nr-matrix-fns.h \
+ libnr/nr-matrix-ops.h \
+ libnr/nr-matrix-rotate-ops.cpp \
+ libnr/nr-matrix-rotate-ops.h \
+ libnr/nr-matrix-scale-ops.cpp \
+ libnr/nr-matrix-scale-ops.h \
+ libnr/nr-matrix-translate-ops.cpp \
+ libnr/nr-matrix-translate-ops.h \
+ libnr/nr-matrix.cpp \
+ libnr/nr-matrix.h \
+ libnr/nr-maybe.h \
+ libnr/nr-object.cpp \
+ libnr/nr-object.h \
+ libnr/nr-path.cpp \
+ libnr/nr-path.h \
+ libnr/nr-path-code.h \
+ libnr/nr-pixblock-line.cpp \
+ libnr/nr-pixblock-line.h \
+ libnr/nr-pixblock-pattern.cpp \
+ libnr/nr-pixblock-pattern.h \
+ libnr/nr-pixblock-pixel.cpp \
+ libnr/nr-pixblock-pixel.h \
+ libnr/nr-pixblock.cpp \
+ libnr/nr-pixblock.h \
+ libnr/nr-pixops.h \
+ libnr/nr-point-fns.cpp \
+ libnr/nr-point-fns.h \
+ libnr/nr-point-l.h \
+ libnr/nr-point-matrix-ops.h \
+ libnr/nr-point-ops.h \
+ libnr/nr-point.h \
+ libnr/nr-rect-l.cpp \
+ libnr/nr-rect-l.h \
+ libnr/nr-rect.cpp \
+ libnr/nr-rect.h \
+ libnr/nr-rect-ops.h \
+ libnr/nr-render.h \
+ libnr/nr-rotate-fns.cpp \
+ libnr/nr-rotate-fns.h \
+ libnr/nr-rotate-ops.h \
+ libnr/nr-rotate-matrix-ops.cpp \
+ libnr/nr-rotate-matrix-ops.h \
+ libnr/nr-rotate.h \
+ libnr/nr-scale-matrix-ops.cpp \
+ libnr/nr-scale-matrix-ops.h \
+ libnr/nr-scale-translate-ops.cpp \
+ libnr/nr-scale-translate-ops.h \
+ libnr/nr-scale-ops.h \
+ libnr/nr-scale.h \
+ libnr/nr-svp-private.h \
+ libnr/nr-svp-render.cpp \
+ libnr/nr-svp-render.h \
+ libnr/nr-svp.cpp \
+ libnr/nr-svp.h \
+ libnr/nr-translate-matrix-ops.cpp \
+ libnr/nr-translate-matrix-ops.h \
+ libnr/nr-translate-scale-ops.cpp \
+ libnr/nr-translate-scale-ops.h \
+ libnr/nr-translate-ops.h \
+ libnr/nr-translate.h \
+ libnr/nr-translate-rotate-ops.cpp \
+ libnr/nr-translate-rotate-ops.h \
+ libnr/nr-types.cpp \
+ libnr/nr-types.h \
+ libnr/nr-values.cpp \
+ libnr/nr-values.h \
+ $(libnr_mmx_sources)
+
+libnr_testnr_SOURCES = \
+ libnr/testnr.cpp
+
+libnr_testnr_LDADD = \
+ libnr/libnr.a \
+ -lglib-2.0
+
+
+libnr/test-nr-main.cpp: libnr/test-nr.cpp
+ $(top_srcdir)/cxxtest/cxxtestgen.pl --error-printer -root -o libnr/test-nr-main.cpp $(libnr_test_nr_includes)
+
+libnr/test-nr.cpp: $(libnr_test_nr_includes)
+ $(top_srcdir)/cxxtest/cxxtestgen.pl -part -o libnr/test-nr.cpp $(libnr_test_nr_includes)
+
+libnr_test_nr_includes = \
+ $(srcdir)/libnr/nr-types-test.h \
+ $(srcdir)/libnr/nr-translate-test.h \
+ $(srcdir)/libnr/nr-rotate-test.h \
+ $(srcdir)/libnr/nr-scale-test.h \
+ $(srcdir)/libnr/nr-point-fns-test.h \
+ $(srcdir)/libnr/nr-rotate-fns-test.h \
+ $(srcdir)/libnr/in-svg-plane-test.h \
+ $(srcdir)/libnr/nr-matrix-test.h
+
+libnr_libtest_nr_a_SOURCES = \
+ libnr/test-nr.cpp \
+ $(libnr_test_nr_includes)
+
+libnr_test_nr_SOURCES = \
+ libnr/test-nr-main.cpp \
+ $(libnr_test_nr_includes)
+
+libnr_test_nr_LDADD = \
+ libnr/libnr.a \
+ libnr/libtest-nr.a \
+ -lglib-2.0
+
+# -L/usr/X11R6/lib
+# -lX11
+
+libnr_in_svg_plane_test_SOURCES = libnr/in-svg-plane-test.cpp
+libnr_in_svg_plane_test_LDADD = libnr/libnr.a -lglib-2.0
+
+libnr_nr_types_test_SOURCES = libnr/nr-types-test.cpp
+libnr_nr_types_test_LDADD = libnr/libnr.a -lglib-2.0
+
+libnr_nr_point_fns_test_SOURCES = libnr/nr-point-fns-test.cpp
+libnr_nr_point_fns_test_LDADD = libnr/libnr.a -lglib-2.0
+
+libnr_nr_matrix_test_SOURCES = libnr/nr-matrix-test.cpp
+libnr_nr_matrix_test_LDADD = libnr/libnr.a -lglib-2.0
+
+libnr_nr_rotate_test_SOURCES = libnr/nr-rotate-test.cpp
+libnr_nr_rotate_test_LDADD = libnr/libnr.a -lglib-2.0
+
+libnr_nr_rotate_fns_test_SOURCES = libnr/nr-rotate-fns-test.cpp
+libnr_nr_rotate_fns_test_LDADD = libnr/libnr.a -lglib-2.0
+
+libnr_nr_scale_test_SOURCES = libnr/nr-scale-test.cpp
+libnr_nr_scale_test_LDADD = libnr/libnr.a -lglib-2.0
+
+libnr_nr_translate_test_SOURCES = libnr/nr-translate-test.cpp
+libnr_nr_translate_test_LDADD = libnr/libnr.a -lglib-2.0
diff --git a/src/libnr/have_mmx.S b/src/libnr/have_mmx.S
new file mode 100644
index 000000000..d6428191e
--- /dev/null
+++ b/src/libnr/have_mmx.S
@@ -0,0 +1,47 @@
+ .file "have_mmx.S"
+
+# Ensure Inkscape is execshield protected
+ .section .note.GNU-stack
+ .previous
+
+ .version "01.01"
+gcc2_compiled.:
+.text
+ .align 16
+.globl nr_have_mmx
+ .type nr_have_mmx,@function
+
+nr_have_mmx:
+ push %ebx
+
+# Check if bit 21 in flags word is writeable
+
+ pushfl
+ popl %eax
+ movl %eax,%ebx
+ xorl $0x00200000, %eax
+ pushl %eax
+ popfl
+ pushfl
+ popl %eax
+
+ cmpl %eax, %ebx
+
+ je .notfound
+
+# OK, we have CPUID
+
+ movl $1, %eax
+ cpuid
+
+ test $0x00800000, %edx
+ jz .notfound
+
+ movl $1, %eax
+ jmp .out
+
+.notfound:
+ movl $0, %eax
+.out:
+ popl %ebx
+ ret
diff --git a/src/libnr/in-svg-plane-test.cpp b/src/libnr/in-svg-plane-test.cpp
new file mode 100644
index 000000000..f5620c32b
--- /dev/null
+++ b/src/libnr/in-svg-plane-test.cpp
@@ -0,0 +1,58 @@
+#include <glib/gmacros.h>
+#include <cmath>
+
+#include "libnr/in-svg-plane.h"
+#include "utest/utest.h"
+#include "isnan.h"
+
+int main(int argc, char *argv[])
+{
+ utest_start("in-svg-plane.h");
+
+ NR::Point const p3n4(3.0, -4.0);
+ NR::Point const p0(0.0, 0.0);
+ double const small = pow(2.0, -1070);
+ double const inf = 1e400;
+ double const nan = inf - inf;
+
+ NR::Point const small_left(-small, 0.0);
+ NR::Point const small_n3_4(-3.0 * small, 4.0 * small);
+ NR::Point const part_nan(3., nan);
+
+ assert(isNaN(nan));
+ assert(!isNaN(small));
+
+ UTEST_TEST("in_svg_plane") {
+ UTEST_ASSERT(in_svg_plane(p3n4));
+ UTEST_ASSERT(in_svg_plane(p0));
+ UTEST_ASSERT(in_svg_plane(small_left));
+ UTEST_ASSERT(in_svg_plane(small_n3_4));
+ UTEST_ASSERT(nan != nan);
+ UTEST_ASSERT(!in_svg_plane(NR::Point(nan, 3.)));
+ UTEST_ASSERT(!in_svg_plane(NR::Point(inf, nan)));
+ UTEST_ASSERT(!in_svg_plane(NR::Point(0., -inf)));
+ double const xs[] = {inf, -inf, nan, 1., -2., small, -small};
+ for (unsigned i = 0; i < G_N_ELEMENTS(xs); ++i) {
+ for (unsigned j = 0; j < G_N_ELEMENTS(xs); ++j) {
+ UTEST_ASSERT( in_svg_plane(NR::Point(xs[i], xs[j]))
+ == (fabs(xs[i]) < inf &&
+ fabs(xs[j]) < inf ) );
+ }
+ }
+ }
+
+ return ( utest_end()
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE );
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/in-svg-plane-test.h b/src/libnr/in-svg-plane-test.h
new file mode 100644
index 000000000..6cd29bd6a
--- /dev/null
+++ b/src/libnr/in-svg-plane-test.h
@@ -0,0 +1,81 @@
+#include <cxxtest/TestSuite.h>
+
+#include <glib/gmacros.h>
+#include <cmath>
+
+#include "libnr/in-svg-plane.h"
+#include "isnan.h"
+
+class InSvgPlaneTest : public CxxTest::TestSuite
+{
+public:
+
+ InSvgPlaneTest() :
+ setupValid(true),
+ p3n4( 3.0, -4.0 ),
+ p0(0.0, 0.0),
+ small( pow(2.0, -1070) ),
+ inf( 1e400 ),
+ nan( inf - inf ),
+ small_left( -small, 0.0 ),
+ small_n3_4( -3.0 * small, 4.0 * small ),
+ part_nan( 3., nan )
+ {
+ setupValid &= isNaN(nan);
+ setupValid &= !isNaN(small);
+ }
+ virtual ~InSvgPlaneTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static InSvgPlaneTest *createSuite() { return new InSvgPlaneTest(); }
+ static void destroySuite( InSvgPlaneTest *suite ) { delete suite; }
+
+// Called before each test in this suite
+ void setUp()
+ {
+ TS_ASSERT( setupValid );
+ }
+
+ bool setupValid;
+ NR::Point const p3n4;
+ NR::Point const p0;
+ double const small;
+ double const inf;
+ double const nan;
+ NR::Point const small_left;
+ NR::Point const small_n3_4;
+ NR::Point const part_nan;
+
+
+ void testInSvgPlane(void)
+ {
+ TS_ASSERT( in_svg_plane(p3n4) );
+ TS_ASSERT( in_svg_plane(p0) );
+ TS_ASSERT( in_svg_plane(small_left) );
+ TS_ASSERT( in_svg_plane(small_n3_4) );
+ TS_ASSERT_DIFFERS( nan, nan );
+ TS_ASSERT( !in_svg_plane(NR::Point(nan, 3.)) );
+ TS_ASSERT( !in_svg_plane(NR::Point(inf, nan)) );
+ TS_ASSERT( !in_svg_plane(NR::Point(0., -inf)) );
+ double const xs[] = {inf, -inf, nan, 1., -2., small, -small};
+ for (unsigned i = 0; i < G_N_ELEMENTS(xs); ++i) {
+ for (unsigned j = 0; j < G_N_ELEMENTS(xs); ++j) {
+ TS_ASSERT_EQUALS( in_svg_plane(NR::Point(xs[i], xs[j])),
+ (fabs(xs[i]) < inf &&
+ fabs(xs[j]) < inf ) );
+ }
+ }
+ }
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/in-svg-plane.h b/src/libnr/in-svg-plane.h
new file mode 100644
index 000000000..901fc748f
--- /dev/null
+++ b/src/libnr/in-svg-plane.h
@@ -0,0 +1,33 @@
+#ifndef SEEN_LIBNR_IN_SVG_PLANE_H
+#define SEEN_LIBNR_IN_SVG_PLANE_H
+
+#include "libnr/nr-point-fns.h"
+
+
+/**
+ * Returns true iff the coordinates of \a p are finite, non-NaN, and "small enough". Currently we
+ * use the magic number 1e18 for determining "small enough", as this number has in the past been
+ * used in sodipodi code as a sort of "infinity" value.
+ *
+ * For SVG Tiny output, we might choose a smaller value corresponding to the range of valid numbers
+ * in SVG Tiny (which uses fixed-point arithmetic).
+ */
+inline bool
+in_svg_plane(NR::Point const p)
+{
+ return NR::LInfty(p) < 1e18;
+}
+
+
+#endif /* !SEEN_LIBNR_IN_SVG_PLANE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/libnr.def b/src/libnr/libnr.def
new file mode 100644
index 000000000..d8f224ca9
--- /dev/null
+++ b/src/libnr/libnr.def
@@ -0,0 +1,89 @@
+EXPORTS
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM
+ nr_R8G8B8A8_N_EMPTY_A8_RGBA32
+ nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N
+ nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_A8
+ nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P
+ nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_A8
+ nr_R8G8B8A8_N_R8G8B8A8_N_A8_RGBA32
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_A8
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_TRANSFORM
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_A8
+ nr_R8G8B8A8_P_EMPTY_A8_RGBA32
+ nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N
+ nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_A8
+ nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P
+ nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_A8
+ nr_R8G8B8A8_P_R8G8B8A8_P_A8_RGBA32
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_A8
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_A8
+; nr_R8G8B8_EMPTY_A8_RGBA32
+ nr_R8G8B8_R8G8B8_A8_RGBA32
+ nr_R8G8B8_R8G8B8_R8G8B8A8_N
+ nr_R8G8B8_R8G8B8_R8G8B8A8_P
+ nr_active_object_add_listener
+ nr_active_object_get_type
+ nr_active_object_remove_listener_by_data
+ nr_blit_pixblock_mask_rgba32
+ nr_blit_pixblock_pixblock_alpha
+ nr_blit_pixblock_pixblock_mask
+ nr_compose_pixblock_pixblock_pixel
+ nr_emit_fail_warning
+ nr_flat_free_list
+ nr_flat_free_one
+ nr_flat_insert_sorted
+ nr_flat_new_full
+ nr_lgradient_renderer_setup
+ nr_matrix_invert
+ nr_matrix_set_rotate
+ nr_matrix_set_scale
+ nr_matrix_set_translate
+ nr_matrix_multiply
+ nr_object_check_instance_cast
+ nr_object_check_instance_type
+ nr_object_delete
+ nr_object_get_type
+ nr_object_new
+ nr_object_ref
+ nr_object_register_type
+ nr_object_release
+ nr_object_setup
+ nr_object_unref
+ nr_path_duplicate_transform
+ nr_path_matrix_bbox_nion
+ nr_path_matrix_point_bbox_wind_distance
+ nr_pixblock_draw_line_rgba32
+ nr_pixblock_free
+ nr_pixblock_new
+ nr_pixblock_release
+ nr_pixblock_render_gray_noise
+ nr_pixblock_render_svp_mask_or
+ nr_pixblock_setup
+ nr_pixblock_setup_extern
+ nr_pixblock_setup_fast
+ nr_pixelstore_16K_free
+ nr_pixelstore_16K_new
+ nr_pixelstore_4K_free
+ nr_pixelstore_4K_new
+ nr_pixelstore_64K_free
+ nr_pixelstore_64K_new
+ nr_rect_d_intersect
+ nr_rect_d_matrix_transform
+ nr_rect_d_union
+ nr_rect_l_intersect
+ nr_rect_l_union
+ nr_rgradient_renderer_setup
+ nr_svp_bbox
+ nr_svp_free
+ nr_svp_point_distance
+ nr_svp_point_wind
+ nr_type_is_a
+ nr_vertex_free_list
+ nr_vertex_free_one
+ nr_vertex_new
+ nr_vertex_new_xy
+ nr_vertex_reverse_list
diff --git a/src/libnr/makefile.in b/src/libnr/makefile.in
new file mode 100644
index 000000000..8c80244ab
--- /dev/null
+++ b/src/libnr/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+ cd .. && $(MAKE) libnr/all
+
+clean %.a %.o:
+ cd .. && $(MAKE) libnr/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/libnr/n-art-bpath.h b/src/libnr/n-art-bpath.h
new file mode 100644
index 000000000..abce499e0
--- /dev/null
+++ b/src/libnr/n-art-bpath.h
@@ -0,0 +1,61 @@
+#ifndef SEEN_LIBNR_N_ART_BPATH_H
+#define SEEN_LIBNR_N_ART_BPATH_H
+
+/** \file
+ * NArtBpath: old-style path segment.
+ */
+
+#include "libnr/nr-point.h"
+#include "libnr/nr-path-code.h"
+
+/**
+ * Old-style path segment.
+ *
+ * Arrays of paths segment start with a MOVETO or MOVETO_OPEN segment
+ * where the former indicates the beginning of a closed subpath.
+ * \see subpath_from_bpath()
+ */
+class NArtBpath {
+public:
+ NRPathcode code; ///< Type of segment
+ double x1, y1; ///< Position of control point in case of NR_CURVETO
+ double x2, y2; ///< Position of control point in case of NR_CURVETO
+ double x3, y3; ///< Position of next point
+
+ /// Convert i-th position data pair to Point object
+ /// \pre 1 <= i <= 3
+ NR::Point c(unsigned const i) const {
+ switch (i) {
+ case 1: return NR::Point(x1, y1);
+ case 2: return NR::Point(x2, y2);
+ case 3: return NR::Point(x3, y3);
+ default: abort();
+ }
+ }
+
+ /// Set i-th position data pair from Point
+ /// \pre 1 <= i <= 3
+ void setC(unsigned const i, NR::Point const &p) {
+ using NR::X; using NR::Y;
+ switch (i) {
+ case 1: x1 = p[X]; y1 = p[Y]; break;
+ case 2: x2 = p[X]; y2 = p[Y]; break;
+ case 3: x3 = p[X]; y3 = p[Y]; break;
+ default: abort();
+ }
+ }
+};
+
+
+#endif /* !SEEN_LIBNR_N_ART_BPATH_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 :
diff --git a/src/libnr/nr-blit.cpp b/src/libnr/nr-blit.cpp
new file mode 100644
index 000000000..2a93bc9bd
--- /dev/null
+++ b/src/libnr/nr-blit.cpp
@@ -0,0 +1,300 @@
+#define __NR_BLIT_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include "nr-pixops.h"
+#include "nr-compose.h"
+#include "nr-blit.h"
+
+void
+nr_blit_pixblock_pixblock_alpha (NRPixBlock *d, NRPixBlock *s, unsigned int alpha)
+{
+ NRRectL clip;
+ unsigned char *dpx, *spx;
+ int dbpp, sbpp;
+ int w, h;
+
+ if (alpha == 0) return;
+ if (s->empty) return;
+ /* fixme: */
+ if (s->mode == NR_PIXBLOCK_MODE_A8) return;
+ /* fixme: */
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8) return;
+
+ /*
+ * Possible variants as of now:
+ *
+ * 0. SRC EP - DST EP *
+ * 1. SRC EP - DST EN *
+ * 2. SRC EP - DST P *
+ * 3. SRC EP - DST N *
+ * 4. SRC EN - DST EP *
+ * 5. SRC EN - DST EN *
+ * 6. SRC EN - DST P *
+ * 7. SRC EN - DST N *
+ * 8. SRC P - DST EP *
+ * 9. SRC P - DST EN *
+ * A. SRC P - DST P *
+ * B. SRC P - DST N *
+ * C. SRC N - DST EP *
+ * D. SRC N - DST EN *
+ * E. SRC N - DST P *
+ * F. SRC N - DST N *
+ *
+ */
+
+ nr_rect_l_intersect (&clip, &d->area, &s->area);
+
+ if (nr_rect_l_test_empty (&clip)) return;
+
+ /* Pointers */
+ dbpp = NR_PIXBLOCK_BPP (d);
+ dpx = NR_PIXBLOCK_PX (d) + (clip.y0 - d->area.y0) * d->rs + dbpp * (clip.x0 - d->area.x0);
+ sbpp = NR_PIXBLOCK_BPP (s);
+ spx = NR_PIXBLOCK_PX (s) + (clip.y0 - s->area.y0) * s->rs + sbpp * (clip.x0 - s->area.x0);
+ w = clip.x1 - clip.x0;
+ h = clip.y1 - clip.y0;
+
+ switch (d->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ /* No rendering into alpha at moment */
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ nr_R8G8B8_R8G8B8_R8G8B8A8_P (dpx, w, h, d->rs, spx, s->rs, alpha);
+ } else {
+ nr_R8G8B8_R8G8B8_R8G8B8A8_N (dpx, w, h, d->rs, spx, s->rs, alpha);
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ if (d->empty) {
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ /* Case 8 */
+ nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P (dpx, w, h, d->rs, spx, s->rs, alpha);
+ } else {
+ /* Case C */
+ nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N (dpx, w, h, d->rs, spx, s->rs, alpha);
+ }
+ } else {
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ /* case A */
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P (dpx, w, h, d->rs, spx, s->rs, alpha);
+ } else {
+ /* case E */
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N (dpx, w, h, d->rs, spx, s->rs, alpha);
+ }
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ if (d->empty) {
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ /* Case 9 */
+ nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P (dpx, w, h, d->rs, spx, s->rs, alpha);
+ } else {
+ /* Case D */
+ nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N (dpx, w, h, d->rs, spx, s->rs, alpha);
+ }
+ } else {
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ /* case B */
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P (dpx, w, h, d->rs, spx, s->rs, alpha);
+ } else {
+ /* case F */
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N (dpx, w, h, d->rs, spx, s->rs, alpha);
+ }
+ }
+ break;
+ }
+}
+
+void
+nr_blit_pixblock_pixblock_mask (NRPixBlock *d, NRPixBlock *s, NRPixBlock *m)
+{
+ NRRectL clip;
+ unsigned char *dpx, *spx, *mpx;
+ int dbpp, sbpp;
+ int w, h;
+
+ if (s->empty) return;
+ /* fixme: */
+ if (s->mode == NR_PIXBLOCK_MODE_A8) return;
+ /* fixme: */
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8) return;
+
+ /*
+ * Possible variants as of now:
+ *
+ * 0. SRC EP - DST EP *
+ * 1. SRC EP - DST EN *
+ * 2. SRC EP - DST P *
+ * 3. SRC EP - DST N *
+ * 4. SRC EN - DST EP *
+ * 5. SRC EN - DST EN *
+ * 6. SRC EN - DST P *
+ * 7. SRC EN - DST N *
+ * 8. SRC P - DST EP *
+ * 9. SRC P - DST EN *
+ * A. SRC P - DST P *
+ * B. SRC P - DST N *
+ * C. SRC N - DST EP *
+ * D. SRC N - DST EN *
+ * E. SRC N - DST P *
+ * F. SRC N - DST N *
+ *
+ */
+
+ nr_rect_l_intersect (&clip, &d->area, &s->area);
+ nr_rect_l_intersect (&clip, &clip, &m->area);
+
+ if (nr_rect_l_test_empty (&clip)) return;
+
+ /* Pointers */
+ dbpp = NR_PIXBLOCK_BPP (d);
+ dpx = NR_PIXBLOCK_PX (d) + (clip.y0 - d->area.y0) * d->rs + dbpp * (clip.x0 - d->area.x0);
+ sbpp = NR_PIXBLOCK_BPP (s);
+ spx = NR_PIXBLOCK_PX (s) + (clip.y0 - s->area.y0) * s->rs + sbpp * (clip.x0 - s->area.x0);
+ mpx = NR_PIXBLOCK_PX (m) + (clip.y0 - m->area.y0) * m->rs + 1 * (clip.x0 - m->area.x0);
+ w = clip.x1 - clip.x0;
+ h = clip.y1 - clip.y0;
+
+ switch (d->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ /* No rendering into alpha at moment */
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ nr_R8G8B8_R8G8B8_R8G8B8A8_P_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ } else {
+ nr_R8G8B8_R8G8B8_R8G8B8A8_N_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ if (d->empty) {
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ /* Case 8 */
+ nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ } else {
+ /* Case C */
+ nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ }
+ } else {
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ /* case A */
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ } else {
+ /* case E */
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ }
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ if (d->empty) {
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ /* Case 9 */
+ nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ } else {
+ /* Case D */
+ nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ }
+ } else {
+ if (s->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ /* case B */
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ } else {
+ /* case F */
+ nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_A8 (dpx, w, h, d->rs, spx, s->rs, mpx, m->rs);
+ }
+ }
+ break;
+ }
+}
+
+void
+nr_blit_pixblock_mask_rgba32 (NRPixBlock *d, NRPixBlock *m, unsigned long rgba)
+{
+ if (!(rgba & 0xff)) return;
+
+ if (m) {
+ NRRectL clip;
+ unsigned char *dpx, *mpx;
+ int w, h;
+
+ if (m->mode != NR_PIXBLOCK_MODE_A8) return;
+
+ if (!nr_rect_l_test_intersect (&d->area, &m->area)) return;
+
+ nr_rect_l_intersect (&clip, &d->area, &m->area);
+
+ /* Pointers */
+ dpx = NR_PIXBLOCK_PX (d) + (clip.y0 - d->area.y0) * d->rs + NR_PIXBLOCK_BPP (d) * (clip.x0 - d->area.x0);
+ mpx = NR_PIXBLOCK_PX (m) + (clip.y0 - m->area.y0) * m->rs + (clip.x0 - m->area.x0);
+ w = clip.x1 - clip.x0;
+ h = clip.y1 - clip.y0;
+
+ if (d->empty) {
+ if (d->mode == NR_PIXBLOCK_MODE_R8G8B8) {
+ nr_R8G8B8_R8G8B8_A8_RGBA32 (dpx, w, h, d->rs, mpx, m->rs, rgba);
+ } else if (d->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ nr_R8G8B8A8_P_EMPTY_A8_RGBA32 (dpx, w, h, d->rs, mpx, m->rs, rgba);
+ } else {
+ nr_R8G8B8A8_N_EMPTY_A8_RGBA32 (dpx, w, h, d->rs, mpx, m->rs, rgba);
+ }
+ d->empty = 0;
+ } else {
+ if (d->mode == NR_PIXBLOCK_MODE_R8G8B8) {
+ nr_R8G8B8_R8G8B8_A8_RGBA32 (dpx, w, h, d->rs, mpx, m->rs, rgba);
+ } else if (d->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ nr_R8G8B8A8_P_R8G8B8A8_P_A8_RGBA32 (dpx, w, h, d->rs, mpx, m->rs, rgba);
+ } else {
+ nr_R8G8B8A8_N_R8G8B8A8_N_A8_RGBA32 (dpx, w, h, d->rs, mpx, m->rs, rgba);
+ }
+ }
+ } else {
+ unsigned int r, g, b, a;
+ int x, y;
+ r = NR_RGBA32_R (rgba);
+ g = NR_RGBA32_G (rgba);
+ b = NR_RGBA32_B (rgba);
+ a = NR_RGBA32_A (rgba);
+ for (y = d->area.y0; y < d->area.y1; y++) {
+ unsigned char *p;
+ p = NR_PIXBLOCK_PX (d) + (y - d->area.y0) * d->rs;
+ for (x = d->area.x0; x < d->area.x1; x++) {
+ unsigned int da;
+ switch (d->mode) {
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ p[0] = NR_COMPOSEN11 (r, a, p[0]);
+ p[1] = NR_COMPOSEN11 (g, a, p[1]);
+ p[2] = NR_COMPOSEN11 (b, a, p[2]);
+ p += 3;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ p[0] = NR_COMPOSENPP (r, a, p[0], p[3]);
+ p[1] = NR_COMPOSENPP (g, a, p[1], p[3]);
+ p[2] = NR_COMPOSENPP (b, a, p[2], p[3]);
+ p[3] = (65025 - (255 - a) * (255 - p[3]) + 127) / 255;
+ p += 4;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ da = 65025 - (255 - a) * (255 - p[3]);
+ p[0] = NR_COMPOSENNN_A7 (r, a, p[0], p[3], da);
+ p[1] = NR_COMPOSENNN_A7 (g, a, p[1], p[3], da);
+ p[2] = NR_COMPOSENNN_A7 (b, a, p[2], p[3], da);
+ p[3] = (da + 127) / 255;
+ p += 4;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/libnr/nr-blit.h b/src/libnr/nr-blit.h
new file mode 100644
index 000000000..3221c8187
--- /dev/null
+++ b/src/libnr/nr-blit.h
@@ -0,0 +1,32 @@
+#ifndef __NR_BLIT_H__
+#define __NR_BLIT_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixblock.h>
+
+#define nr_blit_pixblock_pixblock(d,s) nr_blit_pixblock_pixblock_alpha (d, s, 255)
+
+void nr_blit_pixblock_pixblock_alpha (NRPixBlock *d, NRPixBlock *s, unsigned int alpha);
+void nr_blit_pixblock_pixblock_mask (NRPixBlock *d, NRPixBlock *s, NRPixBlock *m);
+void nr_blit_pixblock_mask_rgba32 (NRPixBlock *d, NRPixBlock *m, unsigned long rgba32);
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-compose-transform.cpp b/src/libnr/nr-compose-transform.cpp
new file mode 100644
index 000000000..bb5022a74
--- /dev/null
+++ b/src/libnr/nr-compose-transform.cpp
@@ -0,0 +1,360 @@
+#define __NR_COMPOSE_TRANSFORM_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "nr-pixops.h"
+#include "nr-matrix.h"
+
+
+#ifdef WITH_MMX
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+/* fixme: */
+int nr_have_mmx (void);
+void nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const long *FFd2s, unsigned int alpha);
+void nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const long *FFd2s, const long *FF_S, unsigned int alpha, int dbits);
+#define NR_PIXOPS_MMX (1 && nr_have_mmx ())
+#ifdef __cplusplus
+}
+#endif //__cplusplus
+#endif
+
+/* fixme: Implement missing (Lauris) */
+/* fixme: PREMUL colors before calculating average (Lauris) */
+
+/* Fixed point precision */
+#define FBITS 12
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+
+void
+nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd)
+{
+ int xsize, ysize, size, dbits;
+ long FFs_x_x, FFs_x_y, FFs_y_x, FFs_y_y, FFs__x, FFs__y;
+ long FFs_x_x_S, FFs_x_y_S, FFs_y_x_S, FFs_y_y_S;
+ /* Subpixel positions */
+ int FF_sx_S[256];
+ int FF_sy_S[256];
+ unsigned char *d0;
+ int FFsx0, FFsy0;
+ int x, y;
+
+ if (alpha == 0) return;
+
+ xsize = (1 << xd);
+ ysize = (1 << yd);
+ size = xsize * ysize;
+ dbits = xd + yd;
+
+ /* Set up fixed point matrix */
+ FFs_x_x = (long) (d2s[0] * (1 << FBITS) + 0.5);
+ FFs_x_y = (long) (d2s[1] * (1 << FBITS) + 0.5);
+ FFs_y_x = (long) (d2s[2] * (1 << FBITS) + 0.5);
+ FFs_y_y = (long) (d2s[3] * (1 << FBITS) + 0.5);
+ FFs__x = (long) (d2s[4] * (1 << FBITS) + 0.5);
+ FFs__y = (long) (d2s[5] * (1 << FBITS) + 0.5);
+
+ FFs_x_x_S = FFs_x_x >> xd;
+ FFs_x_y_S = FFs_x_y >> xd;
+ FFs_y_x_S = FFs_y_x >> yd;
+ FFs_y_y_S = FFs_y_y >> yd;
+
+ /* Set up subpixel matrix */
+ /* fixme: We can calculate that in floating point (Lauris) */
+ for (y = 0; y < ysize; y++) {
+ for (x = 0; x < xsize; x++) {
+ FF_sx_S[y * xsize + x] = FFs_x_x_S * x + FFs_y_x_S * y;
+ FF_sy_S[y * xsize + x] = FFs_x_y_S * x + FFs_y_y_S * y;
+ }
+ }
+
+ d0 = px;
+ FFsx0 = FFs__x;
+ FFsy0 = FFs__y;
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d;
+ long FFsx, FFsy;
+ d = d0;
+ FFsx = FFsx0;
+ FFsy = FFsy0;
+ for (x = 0; x < w; x++) {
+ unsigned int r, g, b, a;
+ long sx, sy;
+ int i;
+ r = g = b = a = 0;
+ for (i = 0; i < size; i++) {
+ sx = (FFsx + FF_sx_S[i]) >> FBITS;
+ if ((sx >= 0) && (sx < sw)) {
+ sy = (FFsy + FF_sy_S[i]) >> FBITS;
+ if ((sy >= 0) && (sy < sh)) {
+ const unsigned char *s;
+ unsigned int ca;
+ s = spx + sy * srs + sx * 4;
+ ca = NR_PREMUL (s[3], alpha);
+ r += NR_PREMUL (s[0], ca);
+ g += NR_PREMUL (s[1], ca);
+ b += NR_PREMUL (s[2], ca);
+ a += ca;
+ }
+ }
+ }
+ a >>= dbits;
+ if (a != 0) {
+ r = r >> dbits;
+ g = g >> dbits;
+ b = b >> dbits;
+ if (a == 255) {
+ /* Transparent BG, premul src */
+ d[0] = r;
+ d[1] = g;
+ d[2] = b;
+ d[3] = a;
+ } else {
+ unsigned int ca;
+ /* Full composition */
+ ca = 65025 - (255 - a) * (255 - d[3]);
+ d[0] = NR_COMPOSENNN_A7 (r, a, d[0], d[3], ca);
+ d[1] = NR_COMPOSENNN_A7 (g, a, d[1], d[3], ca);
+ d[2] = NR_COMPOSENNN_A7 (b, a, d[2], d[3], ca);
+ d[3] = (ca + 127) / 255;
+ }
+ }
+ /* Advance pointers */
+ FFsx += FFs_x_x;
+ FFsy += FFs_x_y;
+ d += 4;
+ }
+ FFsx0 += FFs_y_x;
+ FFsy0 += FFs_y_y;
+ d0 += rs;
+ }
+}
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+
+static void
+nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const long *FFd2s, unsigned int alpha)
+{
+ unsigned char *d0;
+ int FFsx0, FFsy0;
+ int x, y;
+
+ d0 = px;
+ FFsx0 = FFd2s[4];
+ FFsy0 = FFd2s[5];
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d;
+ long FFsx, FFsy;
+ d = d0;
+ FFsx = FFsx0;
+ FFsy = FFsy0;
+ for (x = 0; x < w; x++) {
+ long sx, sy;
+ sx = FFsx >> FBITS;
+ if ((sx >= 0) && (sx < sw)) {
+ sy = FFsy >> FBITS;
+ if ((sy >= 0) && (sy < sh)) {
+ const unsigned char *s;
+ unsigned int a;
+ s = spx + sy * srs + sx * 4;
+ a = NR_PREMUL (s[3], alpha);
+ if (a != 0) {
+ if ((a == 255) || (d[3] == 0)) {
+ /* Transparent BG, premul src */
+ d[0] = NR_PREMUL (s[0], a);
+ d[1] = NR_PREMUL (s[1], a);
+ d[2] = NR_PREMUL (s[2], a);
+ d[3] = a;
+ } else {
+ d[0] = NR_COMPOSENPP (s[0], a, d[0], d[3]);
+ d[1] = NR_COMPOSENPP (s[1], a, d[1], d[3]);
+ d[2] = NR_COMPOSENPP (s[2], a, d[2], d[3]);
+ d[3] = (65025 - (255 - a) * (255 - d[3]) + 127) / 255;
+ }
+ }
+ }
+ }
+ /* Advance pointers */
+ FFsx += FFd2s[0];
+ FFsy += FFd2s[1];
+ d += 4;
+ }
+ FFsx0 += FFd2s[2];
+ FFsy0 += FFd2s[3];
+ d0 += rs;
+ }
+}
+
+static void
+nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const long *FFd2s, const long *FF_S, unsigned int alpha, int dbits)
+{
+ int size;
+ unsigned char *d0;
+ int FFsx0, FFsy0;
+ int x, y;
+
+ size = (1 << dbits);
+ unsigned alpha_rounding_fix = size * 255;
+ unsigned rgb_rounding_fix = size * (255 * 256);
+ if (alpha > 127) ++alpha;
+
+ d0 = px;
+ FFsx0 = FFd2s[4];
+ FFsy0 = FFd2s[5];
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d;
+ long FFsx, FFsy;
+ d = d0;
+ FFsx = FFsx0;
+ FFsy = FFsy0;
+ for (x = 0; x < w; x++) {
+ unsigned int r, g, b, a;
+ int i;
+ r = g = b = a = 0;
+ for (i = 0; i < size; i++) {
+ long sx, sy;
+ sx = (FFsx + FF_S[2 * i]) >> FBITS;
+ if ((sx >= 0) && (sx < sw)) {
+ sy = (FFsy + FF_S[2 * i + 1]) >> FBITS;
+ if ((sy >= 0) && (sy < sh)) {
+ const unsigned char *s;
+ unsigned int ca;
+ s = spx + sy * srs + sx * 4;
+ ca = s[3] * alpha;
+ r += s[0] * ca;
+ g += s[1] * ca;
+ b += s[2] * ca;
+ a += ca;
+ }
+ }
+ }
+ a = (a + alpha_rounding_fix) >> (8 + dbits);
+ if (a != 0) {
+ r = (r + rgb_rounding_fix) >> (16 + dbits);
+ g = (g + rgb_rounding_fix) >> (16 + dbits);
+ b = (b + rgb_rounding_fix) >> (16 + dbits);
+ if ((a == 255) || (d[3] == 0)) {
+ /* Transparent BG, premul src */
+ d[0] = r;
+ d[1] = g;
+ d[2] = b;
+ d[3] = a;
+ } else {
+ d[0] = NR_COMPOSEPPP (r, a, d[0], d[3]);
+ d[1] = NR_COMPOSEPPP (g, a, d[1], d[3]);
+ d[2] = NR_COMPOSEPPP (b, a, d[2], d[3]);
+ d[3] = (65025 - (255 - a) * (255 - d[3]) + 127) / 255;
+ }
+ }
+ /* Advance pointers */
+ FFsx += FFd2s[0];
+ FFsy += FFd2s[1];
+ d += 4;
+ }
+ FFsx0 += FFd2s[2];
+ FFsy0 += FFd2s[3];
+ d0 += rs;
+ }
+}
+
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd)
+{
+ int dbits;
+ long FFd2s[6];
+ int i;
+
+ if (alpha == 0) return;
+
+ dbits = xd + yd;
+
+ for (i = 0; i < 6; i++) {
+ FFd2s[i] = (long) (d2s[i] * (1 << FBITS) + 0.5);
+ }
+
+ if (dbits == 0) {
+#ifdef WITH_MMX
+ if (NR_PIXOPS_MMX) {
+ /* WARNING: MMX composer REQUIRES w > 0 and h > 0 */
+ nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (px, w, h, rs, spx, sw, sh, srs, FFd2s, alpha);
+ return;
+ }
+#endif
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0 (px, w, h, rs, spx, sw, sh, srs, FFd2s, alpha);
+ } else {
+ int xsize, ysize;
+ long FFs_x_x_S, FFs_x_y_S, FFs_y_x_S, FFs_y_y_S;
+ long FF_S[2 * 256];
+ int x, y;
+
+ xsize = (1 << xd);
+ ysize = (1 << yd);
+
+ FFs_x_x_S = FFd2s[0] >> xd;
+ FFs_x_y_S = FFd2s[1] >> xd;
+ FFs_y_x_S = FFd2s[2] >> yd;
+ FFs_y_y_S = FFd2s[3] >> yd;
+
+ /* Set up subpixel matrix */
+ /* fixme: We can calculate that in floating point (Lauris) */
+ for (y = 0; y < ysize; y++) {
+ for (x = 0; x < xsize; x++) {
+ FF_S[2 * (y * xsize + x)] = FFs_x_x_S * x + FFs_y_x_S * y;
+ FF_S[2 * (y * xsize + x) + 1] = FFs_x_y_S * x + FFs_y_y_S * y;
+ }
+ }
+
+#ifdef WITH_MMX
+ if (NR_PIXOPS_MMX) {
+ /* WARNING: MMX composer REQUIRES w > 0 and h > 0 */
+ nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (px, w, h, rs, spx, sw, sh, srs, FFd2s, FF_S, alpha, dbits);
+ return;
+ }
+#endif
+ nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n (px, w, h, rs, spx, sw, sh, srs, FFd2s, FF_S, alpha, dbits);
+ }
+}
+
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
diff --git a/src/libnr/nr-compose-transform.h b/src/libnr/nr-compose-transform.h
new file mode 100644
index 000000000..7ffb20074
--- /dev/null
+++ b/src/libnr/nr-compose-transform.h
@@ -0,0 +1,43 @@
+#ifndef __NR_COMPOSE_TRANSFORM_H__
+#define __NR_COMPOSE_TRANSFORM_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-forward.h>
+
+/* FINAL DST SRC */
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_TRANSFORM (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int sw, int sh, int srs,
+ const NR::Matrix &d2s, unsigned int alpha, int xd, int yd);
+
+#endif
diff --git a/src/libnr/nr-compose.cpp b/src/libnr/nr-compose.cpp
new file mode 100644
index 000000000..8eed6e84c
--- /dev/null
+++ b/src/libnr/nr-compose.cpp
@@ -0,0 +1,986 @@
+#define __NR_COMPOSE_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include "nr-pixops.h"
+
+#ifdef WITH_MMX
+/* fixme: */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+int nr_have_mmx (void);
+void nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned char *c);
+void nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned char *c);
+void nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+#define NR_PIXOPS_MMX nr_have_mmx ()
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
+
+void
+nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ if (alpha == 0) {
+ memset (px, 0x0, 4 * w);
+ } else if (alpha == 255) {
+ memcpy (px, spx, 4 * w);
+ } else {
+ const unsigned char *s;
+ unsigned char *d;
+ d = px;
+ s = spx;
+ for (c = 0; c < w; c++) {
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = *s++;
+ *d++ = NR_PREMUL (*s, alpha);
+ s++;
+ }
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ if (alpha == 0) {
+ memset (px, 0x0, 4 * w);
+ } else {
+ const unsigned char *s;
+ unsigned char *d;
+ s = spx;
+ d = px;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], alpha);
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = a;
+ d += 4;
+ s += 4;
+ }
+ px += rs;
+ spx += srs;
+ }
+ }
+}
+
+void
+nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = (s[3] * alpha + 127) / 255;
+ d[0] = (s[0] * a + 127) / 255;
+ d[1] = (s[1] * a + 127) / 255;
+ d[2] = (s[2] * a + 127) / 255;
+ d[3] = a;
+ d += 4;
+ s += 4;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (c = 0; c < w; c++) {
+ if (alpha == 255) {
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = s[3];
+ } else {
+ d[0] = NR_PREMUL (s[0], alpha);
+ d[1] = NR_PREMUL (s[1], alpha);
+ d[2] = NR_PREMUL (s[2], alpha);
+ d[3] = NR_PREMUL (s[3], alpha);
+ }
+ d += 4;
+ s += 4;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], alpha);
+ if (a == 0) {
+ /* Transparent FG, NOP */
+ } else if ((a == 255) || (d[3] == 0)) {
+ /* Full coverage, COPY */
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = a;
+ } else {
+ unsigned int ca;
+ /* Full composition */
+ ca = 65025 - (255 - a) * (255 - d[3]);
+ d[0] = NR_COMPOSENNN_A7 (s[0], a, d[0], d[3], ca);
+ d[1] = NR_COMPOSENNN_A7 (s[1], a, d[1], d[3], ca);
+ d[2] = NR_COMPOSENNN_A7 (s[2], a, d[2], d[3], ca);
+ d[3] = (ca + 127) / 255;
+ }
+ d += 4;
+ s += 4;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], alpha);
+ if (a == 0) {
+ /* Transparent FG, NOP */
+ } else if ((a == 255) || (d[3] == 0)) {
+ /* Full coverage, demul src */
+ d[0] = (s[0] * 255 + (s[3] >> 1)) / s[3];
+ d[1] = (s[1] * 255 + (s[3] >> 1)) / s[3];
+ d[2] = (s[2] * 255 + (s[3] >> 1)) / s[3];
+ d[3] = a;
+ } else {
+ if (alpha == 255) {
+ unsigned int ca;
+ /* Full composition */
+ ca = 65025 - (255 - s[3]) * (255 - d[3]);
+ d[0] = NR_COMPOSEPNN_A7 (s[0], s[3], d[0], d[3], ca);
+ d[1] = NR_COMPOSEPNN_A7 (s[1], s[3], d[1], d[3], ca);
+ d[2] = NR_COMPOSEPNN_A7 (s[2], s[3], d[2], d[3], ca);
+ d[3] = (65025 - (255 - s[3]) * (255 - d[3]) + 127) / 255;
+ } else {
+ // calculate premultiplied from two premultiplieds:
+ d[0] = NR_COMPOSEPPP(NR_PREMUL (s[0], alpha), a, NR_PREMUL (d[0], d[3]), 0); // last parameter not used
+ d[1] = NR_COMPOSEPPP(NR_PREMUL (s[1], alpha), a, NR_PREMUL (d[1], d[3]), 0);
+ d[2] = NR_COMPOSEPPP(NR_PREMUL (s[2], alpha), a, NR_PREMUL (d[2], d[3]), 0);
+ // total opacity:
+ d[3] = (65025 - (255 - a) * (255 - d[3]) + 127) / 255;
+ // un-premultiply channels:
+ d[0] = d[0]*255/d[3];
+ d[1] = d[1]*255/d[3];
+ d[2] = d[2]*255/d[3];
+ }
+ }
+ d += 4;
+ s += 4;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], alpha);
+ if (a == 0) {
+ /* Transparent FG, NOP */
+ } else if ((a == 255) || (d[3] == 0)) {
+ /* Transparent BG, premul src */
+ d[0] = NR_PREMUL (s[0], a);
+ d[1] = NR_PREMUL (s[1], a);
+ d[2] = NR_PREMUL (s[2], a);
+ d[3] = a;
+ } else {
+ d[0] = NR_COMPOSENPP (s[0], a, d[0], d[3]);
+ d[1] = NR_COMPOSENPP (s[1], a, d[1], d[3]);
+ d[2] = NR_COMPOSENPP (s[2], a, d[2], d[3]);
+ d[3] = (65025 - (255 - a) * (255 - d[3]) + 127) / 255;
+ }
+ d += 4;
+ s += 4;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], alpha);
+ if (a == 0) {
+ /* Transparent FG, NOP */
+ } else if ((a == 255) || (d[3] == 0)) {
+ /* Transparent BG, COPY */
+ d[0] = NR_PREMUL (s[0], alpha);
+ d[1] = NR_PREMUL (s[1], alpha);
+ d[2] = NR_PREMUL (s[2], alpha);
+ d[3] = NR_PREMUL (s[3], alpha);
+ } else {
+ if (alpha == 255) {
+ /* Simple */
+ d[0] = NR_COMPOSEPPP (s[0], s[3], d[0], d[3]);
+ d[1] = NR_COMPOSEPPP (s[1], s[3], d[1], d[3]);
+ d[2] = NR_COMPOSEPPP (s[2], s[3], d[2], d[3]);
+ d[3] = (65025 - (255 - s[3]) * (255 - d[3]) + 127) / 255;
+ } else {
+ unsigned int c;
+ c = NR_PREMUL (s[0], alpha);
+ d[0] = NR_COMPOSEPPP (c, a, d[0], d[3]);
+ c = NR_PREMUL (s[1], alpha);
+ d[1] = NR_COMPOSEPPP (c, a, d[1], d[3]);
+ c = NR_PREMUL (s[2], alpha);
+ d[2] = NR_COMPOSEPPP (c, a, d[2], d[3]);
+ d[3] = (65025 - (255 - a) * (255 - d[3]) + 127) / 255;
+ }
+ }
+ d += 4;
+ s += 4;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+/* Masked operations */
+
+void
+nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int x, y;
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (x = 0; x < w; x++) {
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = (s[3] * m[0] + 127) / 255;
+ d += 4;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int x, y;
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (x = 0; x < w; x++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], m[0]);
+ if (a == 0) {
+ d[3] = 0;
+ } else {
+ d[0] = (s[0] * 255 + (a >> 1)) / a;
+ d[1] = (s[1] * 255 + (a >> 1)) / a;
+ d[2] = (s[2] * 255 + (a >> 1)) / a;
+ d[3] = a;
+ }
+ d += 4;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], m[0]);
+ d[0] = NR_PREMUL (s[0], a);
+ d[1] = NR_PREMUL (s[1], a);
+ d[2] = NR_PREMUL (s[2], a);
+ d[3] = a;
+ d += 4;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (c = 0; c < w; c++) {
+ if (m[0] == 255) {
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = s[3];
+ } else {
+ d[0] = NR_PREMUL (s[0], m[0]);
+ d[1] = NR_PREMUL (s[1], m[0]);
+ d[2] = NR_PREMUL (s[2], m[0]);
+ d[3] = NR_PREMUL (s[3], m[0]);
+ }
+ d += 4;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], m[0]);
+ if (a == 0) {
+ /* Transparent FG, NOP */
+ } else if ((a == 255) || (d[3] == 0)) {
+ /* Full coverage, COPY */
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = a;
+ } else {
+ unsigned int ca;
+ /* Full composition */
+ ca = 65025 - (255 - a) * (255 - d[3]);
+ d[0] = NR_COMPOSENNN_A7 (s[0], a, d[0], d[3], ca);
+ d[1] = NR_COMPOSENNN_A7 (s[1], a, d[1], d[3], ca);
+ d[2] = NR_COMPOSENNN_A7 (s[2], a, d[2], d[3], ca);
+ d[3] = (ca + 127) / 255;
+ }
+ d += 4;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], m[0]);
+ if (a == 0) {
+ /* Transparent FG, NOP */
+ } else if ((a == 255) || (d[3] == 0)) {
+ /* Full coverage, demul src */
+ d[0] = (s[0] * 255 + (s[3] >> 1)) / s[3];
+ d[1] = (s[1] * 255 + (s[3] >> 1)) / s[3];
+ d[2] = (s[2] * 255 + (s[3] >> 1)) / s[3];
+ d[3] = a;
+ } else {
+ if (m[0] == 255) {
+ unsigned int ca;
+ /* Full composition */
+ ca = 65025 - (255 - s[3]) * (255 - d[3]);
+ d[0] = NR_COMPOSEPNN_A7 (s[0], s[3], d[0], d[3], ca);
+ d[1] = NR_COMPOSEPNN_A7 (s[1], s[3], d[1], d[3], ca);
+ d[2] = NR_COMPOSEPNN_A7 (s[2], s[3], d[2], d[3], ca);
+ d[3] = (65025 - (255 - s[3]) * (255 - d[3]) + 127) / 255;
+ } else {
+ // calculate premultiplied from two premultiplieds:
+ d[0] = NR_COMPOSEPPP(NR_PREMUL (s[0], m[0]), a, NR_PREMUL (d[0], d[3]), 0); // last parameter not used
+ d[1] = NR_COMPOSEPPP(NR_PREMUL (s[1], m[0]), a, NR_PREMUL (d[1], d[3]), 0);
+ d[2] = NR_COMPOSEPPP(NR_PREMUL (s[2], m[0]), a, NR_PREMUL (d[2], d[3]), 0);
+ // total opacity:
+ d[3] = (65025 - (255 - a) * (255 - d[3]) + 127) / 255;
+ // un-premultiply channels:
+ d[0] = d[0]*255/d[3];
+ d[1] = d[1]*255/d[3];
+ d[2] = d[2]*255/d[3];
+ }
+ }
+ d += 4;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], m[0]);
+ if (a == 0) {
+ /* Transparent FG, NOP */
+ } else if ((a == 255) || (d[3] == 0)) {
+ /* Transparent BG, premul src */
+ d[0] = NR_PREMUL (s[0], a);
+ d[1] = NR_PREMUL (s[1], a);
+ d[2] = NR_PREMUL (s[2], a);
+ d[3] = a;
+ } else {
+ d[0] = NR_COMPOSENPP (s[0], a, d[0], d[3]);
+ d[1] = NR_COMPOSENPP (s[1], a, d[1], d[3]);
+ d[2] = NR_COMPOSENPP (s[2], a, d[2], d[3]);
+ d[3] = (65025 - (255 - a) * (255 - d[3]) + 127) / 255;
+ }
+ d += 4;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], m[0]);
+ if (a == 0) {
+ /* Transparent FG, NOP */
+ } else if ((a == 255) || (d[3] == 0)) {
+ /* Transparent BG, COPY */
+ d[0] = NR_PREMUL (s[0], m[0]);
+ d[1] = NR_PREMUL (s[1], m[0]);
+ d[2] = NR_PREMUL (s[2], m[0]);
+ d[3] = NR_PREMUL (s[3], m[0]);
+ } else {
+ if (m[0] == 255) {
+ /* Simple */
+ d[0] = NR_COMPOSEPPP (s[0], s[3], d[0], d[3]);
+ d[1] = NR_COMPOSEPPP (s[1], s[3], d[1], d[3]);
+ d[2] = NR_COMPOSEPPP (s[2], s[3], d[2], d[3]);
+ d[3] = NR_A7_NORMALIZED(s[3], d[3]);
+ } else {
+ unsigned int c;
+ c = NR_PREMUL (s[0], m[0]);
+ d[0] = NR_COMPOSEPPP (c, a, d[0], d[3]);
+ c = NR_PREMUL (s[1], m[0]);
+ d[1] = NR_COMPOSEPPP (c, a, d[1], d[3]);
+ c = NR_PREMUL (s[2], m[0]);
+ d[2] = NR_COMPOSEPPP (c, a, d[2], d[3]);
+ d[3] = NR_A7_NORMALIZED(a, d[3]);
+ }
+ }
+ d += 4;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_N_EMPTY_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba)
+{
+ unsigned int r, g, b, a;
+ int x, y;
+
+ r = NR_RGBA32_R (rgba);
+ g = NR_RGBA32_G (rgba);
+ b = NR_RGBA32_B (rgba);
+ a = NR_RGBA32_A (rgba);
+
+ if (a == 0) return;
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (x = 0; x < w; x++) {
+ d[0] = r;
+ d[1] = g;
+ d[2] = b;
+ d[3] = NR_PREMUL (s[0], a);
+ d += 4;
+ s += 1;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_EMPTY_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba)
+{
+ unsigned int r, g, b, a;
+ int x, y;
+
+ r = NR_RGBA32_R (rgba);
+ g = NR_RGBA32_G (rgba);
+ b = NR_RGBA32_B (rgba);
+ a = NR_RGBA32_A (rgba);
+
+ if (a == 0) return;
+
+#ifdef WITH_MMX
+ if (NR_PIXOPS_MMX) {
+ unsigned char c[4];
+ c[0] = NR_PREMUL (r, a);
+ c[1] = NR_PREMUL (g, a);
+ c[2] = NR_PREMUL (b, a);
+ c[3] = a;
+ /* WARNING: MMX composer REQUIRES w > 0 and h > 0 */
+ nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP (px, w, h, rs, spx, srs, c);
+ return;
+ }
+#endif
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (x = 0; x < w; x++) {
+ unsigned int ca;
+ ca = s[0] * a;
+ d[0] = (r * ca + 32512) / 65025;
+ d[1] = (g * ca + 32512) / 65025;
+ d[2] = (b * ca + 32512) / 65025;
+ d[3] = (ca + 127) / 255;
+ d += 4;
+ s += 1;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8_R8G8B8_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *mpx, int mrs, unsigned long rgba)
+{
+ unsigned int r, g, b, a;
+ int x, y;
+
+ r = NR_RGBA32_R (rgba);
+ g = NR_RGBA32_G (rgba);
+ b = NR_RGBA32_B (rgba);
+ a = NR_RGBA32_A (rgba);
+
+ if (a == 0) return;
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *m;
+ d = (unsigned char *) px;
+ m = (unsigned char *) mpx;
+ for (x = 0; x < w; x++) {
+ unsigned int alpha;
+ alpha = NR_PREMUL (a, m[0]);
+ d[0] = NR_COMPOSEN11 (r, alpha, d[0]);
+ d[1] = NR_COMPOSEN11 (g, alpha, d[1]);
+ d[2] = NR_COMPOSEN11 (b, alpha, d[2]);
+ d += 3;
+ m += 1;
+ }
+ px += rs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8A8_N_R8G8B8A8_N_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba)
+{
+ unsigned int r, g, b, a;
+ int x, y;
+
+ r = NR_RGBA32_R (rgba);
+ g = NR_RGBA32_G (rgba);
+ b = NR_RGBA32_B (rgba);
+ a = NR_RGBA32_A (rgba);
+
+ if (a == 0) return;
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (x = 0; x < w; x++) {
+ unsigned int ca;
+ ca = NR_PREMUL (s[0], a);
+ if (ca == 0) {
+ /* Transparent FG, NOP */
+ } else if ((ca == 255) || (d[3] == 0)) {
+ /* Full coverage, COPY */
+ d[0] = r;
+ d[1] = g;
+ d[2] = b;
+ d[3] = ca;
+ } else {
+ unsigned int da;
+ /* Full composition */
+ da = 65025 - (255 - ca) * (255 - d[3]);
+ d[0] = NR_COMPOSENNN_A7 (r, ca, d[0], d[3], da);
+ d[1] = NR_COMPOSENNN_A7 (g, ca, d[1], d[3], da);
+ d[2] = NR_COMPOSENNN_A7 (b, ca, d[2], d[3], da);
+ d[3] = (da + 127) / 255;
+ }
+ d += 4;
+ s += 1;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8A8_P_R8G8B8A8_P_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba)
+{
+ unsigned int r, g, b, a;
+ int x, y;
+
+ if (!(rgba & 0xff)) return;
+
+ r = NR_RGBA32_R (rgba);
+ g = NR_RGBA32_G (rgba);
+ b = NR_RGBA32_B (rgba);
+ a = NR_RGBA32_A (rgba);
+
+#ifdef WITH_MMX
+ if (NR_PIXOPS_MMX) {
+ unsigned char c[4];
+ c[0] = NR_PREMUL (r, a);
+ c[1] = NR_PREMUL (g, a);
+ c[2] = NR_PREMUL (b, a);
+ c[3] = a;
+ /* WARNING: MMX composer REQUIRES w > 0 and h > 0 */
+ nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP (px, w, h, rs, spx, srs, c);
+ return;
+ }
+#endif
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *s;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ for (x = 0; x < w; x++) {
+ unsigned int ca;
+ ca = NR_PREMUL (s[0], a);
+ if (ca == 0) {
+ /* Transparent FG, NOP */
+ } else if ((ca == 255) || (d[3] == 0)) {
+ /* Full coverage, COPY */
+ d[0] = NR_PREMUL (r, ca);
+ d[1] = NR_PREMUL (g, ca);
+ d[2] = NR_PREMUL (b, ca);
+ d[3] = ca;
+ } else {
+ /* Full composition */
+ d[0] = NR_COMPOSENPP (r, ca, d[0], d[3]);
+ d[1] = NR_COMPOSENPP (g, ca, d[1], d[3]);
+ d[2] = NR_COMPOSENPP (b, ca, d[2], d[3]);
+ d[3] = (65025 - (255 - ca) * (255 - d[3]) + 127) / 255;
+ }
+ d += 4;
+ s += 1;
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+/* RGB */
+
+void
+nr_R8G8B8_R8G8B8_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ if (alpha == 0) return;
+
+#ifdef WITH_MMX
+ if (NR_PIXOPS_MMX) {
+ /* WARNING: MMX composer REQUIRES w > 0 and h > 0 */
+ nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P (px, w, h, rs, spx, srs, alpha);
+ return;
+ }
+#endif
+
+ for (r = 0; r < h; r++) {
+ const unsigned char *s;
+ unsigned char *d;
+ if (alpha == 255) {
+ d = px;
+ s = spx;
+ for (c = 0; c < w; c++) {
+ if (s[3] == 0) {
+ /* NOP */
+ } else if (s[3] == 255) {
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ } else {
+ d[0] = NR_COMPOSEP11 (s[0], s[3], d[0]);
+ d[1] = NR_COMPOSEP11 (s[1], s[3], d[1]);
+ d[2] = NR_COMPOSEP11 (s[2], s[3], d[2]);
+ }
+ d += 3;
+ s += 4;
+ }
+ } else {
+ d = px;
+ s = spx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], alpha);
+ if (a == 0) {
+ /* NOP */
+ } else {
+ d[0] = NR_COMPOSEP11 (s[0], a, d[0]);
+ d[1] = NR_COMPOSEP11 (s[1], a, d[1]);
+ d[2] = NR_COMPOSEP11 (s[2], a, d[2]);
+ }
+ /* a == 255 is impossible, because alpha < 255 */
+ d += 3;
+ s += 4;
+ }
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8_R8G8B8_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha)
+{
+ int r, c;
+
+ for (r = 0; r < h; r++) {
+ const unsigned char *s;
+ unsigned char *d;
+ if (alpha == 0) {
+ /* NOP */
+ } else if (alpha == 255) {
+ d = px;
+ s = spx;
+ for (c = 0; c < w; c++) {
+ if (s[3] == 0) {
+ /* NOP */
+ } else if (s[3] == 255) {
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ } else {
+ d[0] = NR_COMPOSEN11 (s[0], s[3], d[0]);
+ d[1] = NR_COMPOSEN11 (s[1], s[3], d[1]);
+ d[2] = NR_COMPOSEN11 (s[2], s[3], d[2]);
+ }
+ d += 3;
+ s += 4;
+ }
+ } else {
+ d = px;
+ s = spx;
+ for (c = 0; c < w; c++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], alpha);
+ if (a == 0) {
+ /* NOP */
+ } else {
+ d[0] = NR_COMPOSEN11 (s[0], a, d[0]);
+ d[1] = NR_COMPOSEN11 (s[1], a, d[1]);
+ d[2] = NR_COMPOSEN11 (s[2], a, d[2]);
+ }
+ /* a == 255 is impossible, because alpha < 255 */
+ d += 3;
+ s += 4;
+ }
+ }
+ px += rs;
+ spx += srs;
+ }
+}
+
+void
+nr_R8G8B8_R8G8B8_R8G8B8A8_P_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int x, y;
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (x = 0; x < w; x++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], m[0]);
+ if (a != 0) {
+ unsigned int r, g, b;
+ r = NR_PREMUL (s[0], m[0]);
+ d[0] = NR_COMPOSEP11 (r, a, d[0]);
+ g = NR_PREMUL (s[1], m[0]);
+ d[1] = NR_COMPOSEP11 (g, a, d[1]);
+ b = NR_PREMUL (s[2], m[0]);
+ d[2] = NR_COMPOSEP11 (b, a, d[2]);
+ }
+ d += 3;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+void
+nr_R8G8B8_R8G8B8_R8G8B8A8_N_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs)
+{
+ int x, y;
+
+ for (y = 0; y < h; y++) {
+ unsigned char *d, *s, *m;
+ d = (unsigned char *) px;
+ s = (unsigned char *) spx;
+ m = (unsigned char *) mpx;
+ for (x = 0; x < w; x++) {
+ unsigned int a;
+ a = NR_PREMUL (s[3], m[0]);
+ if (a != 0) {
+ d[0] = NR_COMPOSEP11 (s[0], a, d[0]);
+ d[1] = NR_COMPOSEP11 (s[1], a, d[1]);
+ d[2] = NR_COMPOSEP11 (s[2], a, d[2]);
+ }
+ d += 3;
+ s += 4;
+ m += 1;
+ }
+ px += rs;
+ spx += srs;
+ mpx += mrs;
+ }
+}
+
+
diff --git a/src/libnr/nr-compose.h b/src/libnr/nr-compose.h
new file mode 100644
index 000000000..ccdb52cb0
--- /dev/null
+++ b/src/libnr/nr-compose.h
@@ -0,0 +1,69 @@
+#ifndef __NR_COMPOSE_H__
+#define __NR_COMPOSE_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+/* FINAL DST SRC */
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+
+/* FINAL DST SRC MASK */
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_A8 (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int srs,
+ const unsigned char *mpx, int mrs);
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_A8 (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int srs,
+ const unsigned char *mpx, int mrs);
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_A8 (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int srs,
+ const unsigned char *mpx, int mrs);
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_A8 (unsigned char *px, int w, int h, int rs,
+ const unsigned char *spx, int srs,
+ const unsigned char *mpx, int mrs);
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_A8 (unsigned char *p, int w, int h, int rs,
+ const unsigned char *s, int srs,
+ const unsigned char *m, int mrs);
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_A8 (unsigned char *p, int w, int h, int rs,
+ const unsigned char *s, int srs,
+ const unsigned char *m, int mrs);
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_A8 (unsigned char *p, int w, int h, int rs,
+ const unsigned char *s, int srs,
+ const unsigned char *m, int mrs);
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_A8 (unsigned char *p, int w, int h, int rs,
+ const unsigned char *s, int srs,
+ const unsigned char *m, int mrs);
+
+/* FINAL DST MASK COLOR */
+
+void nr_R8G8B8A8_N_EMPTY_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *mpx, int mrs, unsigned long rgba);
+void nr_R8G8B8A8_P_EMPTY_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *mpx, int mrs, unsigned long rgba);
+
+void nr_R8G8B8_R8G8B8_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba);
+void nr_R8G8B8A8_N_R8G8B8A8_N_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba);
+void nr_R8G8B8A8_P_R8G8B8A8_P_A8_RGBA32 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba);
+
+/* RGB */
+
+void nr_R8G8B8_R8G8B8_R8G8B8A8_P (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8_R8G8B8_R8G8B8A8_N (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8_R8G8B8_R8G8B8A8_P_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs);
+void nr_R8G8B8_R8G8B8_R8G8B8A8_N_A8 (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs);
+
+#endif
diff --git a/src/libnr/nr-convex-hull-ops.h b/src/libnr/nr-convex-hull-ops.h
new file mode 100644
index 000000000..2e96bf367
--- /dev/null
+++ b/src/libnr/nr-convex-hull-ops.h
@@ -0,0 +1,29 @@
+#ifndef SEEN_NR_CONVEX_HULL_FNS_H
+#define SEEN_NR_CONVEX_HULL_FNS_H
+
+/* ex:set et ts=4 sw=4: */
+
+/*
+ * A class representing the convex hull of a set of points.
+ *
+ * Copyright 2004 MenTaLguY <mental@rydia.net>
+ *
+ * This code is licensed under the GNU GPL; see COPYING for more information.
+ */
+
+#include <libnr/nr-matrix-fns.h>
+#include <libnr/nr-convex-hull.h>
+
+namespace NR {
+
+ConvexHull operator*(const Rect &r, const Matrix &m) {
+ ConvexHull points(r.corner(0));
+ for ( unsigned i = 1 ; i < 4 ; i++ ) {
+ points.add(r.corner(i));
+ }
+ return points;
+}
+
+} /* namespace NR */
+
+#endif
diff --git a/src/libnr/nr-convex-hull.h b/src/libnr/nr-convex-hull.h
new file mode 100644
index 000000000..28cde376d
--- /dev/null
+++ b/src/libnr/nr-convex-hull.h
@@ -0,0 +1,49 @@
+#ifndef SEEN_NR_CONVEX_HULL_H
+#define SEEN_NR_CONVEX_HULL_H
+
+/* ex:set et ts=4 sw=4: */
+
+/*
+ * A class representing the convex hull of a set of points.
+ *
+ * Copyright 2004 MenTaLguY <mental@rydia.net>
+ *
+ * This code is licensed under the GNU GPL; see COPYING for more information.
+ */
+
+#include <libnr/nr-rect.h>
+
+namespace NR {
+
+class ConvexHull {
+public:
+ explicit ConvexHull(Point const &p) : _bounds(p, p) {}
+
+ Point midpoint() const {
+ return _bounds.midpoint();
+ }
+
+ void add(Point const &p) {
+ _bounds.expandTo(p);
+ }
+ void add(Rect const &p) {
+ // Note that this is a hack. when convexhull actually works
+ // you will need to add all four points.
+ _bounds.expandTo(p.min());
+ _bounds.expandTo(p.max());
+ }
+ void add(ConvexHull const &h) {
+ _bounds.expandTo(h._bounds);
+ }
+
+ Rect const &bounds() const {
+ return _bounds;
+ }
+
+private:
+ Rect _bounds;
+};
+
+} /* namespace NR */
+
+#endif
diff --git a/src/libnr/nr-coord.h b/src/libnr/nr-coord.h
new file mode 100644
index 000000000..668e2b460
--- /dev/null
+++ b/src/libnr/nr-coord.h
@@ -0,0 +1,29 @@
+#ifndef SEEN_NR_COORD_H
+#define SEEN_NR_COORD_H
+
+namespace NR {
+
+/**
+ * A "real" type with sufficient precision for coordinates.
+ *
+ * You may safely assume that double (or even float) provides enough precision for storing
+ * on-canvas points, and hence that double provides enough precision for dot products of
+ * differences of on-canvas points.
+ */
+typedef double Coord;
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_COORD_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-dim2.h b/src/libnr/nr-dim2.h
new file mode 100644
index 000000000..d06fd4227
--- /dev/null
+++ b/src/libnr/nr-dim2.h
@@ -0,0 +1,22 @@
+#ifndef SEEN_NR_DIM2_H
+#define SEEN_NR_DIM2_H
+
+namespace NR {
+
+enum Dim2 { X=0, Y };
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_DIM2_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-forward.h b/src/libnr/nr-forward.h
new file mode 100644
index 000000000..cbc4d8eec
--- /dev/null
+++ b/src/libnr/nr-forward.h
@@ -0,0 +1,42 @@
+#ifndef __NR_FORWARD_H__
+#define __NR_FORWARD_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+namespace NR {
+class Matrix;
+class Point;
+class Rect;
+class rotate;
+class scale;
+class translate;
+}
+
+class NArtBpath;
+struct NRBPath;
+struct NRPixBlock;
+struct NRMatrix;
+struct NRPoint;
+struct NRRect;
+struct NRRectL;
+
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-gradient.cpp b/src/libnr/nr-gradient.cpp
new file mode 100644
index 000000000..fa4f9f91f
--- /dev/null
+++ b/src/libnr/nr-gradient.cpp
@@ -0,0 +1,317 @@
+#define __NR_GRADIENT_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixops.h>
+#include <libnr/nr-pixblock-pixel.h>
+#include <libnr/nr-blit.h>
+#include <libnr/nr-gradient.h>
+
+#define NRG_MASK (NR_GRADIENT_VECTOR_LENGTH - 1)
+#define NRG_2MASK ((long long) ((NR_GRADIENT_VECTOR_LENGTH << 1) - 1))
+
+/* Radial */
+
+static void nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
+static void nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
+static void nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
+static void nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb);
+static void nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb);
+
+NRRenderer *
+nr_rgradient_renderer_setup(NRRGradientRenderer *rgr,
+ unsigned char const *cv,
+ unsigned spread,
+ NRMatrix const *gs2px,
+ float cx, float cy,
+ float fx, float fy,
+ float r)
+{
+ rgr->vector = cv;
+ rgr->spread = spread;
+
+ if (r < NR_EPSILON) {
+ rgr->renderer.render = nr_rgradient_render_block_end;
+ } else if (NR_DF_TEST_CLOSE(cx, fx, NR_EPSILON) &&
+ NR_DF_TEST_CLOSE(cy, fy, NR_EPSILON)) {
+ rgr->renderer.render = nr_rgradient_render_block_symmetric;
+
+ nr_matrix_invert(&rgr->px2gs, gs2px);
+ rgr->px2gs.c[0] *= (NR_GRADIENT_VECTOR_LENGTH / r);
+ rgr->px2gs.c[1] *= (NR_GRADIENT_VECTOR_LENGTH / r);
+ rgr->px2gs.c[2] *= (NR_GRADIENT_VECTOR_LENGTH / r);
+ rgr->px2gs.c[3] *= (NR_GRADIENT_VECTOR_LENGTH / r);
+ rgr->px2gs.c[4] -= cx;
+ rgr->px2gs.c[5] -= cy;
+ rgr->px2gs.c[4] *= (NR_GRADIENT_VECTOR_LENGTH / r);
+ rgr->px2gs.c[5] *= (NR_GRADIENT_VECTOR_LENGTH / r);
+
+ rgr->cx = 0.0;
+ rgr->cy = 0.0;
+ rgr->fx = rgr->cx;
+ rgr->fy = rgr->cy;
+ rgr->r = 1.0;
+ } else {
+ rgr->renderer.render = nr_rgradient_render_block_optimized;
+
+ NR::Coord const df = hypot(fx - cx, fy - cy);
+ if (df >= r) {
+ fx = cx + (fx - cx ) * r / (float) df;
+ fy = cy + (fy - cy ) * r / (float) df;
+ }
+
+ NRMatrix n2gs;
+ n2gs.c[0] = cx - fx;
+ n2gs.c[1] = cy - fy;
+ n2gs.c[2] = cy - fy;
+ n2gs.c[3] = fx - cx;
+ n2gs.c[4] = fx;
+ n2gs.c[5] = fy;
+
+ NRMatrix n2px;
+ nr_matrix_multiply(&n2px, &n2gs, gs2px);
+ nr_matrix_invert(&rgr->px2gs, &n2px);
+
+ rgr->cx = 1.0;
+ rgr->cy = 0.0;
+ rgr->fx = 0.0;
+ rgr->fy = 0.0;
+ rgr->r = r / (float) hypot(fx - cx, fy - cy);
+ rgr->C = 1.0F - rgr->r * rgr->r;
+ /* INVARIANT: C < 0 */
+ rgr->C = MIN(rgr->C, -NR_EPSILON);
+ }
+
+ return (NRRenderer *) rgr;
+}
+
+static void
+nr_rgradient_render_block_symmetric(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+{
+ NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
+ nr_rgradient_render_generic_symmetric(rgr, pb);
+}
+
+static void
+nr_rgradient_render_block_optimized(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+{
+ NRRGradientRenderer *rgr = (NRRGradientRenderer *) r;
+ nr_rgradient_render_generic_optimized(rgr, pb);
+}
+
+static void
+nr_rgradient_render_block_end(NRRenderer *r, NRPixBlock *pb, NRPixBlock *m)
+{
+ unsigned char const *c = ((NRRGradientRenderer *) r)->vector + 4 * (NR_GRADIENT_VECTOR_LENGTH - 1);
+
+ nr_blit_pixblock_mask_rgba32(pb, m, (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]);
+}
+
+/*
+ * The archetype is following
+ *
+ * gx gy - pixel coordinates
+ * Px Py - coordinates, where Fx Fy - gx gy line intersects with circle
+ *
+ * (1) (gx - fx) * (Py - fy) = (gy - fy) * (Px - fx)
+ * (2) (Px - cx) * (Px - cx) + (Py - cy) * (Py - cy) = r * r
+ *
+ * (3) Py = (Px - fx) * (gy - fy) / (gx - fx) + fy
+ * (4) (gy - fy) / (gx - fx) = D
+ * (5) Py = D * Px - D * fx + fy
+ *
+ * (6) D * fx - fy + cy = N
+ * (7) Px * Px - 2 * Px * cx + cx * cx + (D * Px) * (D * Px) - 2 * (D * Px) * N + N * N = r * r
+ * (8) (D * D + 1) * (Px * Px) - 2 * (cx + D * N) * Px + cx * cx + N * N = r * r
+ *
+ * (9) A = D * D + 1
+ * (10) B = -2 * (cx + D * N)
+ * (11) C = cx * cx + N * N - r * r
+ *
+ * (12) Px = (-B +- SQRT(B * B - 4 * A * C)) / 2 * A
+ */
+
+static void
+nr_rgradient_render_generic_symmetric(NRRGradientRenderer *rgr, NRPixBlock *pb)
+{
+ NR::Coord const dx = rgr->px2gs.c[0];
+ NR::Coord const dy = rgr->px2gs.c[1];
+
+ if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8P) {
+ for (int y = pb->area.y0; y < pb->area.y1; y++) {
+ unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
+ NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
+ NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
+ for (int x = pb->area.x0; x < pb->area.x1; x++) {
+ NR::Coord const pos = hypot(gx, gy);
+ int idx;
+ if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
+ idx = (int) ((long long) pos & NRG_2MASK);
+ if (idx > NRG_MASK) idx = NRG_2MASK - idx;
+ } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
+ idx = (int) ((long long) pos & NRG_MASK);
+ } else {
+ idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
+ }
+ unsigned char const *s = rgr->vector + 4 * idx;
+ d[0] = NR_COMPOSENPP(s[0], s[3], d[0], d[3]);
+ d[1] = NR_COMPOSENPP(s[1], s[3], d[1], d[3]);
+ d[2] = NR_COMPOSENPP(s[2], s[3], d[2], d[3]);
+ d[3] = (255*255 - (255 - s[3]) * (255 - d[3]) + 127) / 255;
+ d += 4;
+ gx += dx;
+ gy += dy;
+ }
+ }
+ } else if (pb->mode == NR_PIXBLOCK_MODE_R8G8B8A8N) {
+ for (int y = pb->area.y0; y < pb->area.y1; y++) {
+ unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
+ NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
+ NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
+ for (int x = pb->area.x0; x < pb->area.x1; x++) {
+ NR::Coord const pos = hypot(gx, gy);
+ int idx;
+ if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
+ idx = (int) ((long long) pos & NRG_2MASK);
+ if (idx > NRG_MASK) idx = NRG_2MASK - idx;
+ } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
+ idx = (int) ((long long) pos & NRG_MASK);
+ } else {
+ idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
+ }
+ unsigned char const *s = rgr->vector + 4 * idx;
+ if (s[3] == 255) {
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = 255;
+ } else if (s[3] != 0) {
+ unsigned ca = 255*255 - (255 - s[3]) * (255 - d[3]);
+ d[0] = NR_COMPOSENNN_A7(s[0], s[3], d[0], d[3], ca);
+ d[1] = NR_COMPOSENNN_A7(s[1], s[3], d[1], d[3], ca);
+ d[2] = NR_COMPOSENNN_A7(s[2], s[3], d[2], d[3], ca);
+ d[3] = (ca + 127) / 255;
+ }
+ d += 4;
+ gx += dx;
+ gy += dy;
+ }
+ }
+ } else {
+ NRPixBlock spb;
+ nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
+ (unsigned char *) rgr->vector,
+ 4 * NR_GRADIENT_VECTOR_LENGTH,
+ 0, 0);
+ int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
+ ? 1
+ : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
+ ? 3
+ : 4 );
+
+ for (int y = pb->area.y0; y < pb->area.y1; y++) {
+ unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - pb->area.y0) * pb->rs;
+ NR::Coord gx = rgr->px2gs.c[0] * pb->area.x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
+ NR::Coord gy = rgr->px2gs.c[1] * pb->area.x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
+ for (int x = pb->area.x0; x < pb->area.x1; x++) {
+ NR::Coord const pos = hypot(gx, gy);
+ int idx;
+ if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
+ idx = (int) ((long long) pos & NRG_2MASK);
+ if (idx > NRG_MASK) idx = NRG_2MASK - idx;
+ } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
+ idx = (int) ((long long) pos & NRG_MASK);
+ } else {
+ idx = (int) CLAMP(pos, 0, (double) NRG_MASK);
+ }
+ unsigned char const *s = rgr->vector + 4 * idx;
+ nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
+ d += bpp;
+ gx += dx;
+ gy += dy;
+ }
+ }
+
+ nr_pixblock_release(&spb);
+ }
+}
+
+static void
+nr_rgradient_render_generic_optimized(NRRGradientRenderer *rgr, NRPixBlock *pb)
+{
+ int const x0 = pb->area.x0;
+ int const y0 = pb->area.y0;
+ int const x1 = pb->area.x1;
+ int const y1 = pb->area.y1;
+ int const rs = pb->rs;
+
+ NRPixBlock spb;
+ nr_pixblock_setup_extern(&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, NR_GRADIENT_VECTOR_LENGTH, 1,
+ (unsigned char *) rgr->vector,
+ 4 * NR_GRADIENT_VECTOR_LENGTH,
+ 0, 0);
+ int const bpp = ( pb->mode == NR_PIXBLOCK_MODE_A8
+ ? 1
+ : pb->mode == NR_PIXBLOCK_MODE_R8G8B8
+ ? 3
+ : 4 );
+
+ for (int y = y0; y < y1; y++) {
+ unsigned char *d = NR_PIXBLOCK_PX(pb) + (y - y0) * rs;
+ NR::Coord gx = rgr->px2gs.c[0] * x0 + rgr->px2gs.c[2] * y + rgr->px2gs.c[4];
+ NR::Coord gy = rgr->px2gs.c[1] * x0 + rgr->px2gs.c[3] * y + rgr->px2gs.c[5];
+ NR::Coord const dx = rgr->px2gs.c[0];
+ NR::Coord const dy = rgr->px2gs.c[1];
+ for (int x = x0; x < x1; x++) {
+ NR::Coord const gx2 = gx * gx;
+ NR::Coord const gxy2 = gx2 + gy * gy;
+ NR::Coord const qgx2_4 = gx2 - rgr->C * gxy2;
+ /* INVARIANT: qgx2_4 >= 0.0 */
+ /* qgx2_4 = MAX(qgx2_4, 0.0); */
+ NR::Coord const pxgx = gx + sqrt(qgx2_4);
+ /* We can safely divide by 0 here */
+ /* If we are sure pxgx cannot be -0 */
+ NR::Coord const pos = gxy2 / pxgx * NR_GRADIENT_VECTOR_LENGTH;
+ int idx;
+ if (pos < (1U << 31)) {
+ if (rgr->spread == NR_GRADIENT_SPREAD_REFLECT) {
+ idx = (int) ((long long) pos & NRG_2MASK);
+ if (idx > NRG_MASK) idx = NRG_2MASK - idx;
+ } else if (rgr->spread == NR_GRADIENT_SPREAD_REPEAT) {
+ idx = (int) ((long long) pos & NRG_MASK);
+ } else {
+ idx = (int) CLAMP(pos, 0, (double) (NR_GRADIENT_VECTOR_LENGTH - 1));
+ }
+ } else {
+ idx = NR_GRADIENT_VECTOR_LENGTH - 1;
+ }
+ unsigned char const *s = rgr->vector + 4 * idx;
+ nr_compose_pixblock_pixblock_pixel(pb, d, &spb, s);
+ d += bpp;
+
+ gx += dx;
+ gy += dy;
+ }
+ }
+
+ nr_pixblock_release(&spb);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-gradient.h b/src/libnr/nr-gradient.h
new file mode 100644
index 000000000..f9cb1ba45
--- /dev/null
+++ b/src/libnr/nr-gradient.h
@@ -0,0 +1,47 @@
+#ifndef __NR_GRADIENT_H__
+#define __NR_GRADIENT_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-matrix.h>
+#include <libnr/nr-render.h>
+
+#define NR_GRADIENT_VECTOR_LENGTH 1024
+
+enum {
+ NR_GRADIENT_SPREAD_PAD,
+ NR_GRADIENT_SPREAD_REFLECT,
+ NR_GRADIENT_SPREAD_REPEAT
+};
+
+/* Radial */
+
+struct NRRGradientRenderer {
+ NRRenderer renderer;
+ const unsigned char *vector;
+ unsigned int spread;
+ NRMatrix px2gs;
+ float cx, cy;
+ float fx, fy;
+ float r;
+ float C;
+};
+
+NRRenderer *nr_rgradient_renderer_setup (NRRGradientRenderer *rgr,
+ const unsigned char *cv,
+ unsigned int spread,
+ const NRMatrix *gs2px,
+ float cx, float cy,
+ float fx, float fy,
+ float r);
+
+
+
+#endif
diff --git a/src/libnr/nr-i-coord.h b/src/libnr/nr-i-coord.h
new file mode 100644
index 000000000..f87dea3d5
--- /dev/null
+++ b/src/libnr/nr-i-coord.h
@@ -0,0 +1,25 @@
+#ifndef SEEN_NR_I_COORD_H
+#define SEEN_NR_I_COORD_H
+
+#include <glib/gtypes.h>
+
+namespace NR {
+
+/** An integer type with sufficient precision for coordinates. */
+typedef gint32 ICoord;
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_I_COORD_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-macros.h b/src/libnr/nr-macros.h
new file mode 100644
index 000000000..74a627ae7
--- /dev/null
+++ b/src/libnr/nr-macros.h
@@ -0,0 +1,71 @@
+#ifndef __NR_MACROS_H__
+#define __NR_MACROS_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <math.h>
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <assert.h>
+
+#define nr_new(t,n) ((t *) malloc ((n) * sizeof (t)))
+#define nr_free free
+#define nr_renew(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
+
+#ifndef TRUE
+#define TRUE (!0)
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef MAX
+#define MAX(a,b) (((a) < (b)) ? (b) : (a))
+#endif
+#ifndef MIN
+#define MIN(a,b) (((a) > (b)) ? (b) : (a))
+#endif
+
+#ifndef CLAMP
+/** Returns v bounded to within [a, b]. If v is NaN then returns a.
+ *
+ * \pre \a a \<= \a b.
+ */
+# define CLAMP(v,a,b) \
+ (assert (a <= b), \
+ ((v) >= (a)) \
+ ? (((v) > (b)) \
+ ? (b) \
+ : (v)) \
+ : (a))
+#endif
+
+#define NR_DF_TEST_CLOSE(a,b,e) (fabs ((a) - (b)) <= (e))
+
+// Todo: move these into nr-matrix.h
+#define NR_MATRIX_DF_TEST_TRANSFORM_CLOSE(a,b,e) (NR_DF_TEST_CLOSE ((*(a))[0], (*(b))[0], e) && \
+ NR_DF_TEST_CLOSE ((*(a))[1], (*(b))[1], e) && \
+ NR_DF_TEST_CLOSE ((*(a))[2], (*(b))[2], e) && \
+ NR_DF_TEST_CLOSE ((*(a))[3], (*(b))[3], e))
+#define NR_MATRIX_DF_TEST_TRANSLATE_CLOSE(a,b,e) (NR_DF_TEST_CLOSE ((*(a))[4], (*(b))[4], e) && \
+ NR_DF_TEST_CLOSE ((*(a))[5], (*(b))[5], e))
+#define NR_MATRIX_DF_TEST_CLOSE(a,b,e) (NR_MATRIX_DF_TEST_TRANSLATE_CLOSE (a, b, e) && \
+ NR_MATRIX_DF_TEST_TRANSFORM_CLOSE (a, b, e))
+
+#define NR_RECT_DFLS_TEST_EMPTY(a) (((a)->x0 >= (a)->x1) || ((a)->y0 >= (a)->y1))
+#define NR_RECT_DFLS_TEST_INTERSECT(a,b) (((a)->x0 < (b)->x1) && ((a)->x1 > (b)->x0) && ((a)->y0 < (b)->y1) && ((a)->y1 > (b)->y0))
+#define NR_RECT_DF_POINT_DF_TEST_INSIDE(r,p) (((p)->x >= (r)->x0) && ((p)->x < (r)->x1) && ((p)->y >= (r)->y0) && ((p)->y < (r)->y1))
+#define NR_RECT_LS_POINT_LS_TEST_INSIDE(r,p) (((p)->x >= (r)->x0) && ((p)->x < (r)->x1) && ((p)->y >= (r)->y0) && ((p)->y < (r)->y1))
+
+#define NR_MATRIX_D_TO_DOUBLE(m) ((m)->c)
+#define NR_MATRIX_D_FROM_DOUBLE(d) ((NRMatrix *) &(d)[0])
+
+#endif
diff --git a/src/libnr/nr-matrix-div.cpp b/src/libnr/nr-matrix-div.cpp
new file mode 100644
index 000000000..d6fb598b8
--- /dev/null
+++ b/src/libnr/nr-matrix-div.cpp
@@ -0,0 +1,22 @@
+#include "libnr/nr-matrix-ops.h"
+#include "libnr/nr-point-matrix-ops.h"
+
+NR::Point operator/(NR::Point const &p, NR::Matrix const &m) {
+ return p * m.inverse();
+}
+
+NR::Matrix operator/(NR::Matrix const &a, NR::Matrix const &b) {
+ return a * b.inverse();
+}
+
+
+/*
+ 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/libnr/nr-matrix-div.h b/src/libnr/nr-matrix-div.h
new file mode 100644
index 000000000..8669e2ce2
--- /dev/null
+++ b/src/libnr/nr-matrix-div.h
@@ -0,0 +1,21 @@
+#ifndef SEEN_LIBNR_NR_MATRIX_DIV_H
+#define SEEN_LIBNR_NR_MATRIX_DIV_H
+
+#include "libnr/nr-forward.h"
+
+NR::Point operator/(NR::Point const &, NR::Matrix const &);
+
+NR::Matrix operator/(NR::Matrix const &, NR::Matrix const &);
+
+#endif /* !SEEN_LIBNR_NR_MATRIX_DIV_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 :
diff --git a/src/libnr/nr-matrix-fns.cpp b/src/libnr/nr-matrix-fns.cpp
new file mode 100644
index 000000000..f392f3213
--- /dev/null
+++ b/src/libnr/nr-matrix-fns.cpp
@@ -0,0 +1,54 @@
+#include <libnr/nr-matrix-fns.h>
+
+namespace NR {
+
+Matrix elliptic_quadratic_form(Matrix const &m) {
+ double const od = m[0] * m[1] + m[2] * m[3];
+ return Matrix((m[0]*m[0] + m[1]*m[1]), od,
+ od, (m[2]*m[2] + m[3]*m[3]),
+ 0, 0);
+/* def quadratic_form((a, b), (c, d)):
+ return ((a*a + c*c), a*c+b*d),(a*c+b*d, (b*b + d*d)) */
+}
+
+Eigen::Eigen(Matrix const &m) {
+ double const B = -m[0] - m[3];
+ double const C = m[0]*m[3] - m[1]*m[2];
+ double const center = -B/2.0;
+ double const delta = sqrt(B*B-4*C)/2.0;
+ values = Point(center + delta, center - delta);
+ for (int i = 0; i < 2; i++) {
+ vectors[i] = unit_vector(rot90(Point(m[0]-values[i], m[1])));
+ }
+}
+
+/** Returns just the scale/rotate/skew part of the matrix without the translation part. */
+Matrix transform(Matrix const &m) {
+ Matrix const ret(m[0], m[1],
+ m[2], m[3],
+ 0, 0);
+ return ret;
+}
+
+translate get_translation(Matrix const &m) {
+ return translate(m[4], m[5]);
+}
+
+void matrix_print(const gchar *say, Matrix const &m)
+{
+ printf ("%s %g %g %g %g %g %g\n", say, m[0], m[1], m[2], m[3], m[4], m[5]);
+}
+
+} // namespace NR
+
+
+/*
+ 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/libnr/nr-matrix-fns.h b/src/libnr/nr-matrix-fns.h
new file mode 100644
index 000000000..208366746
--- /dev/null
+++ b/src/libnr/nr-matrix-fns.h
@@ -0,0 +1,50 @@
+#ifndef SEEN_NR_MATRIX_FNS_H
+#define SEEN_NR_MATRIX_FNS_H
+
+#include "nr-matrix.h"
+
+namespace NR {
+
+/** Given a matrix m such that unit_circle = m*x, this returns the
+ * quadratic form x*A*x = 1. */
+Matrix elliptic_quadratic_form(Matrix const &m);
+
+/** Given a matrix (ignoring the translation) this returns the eigen
+ * values and vectors. */
+class Eigen{
+public:
+ Point vectors[2];
+ Point values;
+ Eigen(Matrix const &m);
+};
+
+// Matrix factories
+Matrix from_basis(const Point x_basis, const Point y_basis, const Point offset=Point(0,0));
+
+Matrix identity();
+
+double expansion(Matrix const &m);
+
+bool transform_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon);
+bool translate_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon);
+bool matrix_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon);
+
+Matrix transform(Matrix const &m);
+translate get_translation(Matrix const &m);
+
+void matrix_print(const gchar *say, Matrix const &m);
+
+} // namespace NR
+
+#endif /* !SEEN_NR_MATRIX_FNS_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 :
diff --git a/src/libnr/nr-matrix-ops.h b/src/libnr/nr-matrix-ops.h
new file mode 100644
index 000000000..02fb28d0c
--- /dev/null
+++ b/src/libnr/nr-matrix-ops.h
@@ -0,0 +1,45 @@
+/* operator functions for NR::Matrix. */
+#ifndef SEEN_NR_MATRIX_OPS_H
+#define SEEN_NR_MATRIX_OPS_H
+
+#include <libnr/nr-matrix.h>
+
+namespace NR {
+
+inline bool operator==(Matrix const &a, Matrix const &b)
+{
+ for(unsigned i = 0; i < 6; ++i) {
+ if ( a[i] != b[i] ) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(Matrix const &a, Matrix const &b)
+{
+ return !( a == b );
+}
+
+Matrix operator*(Matrix const &a, Matrix const &b);
+
+inline Matrix operator*(Matrix const &a, NRMatrix const &b)
+{
+ return a * NR::Matrix(b);
+}
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_MATRIX_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix-rotate-ops.cpp b/src/libnr/nr-matrix-rotate-ops.cpp
new file mode 100644
index 000000000..625291575
--- /dev/null
+++ b/src/libnr/nr-matrix-rotate-ops.cpp
@@ -0,0 +1,18 @@
+#include "libnr/nr-matrix-ops.h"
+
+NR::Matrix operator*(NR::Matrix const &m, NR::rotate const &r)
+{
+ return m * NR::Matrix(r);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix-rotate-ops.h b/src/libnr/nr-matrix-rotate-ops.h
new file mode 100644
index 000000000..44d9c8726
--- /dev/null
+++ b/src/libnr/nr-matrix-rotate-ops.h
@@ -0,0 +1,20 @@
+#ifndef SEEN_LIBNR_NR_MATRIX_ROTATE_OPS_H
+#define SEEN_LIBNR_NR_MATRIX_ROTATE_OPS_H
+
+#include "libnr/nr-forward.h"
+
+NR::Matrix operator*(NR::Matrix const &m, NR::rotate const &r);
+
+
+#endif /* !SEEN_LIBNR_NR_MATRIX_ROTATE_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix-scale-ops.cpp b/src/libnr/nr-matrix-scale-ops.cpp
new file mode 100644
index 000000000..90cbaf585
--- /dev/null
+++ b/src/libnr/nr-matrix-scale-ops.cpp
@@ -0,0 +1,37 @@
+#include "libnr/nr-matrix-ops.h"
+
+NR::Matrix
+operator/(NR::Matrix const &m, NR::scale const &s)
+{
+ using NR::X; using NR::Y;
+ NR::Matrix ret(m);
+ ret[0] /= s[X]; ret[1] /= s[Y];
+ ret[2] /= s[X]; ret[3] /= s[Y];
+ ret[4] /= s[X]; ret[5] /= s[Y];
+ assert_close( ret, m * NR::Matrix(s.inverse()) );
+ return ret;
+}
+
+NR::Matrix
+operator*(NR::Matrix const &m, NR::scale const &s)
+{
+ using NR::X; using NR::Y;
+ NR::Matrix ret(m);
+ ret[0] *= s[X]; ret[1] *= s[Y];
+ ret[2] *= s[X]; ret[3] *= s[Y];
+ ret[4] *= s[X]; ret[5] *= s[Y];
+ assert_close( ret, m * NR::Matrix(s) );
+ return ret;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix-scale-ops.h b/src/libnr/nr-matrix-scale-ops.h
new file mode 100644
index 000000000..dee275182
--- /dev/null
+++ b/src/libnr/nr-matrix-scale-ops.h
@@ -0,0 +1,14 @@
+#ifndef SEEN_LIBNR_NR_MATRIX_SCALE_OPS_H
+#define SEEN_LIBNR_NR_MATRIX_SCALE_OPS_H
+/** \file
+ * Declarations (and definition if inline) of operator blah (NR::Matrix, NR::scale).
+ */
+
+#include "libnr/nr-forward.h"
+
+NR::Matrix operator/(NR::Matrix const &m, NR::scale const &s);
+
+NR::Matrix operator*(NR::Matrix const &m, NR::scale const &s);
+
+
+#endif /* !SEEN_LIBNR_NR_MATRIX_SCALE_OPS_H */
diff --git a/src/libnr/nr-matrix-test.cpp b/src/libnr/nr-matrix-test.cpp
new file mode 100644
index 000000000..b7f064e48
--- /dev/null
+++ b/src/libnr/nr-matrix-test.cpp
@@ -0,0 +1,197 @@
+#include <utest/utest.h>
+#include <libnr/nr-matrix.h>
+#include <libnr/nr-matrix-fns.h>
+#include <libnr/nr-matrix-ops.h>
+#include <libnr/nr-matrix-rotate-ops.h>
+#include <libnr/nr-matrix-scale-ops.h>
+#include <libnr/nr-point-matrix-ops.h>
+#include <libnr/nr-rotate.h>
+#include <libnr/nr-rotate-ops.h>
+#include <libnr/nr-scale-ops.h>
+#include <libnr/nr-scale-translate-ops.h>
+#include <libnr/nr-translate.h>
+#include <libnr/nr-translate-ops.h>
+#include <libnr/nr-translate-scale-ops.h>
+using NR::Matrix;
+using NR::X;
+using NR::Y;
+
+inline bool point_equalp(NR::Point const &a, NR::Point const &b)
+{
+ return ( NR_DF_TEST_CLOSE(a[X], b[X], 1e-5) &&
+ NR_DF_TEST_CLOSE(a[Y], b[Y], 1e-5) );
+}
+
+int main(int argc, char *argv[])
+{
+ int rc = EXIT_SUCCESS;
+
+ Matrix const m_id(NR::identity());
+ NR::rotate const r_id(NR::Point(1, 0));
+ NR::translate const t_id(0, 0);
+
+ utest_start("Matrix");
+
+ Matrix const c16(1.0, 2.0,
+ 3.0, 4.0,
+ 5.0, 6.0);
+ UTEST_TEST("basic constructors, operator=") {
+ Matrix const c16_copy(c16);
+ Matrix c16_eq(m_id);
+ c16_eq = c16;
+ for(unsigned i = 0; i < 6; ++i) {
+ UTEST_ASSERT( c16[i] == 1.0 + i );
+ UTEST_ASSERT( c16[i] == c16_copy[i] );
+ UTEST_ASSERT( c16[i] == c16_eq[i] );
+ UTEST_ASSERT( m_id[i] == double( i == 0 || i == 3 ) );
+ }
+ }
+
+ UTEST_TEST("scale constructor") {
+ NR::scale const s(2.0, 3.0);
+ NR::Matrix const ms(s);
+ NR::Point const p(5.0, 7.0);
+ UTEST_ASSERT( p * s == NR::Point(10.0, 21.0) );
+ UTEST_ASSERT( p * ms == NR::Point(10.0, 21.0) );
+ }
+
+ NR::rotate const r86(NR::Point(.8, .6));
+ NR::Matrix const mr86(r86);
+ UTEST_TEST("rotate constructor") {
+ NR::Point const p0(1.0, 0.0);
+ NR::Point const p90(0.0, 1.0);
+ UTEST_ASSERT( p0 * r86 == NR::Point(.8, .6) );
+ UTEST_ASSERT( p0 * mr86 == NR::Point(.8, .6) );
+ UTEST_ASSERT( p90 * r86 == NR::Point(-.6, .8) );
+ UTEST_ASSERT( p90 * mr86 == NR::Point(-.6, .8) );
+ UTEST_ASSERT(matrix_equalp(Matrix( r86 * r86 ),
+ mr86 * mr86,
+ 1e-14));
+ }
+
+ NR::translate const t23(2.0, 3.0);
+ UTEST_TEST("translate constructor") {
+ NR::Matrix const mt23(t23);
+ NR::Point const b(-2.0, 3.0);
+ UTEST_ASSERT( b * t23 == b * mt23 );
+ }
+
+ NR::scale const s_id(1.0, 1.0);
+ UTEST_TEST("test_identity") {
+ UTEST_ASSERT(m_id.test_identity());
+ UTEST_ASSERT(Matrix(t_id).test_identity());
+ UTEST_ASSERT(!(Matrix(NR::translate(-2, 3)).test_identity()));
+ UTEST_ASSERT(Matrix(r_id).test_identity());
+ NR::rotate const rot180(NR::Point(-1, 0));
+ UTEST_ASSERT(!(Matrix(rot180).test_identity()));
+ UTEST_ASSERT(Matrix(s_id).test_identity());
+ UTEST_ASSERT(!(Matrix(NR::scale(1.0, 0.0)).test_identity()));
+ UTEST_ASSERT(!(Matrix(NR::scale(0.0, 1.0)).test_identity()));
+ UTEST_ASSERT(!(Matrix(NR::scale(1.0, -1.0)).test_identity()));
+ UTEST_ASSERT(!(Matrix(NR::scale(-1.0, -1.0)).test_identity()));
+ }
+
+ UTEST_TEST("inverse") {
+ UTEST_ASSERT( m_id.inverse() == m_id );
+ UTEST_ASSERT( Matrix(t23).inverse() == Matrix(NR::translate(-2.0, -3.0)) );
+ NR::scale const s2(-4.0, 2.0);
+ NR::scale const sp5(-.25, .5);
+ UTEST_ASSERT( Matrix(s2).inverse() == Matrix(sp5) );
+ }
+
+ UTEST_TEST("nr_matrix_invert") {
+ NRMatrix const nr_m_id(m_id);
+ Matrix const m_s2(NR::scale(-4.0, 2.0));
+ NRMatrix const nr_s2(m_s2);
+ Matrix const m_sp5(NR::scale(-.25, .5));
+ NRMatrix const nr_sp5(m_sp5);
+ Matrix const m_t23(t23);
+ NRMatrix const nr_t23(m_t23);
+ NRMatrix inv;
+ nr_matrix_invert(&inv, &nr_m_id);
+ UTEST_ASSERT( Matrix(inv) == m_id );
+ nr_matrix_invert(&inv, &nr_t23);
+ UTEST_ASSERT( Matrix(inv) == Matrix(NR::translate(-2.0, -3.0)) );
+ nr_matrix_invert(&inv, &nr_s2);
+ UTEST_ASSERT( Matrix(inv) == Matrix(nr_sp5) );
+ nr_matrix_invert(&inv, &nr_sp5);
+ UTEST_ASSERT( Matrix(inv) == Matrix(nr_s2) );
+
+ /* Test that nr_matrix_invert handles src == dest. */
+ inv = nr_s2;
+ nr_matrix_invert(&inv, &inv);
+ UTEST_ASSERT( Matrix(inv) == Matrix(nr_sp5) );
+ inv = nr_t23;
+ nr_matrix_invert(&inv, &inv);
+ UTEST_ASSERT( Matrix(inv) == Matrix(NR::translate(-2.0, -3.0)) );
+ }
+
+ UTEST_TEST("elliptic quadratic form") {
+ NR::Matrix const aff(1.0, 1.0,
+ 0.0, 1.0,
+ 5.0, 6.0);
+ NR::Matrix const invaff = aff.inverse();
+ UTEST_ASSERT( invaff[1] == -1.0 );
+
+ NR::Matrix const ef(elliptic_quadratic_form(invaff));
+ NR::Matrix const exp_ef(2, -1,
+ -1, 1,
+ 0, 0);
+ UTEST_ASSERT( ef == exp_ef );
+ }
+
+ UTEST_TEST("Matrix * rotate") {
+ NR::Matrix const ma(2.0, -1.0,
+ 4.0, 4.0,
+ -0.5, 2.0);
+ NR::Matrix const a_r86( ma * r86 );
+ NR::Matrix const ma1( a_r86 * r86.inverse() );
+ UTEST_ASSERT(matrix_equalp(ma1, ma, 1e-12));
+ NR::Matrix const exp_a_r86( 2*.8 + -1*-.6, 2*.6 + -1*.8,
+ 4*.8 + 4*-.6, 4*.6 + 4*.8,
+ -.5*.8 + 2*-.6, -.5*.6 + 2*.8 );
+ UTEST_ASSERT(matrix_equalp(a_r86, exp_a_r86, 1e-12));
+ }
+
+ UTEST_TEST("translate*scale, scale*translate") {
+ NR::translate const t2n4(2, -4);
+ NR::scale const sn2_8(-2, 8);
+ NR::Matrix const exp_ts(-2, 0,
+ 0, 8,
+ -4, -32);
+ NR::Matrix const exp_st(-2, 0,
+ 0, 8,
+ 2, -4);
+ UTEST_ASSERT( exp_ts == t2n4 * sn2_8 );
+ UTEST_ASSERT( exp_st == sn2_8 * t2n4 );
+ }
+
+ UTEST_TEST("Matrix * scale") {
+ NR::Matrix const ma(2.0, -1.0,
+ 4.0, 4.0,
+ -0.5, 2.0);
+ NR::scale const sn2_8(-2, 8);
+ NR::Matrix const exp_as(-4, -8,
+ -8, 32,
+ 1, 16);
+ UTEST_ASSERT( ma * sn2_8 == exp_as );
+ }
+
+ if (!utest_end()) {
+ rc = EXIT_FAILURE;
+ }
+
+ return rc;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix-test.h b/src/libnr/nr-matrix-test.h
new file mode 100644
index 000000000..476852890
--- /dev/null
+++ b/src/libnr/nr-matrix-test.h
@@ -0,0 +1,221 @@
+#include <cxxtest/TestSuite.h>
+
+#include <libnr/nr-matrix.h>
+#include <libnr/nr-matrix-fns.h>
+#include <libnr/nr-matrix-ops.h>
+#include <libnr/nr-matrix-rotate-ops.h>
+#include <libnr/nr-matrix-scale-ops.h>
+#include <libnr/nr-point-matrix-ops.h>
+#include <libnr/nr-rotate.h>
+#include <libnr/nr-rotate-ops.h>
+#include <libnr/nr-scale-ops.h>
+#include <libnr/nr-scale-translate-ops.h>
+#include <libnr/nr-translate.h>
+#include <libnr/nr-translate-ops.h>
+#include <libnr/nr-translate-scale-ops.h>
+using NR::Matrix;
+using NR::X;
+using NR::Y;
+
+inline bool point_equalp(NR::Point const &a, NR::Point const &b)
+{
+ return ( NR_DF_TEST_CLOSE(a[X], b[X], 1e-5) &&
+ NR_DF_TEST_CLOSE(a[Y], b[Y], 1e-5) );
+}
+
+class NrMatrixTest : public CxxTest::TestSuite
+{
+public:
+
+ NrMatrixTest() :
+ m_id( NR::identity() ),
+ r_id( NR::Point(1, 0) ),
+ t_id( 0, 0 ),
+ c16( 1.0, 2.0,
+ 3.0, 4.0,
+ 5.0, 6.0),
+ r86( NR::Point(.8, .6) ),
+ mr86( r86 ),
+ t23( 2.0, 3.0 ),
+ s_id( 1.0, 1.0 )
+ {
+ }
+ virtual ~NrMatrixTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static NrMatrixTest *createSuite() { return new NrMatrixTest(); }
+ static void destroySuite( NrMatrixTest *suite ) { delete suite; }
+
+ Matrix const m_id;
+ NR::rotate const r_id;
+ NR::translate const t_id;
+ Matrix const c16;
+ NR::rotate const r86;
+ NR::Matrix const mr86;
+ NR::translate const t23;
+ NR::scale const s_id;
+
+
+
+
+ void testCtorsAssignmentOp(void)
+ {
+ Matrix const c16_copy(c16);
+ Matrix c16_eq(m_id);
+ c16_eq = c16;
+ for(unsigned i = 0; i < 6; ++i) {
+ TS_ASSERT_EQUALS( c16[i], 1.0 + i );
+ TS_ASSERT_EQUALS( c16[i], c16_copy[i] );
+ TS_ASSERT_EQUALS( c16[i], c16_eq[i] );
+ TS_ASSERT_EQUALS( m_id[i], double( i == 0 || i == 3 ) );
+ }
+ }
+
+ void testScaleCtor(void)
+ {
+ NR::scale const s(2.0, 3.0);
+ NR::Matrix const ms(s);
+ NR::Point const p(5.0, 7.0);
+ TS_ASSERT_EQUALS( p * s, NR::Point(10.0, 21.0) );
+ TS_ASSERT_EQUALS( p * ms, NR::Point(10.0, 21.0) );
+ }
+
+ void testRotateCtor(void)
+ {
+ NR::Point const p0(1.0, 0.0);
+ NR::Point const p90(0.0, 1.0);
+ TS_ASSERT_EQUALS( p0 * r86, NR::Point(.8, .6) );
+ TS_ASSERT_EQUALS( p0 * mr86, NR::Point(.8, .6) );
+ TS_ASSERT_EQUALS( p90 * r86, NR::Point(-.6, .8) );
+ TS_ASSERT_EQUALS( p90 * mr86, NR::Point(-.6, .8) );
+ TS_ASSERT( matrix_equalp(Matrix( r86 * r86 ),
+ mr86 * mr86,
+ 1e-14) );
+ }
+
+ void testTranslateCtor(void)
+ {
+ NR::Matrix const mt23(t23);
+ NR::Point const b(-2.0, 3.0);
+ TS_ASSERT_EQUALS( b * t23, b * mt23 );
+ }
+
+ void testIdentity(void)
+ {
+ TS_ASSERT( m_id.test_identity() );
+ TS_ASSERT( Matrix(t_id).test_identity() );
+ TS_ASSERT( !(Matrix(NR::translate(-2, 3)).test_identity()) );
+ TS_ASSERT( Matrix(r_id).test_identity() );
+ NR::rotate const rot180(NR::Point(-1, 0));
+ TS_ASSERT( !(Matrix(rot180).test_identity()) );
+ TS_ASSERT( Matrix(s_id).test_identity() );
+ TS_ASSERT( !(Matrix(NR::scale(1.0, 0.0)).test_identity()) );
+ TS_ASSERT( !(Matrix(NR::scale(0.0, 1.0)).test_identity()) );
+ TS_ASSERT( !(Matrix(NR::scale(1.0, -1.0)).test_identity()) );
+ TS_ASSERT( !(Matrix(NR::scale(-1.0, -1.0)).test_identity()) );
+ }
+
+ void testInverse(void)
+ {
+ TS_ASSERT_EQUALS( m_id.inverse(), m_id );
+ TS_ASSERT_EQUALS( Matrix(t23).inverse(), Matrix(NR::translate(-2.0, -3.0)) );
+ NR::scale const s2(-4.0, 2.0);
+ NR::scale const sp5(-.25, .5);
+ TS_ASSERT_EQUALS( Matrix(s2).inverse(), Matrix(sp5) );
+ }
+
+ void testNrMatrixInvert(void)
+ {
+ NRMatrix const nr_m_id(m_id);
+ Matrix const m_s2(NR::scale(-4.0, 2.0));
+ NRMatrix const nr_s2(m_s2);
+ Matrix const m_sp5(NR::scale(-.25, .5));
+ NRMatrix const nr_sp5(m_sp5);
+ Matrix const m_t23(t23);
+ NRMatrix const nr_t23(m_t23);
+ NRMatrix inv;
+ nr_matrix_invert(&inv, &nr_m_id);
+ TS_ASSERT_EQUALS( Matrix(inv), m_id );
+ nr_matrix_invert(&inv, &nr_t23);
+ TS_ASSERT_EQUALS( Matrix(inv), Matrix(NR::translate(-2.0, -3.0)) );
+ nr_matrix_invert(&inv, &nr_s2);
+ TS_ASSERT_EQUALS( Matrix(inv), Matrix(nr_sp5) );
+ nr_matrix_invert(&inv, &nr_sp5);
+ TS_ASSERT_EQUALS( Matrix(inv), Matrix(nr_s2) );
+
+ /* Test that nr_matrix_invert handles src == dest. */
+ inv = nr_s2;
+ nr_matrix_invert(&inv, &inv);
+ TS_ASSERT_EQUALS( Matrix(inv), Matrix(nr_sp5) );
+ inv = nr_t23;
+ nr_matrix_invert(&inv, &inv);
+ TS_ASSERT_EQUALS( Matrix(inv), Matrix(NR::translate(-2.0, -3.0)) );
+ }
+
+ void testEllipticQuadraticForm(void)
+ {
+ NR::Matrix const aff(1.0, 1.0,
+ 0.0, 1.0,
+ 5.0, 6.0);
+ NR::Matrix const invaff = aff.inverse();
+ TS_ASSERT_EQUALS( invaff[1], -1.0 );
+
+ NR::Matrix const ef(elliptic_quadratic_form(invaff));
+ NR::Matrix const exp_ef(2, -1,
+ -1, 1,
+ 0, 0);
+ TS_ASSERT_EQUALS( ef, exp_ef );
+ }
+
+ void testMatrixStarRotate(void)
+ {
+ NR::Matrix const ma(2.0, -1.0,
+ 4.0, 4.0,
+ -0.5, 2.0);
+ NR::Matrix const a_r86( ma * r86 );
+ NR::Matrix const ma1( a_r86 * r86.inverse() );
+ TS_ASSERT( matrix_equalp(ma1, ma, 1e-12) );
+ NR::Matrix const exp_a_r86( 2*.8 + -1*-.6, 2*.6 + -1*.8,
+ 4*.8 + 4*-.6, 4*.6 + 4*.8,
+ -.5*.8 + 2*-.6, -.5*.6 + 2*.8 );
+ TS_ASSERT( matrix_equalp(a_r86, exp_a_r86, 1e-12) );
+ }
+
+ void testTranslateStarScale_ScaleStarTranslate(void)
+ {
+ NR::translate const t2n4(2, -4);
+ NR::scale const sn2_8(-2, 8);
+ NR::Matrix const exp_ts(-2, 0,
+ 0, 8,
+ -4, -32);
+ NR::Matrix const exp_st(-2, 0,
+ 0, 8,
+ 2, -4);
+ TS_ASSERT_EQUALS( exp_ts, t2n4 * sn2_8 );
+ TS_ASSERT_EQUALS( exp_st, sn2_8 * t2n4 );
+ }
+
+ void testMatrixStarScale(void)
+ {
+ NR::Matrix const ma(2.0, -1.0,
+ 4.0, 4.0,
+ -0.5, 2.0);
+ NR::scale const sn2_8(-2, 8);
+ NR::Matrix const exp_as(-4, -8,
+ -8, 32,
+ 1, 16);
+ TS_ASSERT_EQUALS( ma * sn2_8, exp_as );
+ }
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix-translate-ops.cpp b/src/libnr/nr-matrix-translate-ops.cpp
new file mode 100644
index 000000000..0ccdcf9ce
--- /dev/null
+++ b/src/libnr/nr-matrix-translate-ops.cpp
@@ -0,0 +1,26 @@
+#include "libnr/nr-matrix-ops.h"
+
+namespace NR {
+
+Matrix
+operator*(Matrix const &m, translate const &t)
+{
+ Matrix ret(m);
+ ret[4] += t[X];
+ ret[5] += t[Y];
+ assert_close( ret, m * Matrix(t) );
+ return ret;
+}
+
+} // namespace NR
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix-translate-ops.h b/src/libnr/nr-matrix-translate-ops.h
new file mode 100644
index 000000000..f51bccaa1
--- /dev/null
+++ b/src/libnr/nr-matrix-translate-ops.h
@@ -0,0 +1,36 @@
+#ifndef SEEN_LIBNR_NR_MATRIX_TRANSLATE_OPS_H
+#define SEEN_LIBNR_NR_MATRIX_TRANSLATE_OPS_H
+
+/** \file
+ * Declarations (and definition if inline) of operator
+ * blah (NR::Matrix, NR::translate).
+ */
+
+#include "libnr/nr-matrix.h"
+#include "libnr/nr-translate.h"
+
+//NR::Matrix operator*(NR::Matrix const &m, NR::translate const &t);
+
+namespace NR {
+Matrix operator*(Matrix const &m, translate const &t);
+}
+
+inline NR::Matrix
+operator/(NR::Matrix const &numer, NR::translate const &denom)
+{
+ return numer * NR::translate(-denom.offset);
+}
+
+
+#endif /* !SEEN_LIBNR_NR_MATRIX_TRANSLATE_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix.cpp b/src/libnr/nr-matrix.cpp
new file mode 100644
index 000000000..24fa2e206
--- /dev/null
+++ b/src/libnr/nr-matrix.cpp
@@ -0,0 +1,607 @@
+#define __NR_MATRIX_C__
+
+/** \file
+ * Various matrix routines. Currently includes some NR::rotate etc. routines too.
+ */
+
+/*
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include "nr-matrix.h"
+
+
+
+/**
+ * Multiply two NRMatrices together, storing the result in d.
+ */
+NRMatrix *
+nr_matrix_multiply(NRMatrix *d, NRMatrix const *m0, NRMatrix const *m1)
+{
+ if (m0) {
+ if (m1) {
+ NR::Coord d0 = m0->c[0] * m1->c[0] + m0->c[1] * m1->c[2];
+ NR::Coord d1 = m0->c[0] * m1->c[1] + m0->c[1] * m1->c[3];
+ NR::Coord d2 = m0->c[2] * m1->c[0] + m0->c[3] * m1->c[2];
+ NR::Coord d3 = m0->c[2] * m1->c[1] + m0->c[3] * m1->c[3];
+ NR::Coord d4 = m0->c[4] * m1->c[0] + m0->c[5] * m1->c[2] + m1->c[4];
+ NR::Coord d5 = m0->c[4] * m1->c[1] + m0->c[5] * m1->c[3] + m1->c[5];
+
+ NR::Coord *dest = d->c;
+ *dest++ = d0;
+ *dest++ = d1;
+ *dest++ = d2;
+ *dest++ = d3;
+ *dest++ = d4;
+ *dest = d5;
+ } else {
+ *d = *m0;
+ }
+ } else {
+ if (m1) {
+ *d = *m1;
+ } else {
+ nr_matrix_set_identity(d);
+ }
+ }
+
+ return d;
+}
+
+
+
+
+/**
+ * Store the inverted value of Matrix m in d
+ */
+NRMatrix *
+nr_matrix_invert(NRMatrix *d, NRMatrix const *m)
+{
+ if (m) {
+ NR::Coord const det = m->c[0] * m->c[3] - m->c[1] * m->c[2];
+ if (!NR_DF_TEST_CLOSE(det, 0.0, NR_EPSILON)) {
+
+ NR::Coord const idet = 1.0 / det;
+ NR::Coord *dest = d->c;
+
+ /* Cache m->c[0] and m->c[4] in case d == m. */
+ NR::Coord const m_c0(m->c[0]);
+ NR::Coord const m_c4(m->c[4]);
+
+ /*0*/ *dest++ = m->c[3] * idet;
+ /*1*/ *dest++ = -m->c[1] * idet;
+ /*2*/ *dest++ = -m->c[2] * idet;
+ /*3*/ *dest++ = m_c0 * idet;
+ /*4*/ *dest++ = -m_c4 * d->c[0] - m->c[5] * d->c[2];
+ /*5*/ *dest = -m_c4 * d->c[1] - m->c[5] * d->c[3];
+
+ } else {
+ nr_matrix_set_identity(d);
+ }
+ } else {
+ nr_matrix_set_identity(d);
+ }
+
+ return d;
+}
+
+
+
+
+
+/**
+ * Set this matrix to a translation of x and y
+ */
+NRMatrix *
+nr_matrix_set_translate(NRMatrix *m, NR::Coord const x, NR::Coord const y)
+{
+ NR::Coord *dest = m->c;
+
+ *dest++ = 1.0; //0
+ *dest++ = 0.0; //1
+ *dest++ = 0.0; //2
+ *dest++ = 1.0; //3
+ *dest++ = x; //4
+ *dest = y; //5
+
+ return m;
+}
+
+
+
+
+
+/**
+ * Set this matrix to a scaling transform in sx and sy
+ */
+NRMatrix *
+nr_matrix_set_scale(NRMatrix *m, NR::Coord const sx, NR::Coord const sy)
+{
+ NR::Coord *dest = m->c;
+
+ *dest++ = sx; //0
+ *dest++ = 0.0; //1
+ *dest++ = 0.0; //2
+ *dest++ = sy; //3
+ *dest++ = 0.0; //4
+ *dest = 0.0; //5
+
+ return m;
+}
+
+
+
+
+
+/**
+ * Set this matrix to a rotating transform of angle 'theta' radians
+ */
+NRMatrix *
+nr_matrix_set_rotate(NRMatrix *m, NR::Coord const theta)
+{
+ NR::Coord const s = sin(theta);
+ NR::Coord const c = cos(theta);
+
+ NR::Coord *dest = m->c;
+
+ *dest++ = c; //0
+ *dest++ = s; //1
+ *dest++ = -s; //2
+ *dest++ = c; //3
+ *dest++ = 0.0; //4
+ *dest = 0.0; //5
+
+ return m;
+}
+
+
+
+
+
+
+
+
+
+/**
+ * Implement NR functions and methods
+ */
+namespace NR {
+
+
+
+
+
+/**
+ * Constructor. Assign to nr if not null, else identity
+ */
+Matrix::Matrix(NRMatrix const *nr)
+{
+ if (nr) {
+ assign(nr->c);
+ } else {
+ set_identity();
+ }
+}
+
+
+
+
+
+/**
+ * Multiply two matrices together
+ */
+Matrix operator*(Matrix const &m0, Matrix const &m1)
+{
+ NR::Coord const d0 = m0[0] * m1[0] + m0[1] * m1[2];
+ NR::Coord const d1 = m0[0] * m1[1] + m0[1] * m1[3];
+ NR::Coord const d2 = m0[2] * m1[0] + m0[3] * m1[2];
+ NR::Coord const d3 = m0[2] * m1[1] + m0[3] * m1[3];
+ NR::Coord const d4 = m0[4] * m1[0] + m0[5] * m1[2] + m1[4];
+ NR::Coord const d5 = m0[4] * m1[1] + m0[5] * m1[3] + m1[5];
+
+ Matrix ret( d0, d1, d2, d3, d4, d5 );
+
+ return ret;
+}
+
+
+
+
+
+/**
+ * Multiply a matrix by another
+ */
+Matrix &Matrix::operator*=(Matrix const &o)
+{
+ *this = *this * o;
+ return *this;
+}
+
+
+
+
+
+/**
+ * Multiply by a scaling matrix
+ */
+Matrix &Matrix::operator*=(scale const &other)
+{
+ /* This loop is massive overkill. Let's unroll.
+ * o _c[] goes from 0..5
+ * o other[] alternates between 0 and 1
+ */
+ /*
+ * for (unsigned i = 0; i < 3; ++i) {
+ * for (unsigned j = 0; j < 2; ++j) {
+ * this->_c[i * 2 + j] *= other[j];
+ * }
+ * }
+ */
+
+ NR::Coord const xscale = other[0];
+ NR::Coord const yscale = other[1];
+ NR::Coord *dest = _c;
+
+ /*i=0 j=0*/ *dest++ *= xscale;
+ /*i=0 j=1*/ *dest++ *= yscale;
+ /*i=1 j=0*/ *dest++ *= xscale;
+ /*i=1 j=1*/ *dest++ *= yscale;
+ /*i=2 j=0*/ *dest++ *= xscale;
+ /*i=2 j=1*/ *dest *= yscale;
+
+ return *this;
+}
+
+
+
+
+
+/**
+ * Return the inverse of this matrix. If an inverse is not defined,
+ * then return the identity matrix.
+ */
+Matrix Matrix::inverse() const
+{
+ Matrix d(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ NR::Coord const det = _c[0] * _c[3] - _c[1] * _c[2];
+ if (!NR_DF_TEST_CLOSE(det, 0.0, NR_EPSILON)) {
+
+ NR::Coord const idet = 1.0 / det;
+ NR::Coord *dest = d._c;
+
+ /*0*/ *dest++ = _c[3] * idet;
+ /*1*/ *dest++ = -_c[1] * idet;
+ /*2*/ *dest++ = -_c[2] * idet;
+ /*3*/ *dest++ = _c[0] * idet;
+ /*4*/ *dest++ = -_c[4] * d._c[0] - _c[5] * d._c[2];
+ /*5*/ *dest = -_c[4] * d._c[1] - _c[5] * d._c[3];
+
+ } else {
+ d.set_identity();
+ }
+
+ return d;
+}
+
+
+
+
+
+/**
+ * Set this matrix to Identity
+ */
+void Matrix::set_identity()
+{
+ NR::Coord *dest = _c;
+
+ *dest++ = 1.0; //0
+ *dest++ = 0.0; //1
+ *dest++ = 0.0; //2
+ *dest++ = 1.0; //3
+ // translation
+ *dest++ = 0.0; //4
+ *dest = 0.0; //5
+}
+
+
+
+
+
+/**
+ * return an Identity matrix
+ */
+Matrix identity()
+{
+ Matrix ret(1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, 0.0);
+ return ret;
+}
+
+
+
+
+
+/**
+ *
+ */
+Matrix from_basis(Point const x_basis, Point const y_basis, Point const offset)
+{
+ Matrix const ret(x_basis[X], y_basis[X],
+ x_basis[Y], y_basis[Y],
+ offset[X], offset[Y]);
+ return ret;
+}
+
+
+
+
+/**
+ * Returns a rotation matrix corresponding by the specified angle (in radians) about the origin.
+ *
+ * \see NR::rotate_degrees
+ *
+ * Angle direction in Inkscape code: If you use the traditional mathematics convention that y
+ * increases upwards, then positive angles are anticlockwise as per the mathematics convention. If
+ * you take the common non-mathematical convention that y increases downwards, then positive angles
+ * are clockwise, as is common outside of mathematics.
+ */
+rotate::rotate(NR::Coord const theta) :
+ vec(cos(theta),
+ sin(theta))
+{
+}
+
+
+
+
+
+/**
+ * Return the determinant of the Matrix
+ */
+NR::Coord Matrix::det() const
+{
+ return _c[0] * _c[3] - _c[1] * _c[2];
+}
+
+
+
+
+
+/**
+ * Return the scalar of the descriminant of the Matrix
+ */
+NR::Coord Matrix::descrim2() const
+{
+ return fabs(det());
+}
+
+
+
+
+
+/**
+ * Return the descriminant of the Matrix
+ */
+NR::Coord Matrix::descrim() const
+{
+ return sqrt(descrim2());
+}
+
+
+
+
+
+/**
+ * Assign a matrix to a given coordinate array
+ */
+Matrix &Matrix::assign(Coord const *array)
+{
+ assert(array != NULL);
+
+ Coord const *src = array;
+ Coord *dest = _c;
+
+ *dest++ = *src++; //0
+ *dest++ = *src++; //1
+ *dest++ = *src++; //2
+ *dest++ = *src++; //3
+ *dest++ = *src++; //4
+ *dest = *src ; //5
+
+ return *this;
+}
+
+
+
+
+
+/**
+ * Copy this matrix's value to a NRMatrix
+ */
+NRMatrix *Matrix::copyto(NRMatrix *nrm) const {
+
+ assert(nrm != NULL);
+
+ Coord const *src = _c;
+ Coord *dest = nrm->c;
+
+ *dest++ = *src++; //0
+ *dest++ = *src++; //1
+ *dest++ = *src++; //2
+ *dest++ = *src++; //3
+ *dest++ = *src++; //4
+ *dest = *src ; //5
+
+ return nrm;
+}
+
+
+
+
+/**
+ * Copy this matrix's values to an array
+ */
+NR::Coord *Matrix::copyto(NR::Coord *array) const {
+
+ assert(array != NULL);
+
+ Coord const *src = _c;
+ Coord *dest = array;
+
+ *dest++ = *src++; //0
+ *dest++ = *src++; //1
+ *dest++ = *src++; //2
+ *dest++ = *src++; //3
+ *dest++ = *src++; //4
+ *dest = *src ; //5
+
+ return array;
+}
+
+
+
+
+
+/**
+ *
+ */
+double expansion(Matrix const &m) {
+ return sqrt(fabs(m.det()));
+}
+
+
+
+
+
+/**
+ *
+ */
+double Matrix::expansion() const {
+ return sqrt(fabs(det()));
+}
+
+
+
+
+
+/**
+ *
+ */
+double Matrix::expansionX() const {
+ return sqrt(_c[0] * _c[0] + _c[1] * _c[1]);
+}
+
+
+
+
+
+/**
+ *
+ */
+double Matrix::expansionY() const {
+ return sqrt(_c[2] * _c[2] + _c[3] * _c[3]);
+}
+
+
+
+
+
+/**
+ *
+ */
+bool Matrix::is_translation(Coord const eps) const {
+ return ( fabs(_c[0] - 1.0) < eps &&
+ fabs(_c[3] - 1.0) < eps &&
+ fabs(_c[1]) < eps &&
+ fabs(_c[2]) < eps );
+}
+
+
+
+
+
+/**
+ *
+ */
+bool Matrix::test_identity() const {
+ return NR_MATRIX_DF_TEST_CLOSE(this, &NR_MATRIX_IDENTITY, NR_EPSILON);
+}
+
+
+
+
+
+/**
+ *
+ */
+bool transform_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon) {
+ return NR_MATRIX_DF_TEST_TRANSFORM_CLOSE(&m0, &m1, epsilon);
+}
+
+
+
+
+
+/**
+ *
+ */
+bool translate_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon) {
+ return NR_MATRIX_DF_TEST_TRANSLATE_CLOSE(&m0, &m1, epsilon);
+}
+
+
+
+
+
+/**
+ *
+ */
+bool matrix_equalp(Matrix const &m0, Matrix const &m1, NR::Coord const epsilon)
+{
+ return ( NR_MATRIX_DF_TEST_TRANSFORM_CLOSE(&m0, &m1, epsilon) &&
+ NR_MATRIX_DF_TEST_TRANSLATE_CLOSE(&m0, &m1, epsilon) );
+}
+
+
+
+
+
+/**
+ * A home-made assertion. Stop if the two matrixes are not 'close' to
+ * each other.
+ */
+void assert_close(Matrix const &a, Matrix const &b)
+{
+ if (!matrix_equalp(a, b, 1e-3)) {
+ fprintf(stderr,
+ "a = | %g %g |,\tb = | %g %g |\n"
+ " | %g %g | \t | %g %g |\n"
+ " | %g %g | \t | %g %g |\n",
+ a[0], a[1], b[0], b[1],
+ a[2], a[3], b[2], b[3],
+ a[4], a[5], b[4], b[5]);
+ abort();
+ }
+}
+
+
+
+} //namespace NR
+
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-matrix.h b/src/libnr/nr-matrix.h
new file mode 100644
index 000000000..47196137e
--- /dev/null
+++ b/src/libnr/nr-matrix.h
@@ -0,0 +1,453 @@
+#ifndef __NR_MATRIX_H__
+#define __NR_MATRIX_H__
+
+/** \file
+ * Definition of NRMatrix and NR::Matrix types.
+ *
+ * \note Operator functions (e.g. Matrix * Matrix etc.) are mostly in
+ * libnr/nr-matrix-ops.h. See end of file for discussion.
+ *
+ * Main authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>:
+ * Original NRMatrix definition and related macros.
+ *
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>:
+ * NR::Matrix class version of the above.
+ *
+ * This code is in public domain.
+ */
+
+#include <glib/gmessages.h>
+
+#include "libnr/nr-coord.h"
+#include "libnr/nr-values.h"
+#include <libnr/nr-rotate.h>
+#include <libnr/nr-scale.h>
+#include <libnr/nr-translate.h>
+
+/// NRMatrix is the obsolete form of NR::Matrix.
+/// It consists of six NR::Coord values.
+struct NRMatrix {
+ NR::Coord c[6];
+
+ NR::Coord &operator[](int i) { return c[i]; }
+ NR::Coord operator[](int i) const { return c[i]; }
+};
+
+#define nr_matrix_set_identity(m) (*(m) = NR_MATRIX_IDENTITY)
+
+#define nr_matrix_test_identity(m,e) (!(m) || NR_MATRIX_DF_TEST_CLOSE(m, &NR_MATRIX_IDENTITY, e))
+
+#define nr_matrix_test_equal(m0,m1,e) ((!(m0) && !(m1)) || ((m0) && (m1) && NR_MATRIX_DF_TEST_CLOSE(m0, m1, e)))
+#define nr_matrix_test_transform_equal(m0,m1,e) ((!(m0) && !(m1)) || ((m0) && (m1) && NR_MATRIX_DF_TEST_TRANSFORM_CLOSE(m0, m1, e)))
+#define nr_matrix_test_translate_equal(m0,m1,e) ((!(m0) && !(m1)) || ((m0) && (m1) && NR_MATRIX_DF_TEST_TRANSLATE_CLOSE(m0, m1, e)))
+
+NRMatrix *nr_matrix_invert(NRMatrix *d, NRMatrix const *m);
+
+/* d,m0,m1 needn't be distinct in any of these multiply routines. */
+
+NRMatrix *nr_matrix_multiply(NRMatrix *d, NRMatrix const *m0, NRMatrix const *m1);
+
+NRMatrix *nr_matrix_set_translate(NRMatrix *m, NR::Coord const x, NR::Coord const y);
+
+NRMatrix *nr_matrix_set_scale(NRMatrix *m, NR::Coord const sx, NR::Coord const sy);
+
+NRMatrix *nr_matrix_set_rotate(NRMatrix *m, NR::Coord const theta);
+
+#define NR_MATRIX_DF_TRANSFORM_X(m,x,y) ((*(m))[0] * (x) + (*(m))[2] * (y) + (*(m))[4])
+#define NR_MATRIX_DF_TRANSFORM_Y(m,x,y) ((*(m))[1] * (x) + (*(m))[3] * (y) + (*(m))[5])
+
+#define NR_MATRIX_DF_EXPANSION2(m) (fabs((*(m))[0] * (*(m))[3] - (*(m))[1] * (*(m))[2]))
+#define NR_MATRIX_DF_EXPANSION(m) (sqrt(NR_MATRIX_DF_EXPANSION2(m)))
+
+namespace NR {
+
+/**
+ * The Matrix class.
+ *
+ * For purposes of multiplication, points should be thought of as row vectors
+ *
+ * p = ( p[X] p[Y] 1 )
+ *
+ * to be right-multiplied by transformation matrices
+ * \verbatim
+ c[] = | c[0] c[1] 0 |
+ | c[2] c[3] 0 |
+ | c[4] c[5] 1 | \endverbatim
+ *
+ * (so the columns of the matrix correspond to the columns (elements) of the result,
+ * and the rows of the matrix correspond to columns (elements) of the "input").
+ */
+class Matrix {
+
+
+ public:
+
+ /**
+ * Various forms of constructor
+ */
+
+ /**
+ *
+ */
+ explicit Matrix() { }
+
+
+ /**
+ *
+ */
+ Matrix(Matrix const &m) {
+
+ NR::Coord const *src = m._c;
+ NR::Coord *dest = _c;
+
+ *dest++ = *src++; //0
+ *dest++ = *src++; //1
+ *dest++ = *src++; //2
+ *dest++ = *src++; //3
+ *dest++ = *src++; //4
+ *dest = *src ; //5
+
+ }
+
+
+
+
+ /**
+ *
+ */
+ Matrix(NRMatrix const &m) {
+
+ NR::Coord const *src = m.c;
+ NR::Coord *dest = _c;
+
+ *dest++ = *src++; //0
+ *dest++ = *src++; //1
+ *dest++ = *src++; //2
+ *dest++ = *src++; //3
+ *dest++ = *src++; //4
+ *dest = *src ; //5
+
+ }
+
+
+
+
+ /**
+ *
+ */
+ Matrix(double c0, double c1,
+ double c2, double c3,
+ double c4, double c5) {
+
+ NR::Coord *dest = _c;
+
+ *dest++ = c0; //0
+ *dest++ = c1; //1
+ *dest++ = c2; //2
+ *dest++ = c3; //3
+ *dest++ = c4; //4
+ *dest = c5; //5
+
+ }
+
+
+
+ /**
+ *
+ */
+ Matrix &operator=(Matrix const &m) {
+
+ NR::Coord const *src = m._c;
+ NR::Coord *dest = _c;
+
+ *dest++ = *src++; //0
+ *dest++ = *src++; //1
+ *dest++ = *src++; //2
+ *dest++ = *src++; //3
+ *dest++ = *src++; //4
+ *dest = *src ; //5
+
+ return *this;
+ }
+
+
+
+
+ /**
+ *
+ */
+ explicit Matrix(scale const &sm) {
+
+ NR::Coord *dest = _c;
+
+ *dest++ = sm[X]; //0
+ *dest++ = 0.0; //1
+ *dest++ = 0.0; //2
+ *dest++ = sm[Y]; //3
+ *dest++ = 0.0; //4
+ *dest = 0.0; //5
+
+ }
+
+
+
+
+
+
+ /**
+ *
+ */
+ explicit Matrix(rotate const &r) {
+
+ NR::Coord *dest = _c;
+
+ *dest++ = r.vec[X]; //0
+ *dest++ = r.vec[Y]; //1
+ *dest++ = -r.vec[Y]; //2
+ *dest++ = r.vec[X]; //3
+ *dest++ = 0.0; //4
+ *dest = 0.0; //5
+
+ }
+
+
+
+
+ /**
+ *
+ */
+ explicit Matrix(translate const &tm) {
+
+ NR::Coord *dest = _c;
+
+ *dest++ = 1.0; //0
+ *dest++ = 0.0; //1
+ *dest++ = 0.0; //2
+ *dest++ = 1.0; //3
+ *dest++ = tm[X]; //4
+ *dest = tm[Y]; //5
+ }
+
+
+
+ /**
+ *
+ */
+ Matrix(NRMatrix const *nr);
+
+
+ /**
+ *
+ */
+ bool test_identity() const;
+
+
+ /**
+ *
+ */
+ bool is_translation(Coord const eps = 1e-6) const;
+
+
+ /**
+ *
+ */
+ Matrix inverse() const;
+
+
+ /**
+ *
+ */
+ Matrix &operator*=(Matrix const &other);
+
+
+ /**
+ *
+ */
+ Matrix &operator*=(scale const &other);
+
+
+
+ /**
+ *
+ */
+ Matrix &operator*=(translate const &other) {
+ _c[4] += other[X];
+ _c[5] += other[Y];
+ return *this;
+ }
+
+
+
+ /**
+ *
+ */
+ inline Coord &operator[](int const i) {
+ return _c[i];
+ }
+
+
+
+ /**
+ *
+ */
+ inline Coord operator[](int const i) const {
+ return _c[i];
+ }
+
+
+ /**
+ *
+ */
+ void set_identity();
+
+ /**
+ *
+ */
+ Coord det() const;
+
+
+ /**
+ *
+ */
+ Coord descrim2() const;
+
+
+ /**
+ *
+ */
+ Coord descrim() const;
+
+
+ /**
+ *
+ */
+ double expansion() const;
+
+
+ /**
+ *
+ */
+ double expansionX() const;
+
+
+ /**
+ *
+ */
+ double expansionY() const;
+
+ // legacy
+
+
+ /**
+ *
+ */
+ Matrix &assign(Coord const *array);
+
+
+ /**
+ *
+ */
+ NRMatrix *copyto(NRMatrix* nrm) const;
+
+
+ /**
+ *
+ */
+ Coord *copyto(Coord *array) const;
+
+
+
+ /**
+ *
+ */
+ operator NRMatrix&() {
+ g_assert(sizeof(_c) == sizeof(NRMatrix));
+ return *reinterpret_cast<NRMatrix *>(_c);
+ }
+
+
+
+ /**
+ *
+ */
+ operator NRMatrix const&() const {
+ g_assert(sizeof(_c) == sizeof(NRMatrix));
+ return *reinterpret_cast<const NRMatrix *>(_c);
+ }
+
+
+
+ /**
+ *
+ */
+ operator NRMatrix*() {
+ g_assert(sizeof(_c) == sizeof(NRMatrix));
+ return reinterpret_cast<NRMatrix *>(_c);
+ }
+
+
+ /**
+ *
+ */
+ operator NRMatrix const*() const {
+ g_assert(sizeof(_c) == sizeof(NRMatrix));
+ return reinterpret_cast<NRMatrix const *>(_c);
+ }
+
+
+ private:
+
+
+ NR::Coord _c[6];
+};
+
+/** A function to print out the Matrix (for debugging) */
+inline std::ostream &operator<< (std::ostream &out_file, const NR::Matrix &m) {
+ out_file << "A: " << m[0] << " C: " << m[2] << " E: " << m[4] << "\n";
+ out_file << "B: " << m[1] << " D: " << m[3] << " F: " << m[5] << "\n";
+ return out_file;
+}
+
+extern void assert_close(Matrix const &a, Matrix const &b);
+
+} /* namespace NR */
+
+
+
+
+
+
+
+/** \note
+ * Discussion of splitting up nr-matrix.h into lots of little files:
+ *
+ * Advantages:
+ *
+ * - Reducing amount of recompilation necessary when anything changes.
+ *
+ * - Hopefully also reducing compilation time by reducing the number of inline
+ * function definitions encountered by the compiler for a given .o file.
+ * (No timing comparisons done yet. On systems without much memory available
+ * for caching, this may be outweighed by additional I/O costs.)
+ *
+ * Disadvantages:
+ *
+ * - More #include lines necessary per file. If a compile fails due to
+ * not having all the necessary #include lines, then the developer needs
+ * to spend some time working out what #include to add.
+ */
+
+#endif /* !__NR_MATRIX_H__ */
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-maybe.h b/src/libnr/nr-maybe.h
new file mode 100644
index 000000000..6eed01ec1
--- /dev/null
+++ b/src/libnr/nr-maybe.h
@@ -0,0 +1,140 @@
+#ifndef __NR_MAYBE_H__
+#define __NR_MAYBE_H__
+
+/*
+ * Functionalesque "Maybe" class
+ *
+ * Copyright 2004 MenTaLguY
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * This code is licensed under the GNU GPL; see COPYING for more information.
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdexcept>
+#include <typeinfo>
+#include <string>
+
+namespace NR {
+
+/** An exception class for run-time type errors */
+template <typename T>
+class IsNot : public std::domain_error {
+public:
+ IsNot() : domain_error(std::string("Is not ") + typeid(T).name()) {}
+};
+
+/** A type with only one value, which (in principle) is only equal to itself.
+ *
+ * Types that may (at runtime) pretend to be Nothing need only provide an
+ * operator bool operator==(Type, Nothing); the rest of the operator
+ * definitions will be taken care of automatically.
+ *
+ * Such types should also provide a casting operator to Nothing, obviously.
+ */
+struct Nothing {
+ bool operator==(Nothing n) { return true; }
+ bool operator!=(Nothing n) { return false; }
+ template <typename T>
+ bool operator==(T t) { return t == *this; }
+ template <typename T>
+ bool operator!=(T t) { return t != *this; }
+};
+
+template <typename T>
+bool operator==(T t, Nothing n) { return false; }
+template <typename T>
+bool operator!=(T t, Nothing n) { return !( t == n ); }
+
+template <typename T>
+struct MaybeTraits;
+
+template <typename T>
+class Maybe {
+public:
+ typedef MaybeTraits<T> traits;
+ typedef typename traits::storage storage;
+ typedef typename traits::reference reference;
+
+ Maybe(Nothing n) : _is_nothing(true), _t() {}
+
+ Maybe(const Maybe<T> &m) : _is_nothing(m._is_nothing), _t(m._t) {}
+
+ template <typename T2>
+ Maybe(const Maybe<T2> &m)
+ : _is_nothing(m._is_nothing),
+ _t(traits::to_storage(MaybeTraits<T2>::from_storage(m._t))) {}
+
+ template <typename T2>
+ Maybe(T2 t) : _is_nothing(false), _t(traits::to_storage(t)) {}
+
+ reference assume() const throw(IsNot<T>) {
+ if (_is_nothing) {
+ throw IsNot<T>();
+ } else {
+ return traits::from_storage(_t);
+ }
+ }
+
+ operator reference() const throw(IsNot<T>) {
+ if (_is_nothing) {
+ throw IsNot<T>();
+ } else {
+ return traits::from_storage(_t);
+ }
+ }
+ operator Nothing() const throw(IsNot<Nothing>) {
+ if (!_is_nothing) {
+ throw IsNot<Nothing>();
+ } else {
+ return Nothing();
+ }
+ }
+
+ bool operator==(Nothing n) { return _is_nothing; }
+ bool operator==(reference r) {
+ return traits::from_storage(_t) == r;
+ }
+
+private:
+ bool _is_nothing;
+ storage _t;
+};
+
+/* traits classes used by Maybe */
+
+template <typename T>
+struct MaybeTraits {
+ typedef T const storage;
+ typedef T const &reference;
+ static reference to_storage(reference t) { return t; }
+ static reference from_storage(reference t) { return t; }
+};
+
+template <typename T>
+struct MaybeTraits<T&> {
+ typedef T *storage;
+ typedef T &reference;
+ static storage to_storage(reference t) { return &t; }
+ static reference from_storage(storage t) { return *t; }
+};
+
+} /* namespace NR */
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-object.cpp b/src/libnr/nr-object.cpp
new file mode 100644
index 000000000..b18685d11
--- /dev/null
+++ b/src/libnr/nr-object.cpp
@@ -0,0 +1,295 @@
+#define __NR_OBJECT_C__
+
+/*
+ * RGBA display list system for inkscape
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * MenTaLguY <mental@rydia.net>
+ *
+ * This code is in public domain
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <libnr/nr-macros.h>
+
+#include "nr-object.h"
+
+unsigned int nr_emit_fail_warning(const gchar *file, unsigned int line, const gchar *method, const gchar *expr)
+{
+ fprintf (stderr, "File %s line %d (%s): Assertion %s failed\n", file, line, method, expr);
+ return 1;
+}
+
+/* NRObject */
+
+static NRObjectClass **classes = NULL;
+static unsigned int classes_len = 0;
+static unsigned int classes_size = 0;
+
+NRType nr_type_is_a(NRType type, NRType test)
+{
+ nr_return_val_if_fail(type < classes_len, FALSE);
+ nr_return_val_if_fail(test < classes_len, FALSE);
+
+ NRObjectClass *c = classes[type];
+
+ while (c) {
+ if (c->type == test) {
+ return TRUE;
+ }
+ c = c->parent;
+ }
+
+ return FALSE;
+}
+
+void const *nr_object_check_instance_cast(void const *ip, NRType tc)
+{
+ nr_return_val_if_fail(ip != NULL, NULL);
+ nr_return_val_if_fail(nr_type_is_a(((NRObject const *) ip)->klass->type, tc), ip);
+ return ip;
+}
+
+unsigned int nr_object_check_instance_type(void const *ip, NRType tc)
+{
+ if (ip == NULL) {
+ return FALSE;
+ }
+
+ return nr_type_is_a(((NRObject const *) ip)->klass->type, tc);
+}
+
+NRType nr_object_register_type(NRType parent,
+ gchar const *name,
+ unsigned int csize,
+ unsigned int isize,
+ void (* cinit) (NRObjectClass *),
+ void (* iinit) (NRObject *))
+{
+ if (classes_len >= classes_size) {
+ classes_size += 32;
+ classes = nr_renew (classes, NRObjectClass *, classes_size);
+ if (classes_len == 0) {
+ classes[0] = NULL;
+ classes_len = 1;
+ }
+ }
+
+ NRType const type = classes_len;
+ classes_len += 1;
+
+ classes[type] = (NRObjectClass*) new char[csize];
+ NRObjectClass *c = classes[type];
+
+ /* FIXME: is this necessary? */
+ memset(c, 0, csize);
+
+ if (classes[parent]) {
+ memcpy(c, classes[parent], classes[parent]->csize);
+ }
+
+ c->type = type;
+ c->parent = classes[parent];
+ c->name = strdup(name);
+ c->csize = csize;
+ c->isize = isize;
+ c->cinit = cinit;
+ c->iinit = iinit;
+
+ c->cinit(c);
+
+ return type;
+}
+
+static void nr_object_class_init (NRObjectClass *klass);
+static void nr_object_init (NRObject *object);
+static void nr_object_finalize (NRObject *object);
+
+NRType nr_object_get_type()
+{
+ static NRType type = 0;
+
+ if (!type) {
+ type = nr_object_register_type (0,
+ "NRObject",
+ sizeof (NRObjectClass),
+ sizeof (NRObject),
+ (void (*) (NRObjectClass *)) nr_object_class_init,
+ (void (*) (NRObject *)) nr_object_init);
+ }
+
+ return type;
+}
+
+static void nr_object_class_init(NRObjectClass *c)
+{
+ c->finalize = nr_object_finalize;
+ c->cpp_ctor = NRObject::invoke_ctor<NRObject>;
+}
+
+static void nr_object_init (NRObject *object)
+{
+}
+
+static void nr_object_finalize (NRObject *object)
+{
+}
+
+/* Dynamic lifecycle */
+
+static void nr_class_tree_object_invoke_init(NRObjectClass *c, NRObject *object)
+{
+ if (c->parent) {
+ nr_class_tree_object_invoke_init(c->parent, object);
+ }
+ c->iinit (object);
+}
+
+namespace {
+
+void finalize_object(void *base, void *)
+{
+ NRObject *object = reinterpret_cast<NRObject *>(base);
+ object->klass->finalize(object);
+ object->~NRObject();
+}
+
+}
+
+NRObject *NRObject::alloc(NRType type)
+{
+ nr_return_val_if_fail (type < classes_len, NULL);
+
+ NRObjectClass *c = classes[type];
+
+ if ( c->parent && c->cpp_ctor == c->parent->cpp_ctor ) {
+ g_error("Cannot instantiate NRObject class %s which has not registered a C++ constructor\n", c->name);
+ }
+
+ NRObject *object = reinterpret_cast<NRObject *>(
+ ::operator new(c->isize, Inkscape::GC::SCANNED, Inkscape::GC::AUTO,
+ &finalize_object, NULL)
+ );
+ memset(object, 0xf0, c->isize);
+
+ object->klass = c;
+ c->cpp_ctor(object);
+ nr_class_tree_object_invoke_init (c, object);
+
+ return object;
+}
+
+/* NRActiveObject */
+
+static void nr_active_object_class_init(NRActiveObjectClass *c);
+static void nr_active_object_init(NRActiveObject *object);
+static void nr_active_object_finalize(NRObject *object);
+
+static NRObjectClass *parent_class;
+
+NRType nr_active_object_get_type()
+{
+ static NRType type = 0;
+ if (!type) {
+ type = nr_object_register_type (NR_TYPE_OBJECT,
+ "NRActiveObject",
+ sizeof (NRActiveObjectClass),
+ sizeof (NRActiveObject),
+ (void (*) (NRObjectClass *)) nr_active_object_class_init,
+ (void (*) (NRObject *)) nr_active_object_init);
+ }
+ return type;
+}
+
+static void nr_active_object_class_init(NRActiveObjectClass *c)
+{
+ NRObjectClass *object_class = (NRObjectClass *) c;
+
+ parent_class = object_class->parent;
+
+ object_class->finalize = nr_active_object_finalize;
+ object_class->cpp_ctor = NRObject::invoke_ctor<NRActiveObject>;
+}
+
+static void nr_active_object_init(NRActiveObject *object)
+{
+}
+
+static void nr_active_object_finalize(NRObject *object)
+{
+ NRActiveObject *aobject = (NRActiveObject *) object;
+
+ if (aobject->callbacks) {
+ for (unsigned int i = 0; i < aobject->callbacks->length; i++) {
+ NRObjectListener *listener = aobject->callbacks->listeners + i;
+ if ( listener->vector->dispose ) {
+ listener->vector->dispose(object, listener->data);
+ }
+ }
+ free (aobject->callbacks);
+ }
+
+ ((NRObjectClass *) (parent_class))->finalize(object);
+}
+
+void nr_active_object_add_listener(NRActiveObject *object,
+ const NRObjectEventVector *vector,
+ unsigned int size,
+ void *data)
+{
+ if (!object->callbacks) {
+ object->callbacks = (NRObjectCallbackBlock*) malloc(sizeof(NRObjectCallbackBlock));
+ object->callbacks->size = 1;
+ object->callbacks->length = 0;
+ }
+
+ if (object->callbacks->length >= object->callbacks->size) {
+ int newsize = object->callbacks->size << 1;
+ object->callbacks = (NRObjectCallbackBlock *)
+ realloc(object->callbacks, sizeof(NRObjectCallbackBlock) + (newsize - 1) * sizeof (NRObjectListener));
+ object->callbacks->size = newsize;
+ }
+
+ NRObjectListener *listener = object->callbacks->listeners + object->callbacks->length;
+ listener->vector = vector;
+ listener->size = size;
+ listener->data = data;
+ object->callbacks->length += 1;
+}
+
+void nr_active_object_remove_listener_by_data(NRActiveObject *object, void *data)
+{
+ if (object->callbacks == NULL) {
+ return;
+ }
+
+ for (unsigned i = 0; i < object->callbacks->length; i++) {
+ NRObjectListener *listener = object->callbacks->listeners + i;
+ if ( listener->data == data ) {
+ object->callbacks->length -= 1;
+ if ( object->callbacks->length < 1 ) {
+ free(object->callbacks);
+ object->callbacks = NULL;
+ } else if ( object->callbacks->length != i ) {
+ *listener = object->callbacks->listeners[object->callbacks->length];
+ }
+ return;
+ }
+ }
+}
+
+
+
+/*
+ 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/libnr/nr-object.h b/src/libnr/nr-object.h
new file mode 100644
index 000000000..f39814b0d
--- /dev/null
+++ b/src/libnr/nr-object.h
@@ -0,0 +1,157 @@
+#ifndef __NR_OBJECT_H__
+#define __NR_OBJECT_H__
+
+/*
+ * RGBA display list system for inkscape
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * MenTaLguY <mental@rydia.net>
+ *
+ * This code is in public domain
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gtypes.h>
+#include "gc-managed.h"
+#include "gc-finalized.h"
+#include "gc-anchored.h"
+
+typedef guint32 NRType;
+
+struct NRObject;
+struct NRObjectClass;
+
+#define NR_TYPE_OBJECT (nr_object_get_type ())
+#define NR_OBJECT(o) (NR_CHECK_INSTANCE_CAST ((o), NR_TYPE_OBJECT, NRObject))
+#define NR_IS_OBJECT(o) (NR_CHECK_INSTANCE_TYPE ((o), NR_TYPE_OBJECT))
+
+#define NR_TYPE_ACTIVE_OBJECT (nr_active_object_get_type ())
+#define NR_ACTIVE_OBJECT(o) (NR_CHECK_INSTANCE_CAST ((o), NR_TYPE_ACTIVE_OBJECT, NRActiveObject))
+#define NR_IS_ACTIVE_OBJECT(o) (NR_CHECK_INSTANCE_TYPE ((o), NR_TYPE_ACTIVE_OBJECT))
+
+#define nr_return_if_fail(expr) if (!(expr) && nr_emit_fail_warning (__FILE__, __LINE__, "?", #expr)) return
+#define nr_return_val_if_fail(expr,val) if (!(expr) && nr_emit_fail_warning (__FILE__, __LINE__, "?", #expr)) return (val)
+
+unsigned int nr_emit_fail_warning (const gchar *file, unsigned int line, const gchar *method, const gchar *expr);
+
+#ifndef NR_DISABLE_CAST_CHECKS
+#define NR_CHECK_INSTANCE_CAST(ip, tc, ct) ((ct *) nr_object_check_instance_cast (ip, tc))
+#else
+#define NR_CHECK_INSTANCE_CAST(ip, tc, ct) ((ct *) ip)
+#endif
+
+#define NR_CHECK_INSTANCE_TYPE(ip, tc) nr_object_check_instance_type (ip, tc)
+#define NR_OBJECT_GET_CLASS(ip) (((NRObject *) ip)->klass)
+
+NRType nr_type_is_a (NRType type, NRType test);
+
+void const *nr_object_check_instance_cast(void const *ip, NRType tc);
+unsigned int nr_object_check_instance_type(void const *ip, NRType tc);
+
+NRType nr_object_register_type (NRType parent,
+ gchar const *name,
+ unsigned int csize,
+ unsigned int isize,
+ void (* cinit) (NRObjectClass *),
+ void (* iinit) (NRObject *));
+
+/* NRObject */
+
+class NRObject : public Inkscape::GC::Managed<>,
+ public Inkscape::GC::Finalized,
+ public Inkscape::GC::Anchored
+{
+public:
+ NRObjectClass *klass;
+
+ static NRObject *alloc(NRType type);
+
+ template <typename T>
+ static void invoke_ctor(NRObject *object) {
+ new (object) T();
+ }
+
+ /* these can go away eventually */
+ NRObject *reference() {
+ return Inkscape::GC::anchor(this);
+ }
+ NRObject *unreference() {
+ Inkscape::GC::release(this);
+ return NULL;
+ }
+
+protected:
+ NRObject() {}
+
+private:
+ NRObject(NRObject const &); // no copy
+ void operator=(NRObject const &); // no assign
+
+ void *operator new(size_t size, void *placement) { return placement; }
+};
+
+struct NRObjectClass {
+ NRType type;
+ NRObjectClass *parent;
+
+ gchar *name;
+ unsigned int csize;
+ unsigned int isize;
+ void (* cinit) (NRObjectClass *);
+ void (* iinit) (NRObject *);
+ void (* finalize) (NRObject *object);
+ void (*cpp_ctor)(NRObject *object);
+};
+
+NRType nr_object_get_type (void);
+
+/* Dynamic lifecycle */
+
+inline NRObject *nr_object_new (NRType type) {
+ return NRObject::alloc(type);
+}
+
+inline NRObject *nr_object_ref (NRObject *object) {
+ return object->reference();
+}
+inline NRObject *nr_object_unref (NRObject *object) {
+ return object->unreference();
+}
+
+/* NRActiveObject */
+
+struct NRObjectEventVector {
+ void (* dispose) (NRObject *object, void *data);
+};
+
+struct NRObjectListener {
+ const NRObjectEventVector *vector;
+ unsigned int size;
+ void *data;
+};
+
+struct NRObjectCallbackBlock {
+ unsigned int size;
+ unsigned int length;
+ NRObjectListener listeners[1];
+};
+
+struct NRActiveObject : public NRObject {
+ NRActiveObject() : callbacks(NULL) {}
+ NRObjectCallbackBlock *callbacks;
+};
+
+struct NRActiveObjectClass : public NRObjectClass {
+};
+
+NRType nr_active_object_get_type (void);
+
+void nr_active_object_add_listener (NRActiveObject *object, const NRObjectEventVector *vector, unsigned int size, void *data);
+void nr_active_object_remove_listener_by_data (NRActiveObject *object, void *data);
+
+#endif
+
diff --git a/src/libnr/nr-path-code.h b/src/libnr/nr-path-code.h
new file mode 100644
index 000000000..cc174d73b
--- /dev/null
+++ b/src/libnr/nr-path-code.h
@@ -0,0 +1,28 @@
+#ifndef SEEN_LIBNR_NR_PATH_CODE_H
+#define SEEN_LIBNR_NR_PATH_CODE_H
+
+/** \file
+ * NRPathcode enum definition
+ */
+
+typedef enum {
+ NR_MOVETO, ///< Start of closed subpath
+ NR_MOVETO_OPEN, ///< Start of open subpath
+ NR_CURVETO, ///< Bezier curve segment
+ NR_LINETO, ///< Line segment
+ NR_END ///< End record
+} NRPathcode;
+
+
+#endif /* !SEEN_LIBNR_NR_PATH_CODE_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 :
diff --git a/src/libnr/nr-path.cpp b/src/libnr/nr-path.cpp
new file mode 100644
index 000000000..6ce2f5f99
--- /dev/null
+++ b/src/libnr/nr-path.cpp
@@ -0,0 +1,461 @@
+#define __NR_PATH_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include "n-art-bpath.h"
+#include "nr-rect.h"
+#include "nr-path.h"
+
+static void nr_curve_bbox (NR::Coord x000, NR::Coord y000, NR::Coord x001, NR::Coord y001, NR::Coord x011, NR::Coord y011, NR::Coord x111, NR::Coord y111, NRRect *bbox);
+
+static void nr_curve_bbox(NR::Point const p000, NR::Point const p001,
+ NR::Point const p011, NR::Point const p111,
+ NRRect *bbox);
+
+NRBPath *nr_path_duplicate_transform(NRBPath *d, NRBPath *s, NRMatrix const *transform)
+{
+ int i;
+
+ if (!s->path) {
+ d->path = NULL;
+ return d;
+ }
+
+ i = 0;
+ while (s->path[i].code != NR_END) i += 1;
+
+ d->path = nr_new (NArtBpath, i + 1);
+
+ i = 0;
+ while (s->path[i].code != NR_END) {
+ d->path[i].code = s->path[i].code;
+ if (s->path[i].code == NR_CURVETO) {
+ d->path[i].x1 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x1, s->path[i].y1);
+ d->path[i].y1 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x1, s->path[i].y1);
+ d->path[i].x2 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x2, s->path[i].y2);
+ d->path[i].y2 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x2, s->path[i].y2);
+ }
+ d->path[i].x3 = NR_MATRIX_DF_TRANSFORM_X (transform, s->path[i].x3, s->path[i].y3);
+ d->path[i].y3 = NR_MATRIX_DF_TRANSFORM_Y (transform, s->path[i].x3, s->path[i].y3);
+ i += 1;
+ }
+ d->path[i].code = NR_END;
+
+ return d;
+}
+
+NRBPath *nr_path_duplicate_transform(NRBPath *d, NRBPath *s, NR::Matrix const transform) {
+ NRMatrix tr = transform;
+ return nr_path_duplicate_transform(d, s, &tr);
+}
+
+NArtBpath* nr_artpath_affine(NArtBpath *s, NR::Matrix const &aff) {
+ NRBPath bp, abp;
+ bp.path = s;
+ nr_path_duplicate_transform(&abp, &bp, aff);
+ return abp.path;
+}
+
+static void
+nr_line_wind_distance (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, NR::Point &pt, int *wind, NR::Coord *best)
+{
+ NR::Coord Ax, Ay, Bx, By, Dx, Dy, s;
+ NR::Coord dist2;
+
+ /* Find distance */
+ Ax = x0;
+ Ay = y0;
+ Bx = x1;
+ By = y1;
+ Dx = x1 - x0;
+ Dy = y1 - y0;
+ const NR::Coord Px = pt[NR::X];
+ const NR::Coord Py = pt[NR::Y];
+
+ if (best) {
+ s = ((Px - Ax) * Dx + (Py - Ay) * Dy) / (Dx * Dx + Dy * Dy);
+ if (s <= 0.0) {
+ dist2 = (Px - Ax) * (Px - Ax) + (Py - Ay) * (Py - Ay);
+ } else if (s >= 1.0) {
+ dist2 = (Px - Bx) * (Px - Bx) + (Py - By) * (Py - By);
+ } else {
+ NR::Coord Qx, Qy;
+ Qx = Ax + s * Dx;
+ Qy = Ay + s * Dy;
+ dist2 = (Px - Qx) * (Px - Qx) + (Py - Qy) * (Py - Qy);
+ }
+
+ if (dist2 < (*best * *best)) *best = sqrt (dist2);
+ }
+
+ if (wind) {
+ /* Find wind */
+ if ((Ax >= Px) && (Bx >= Px)) return;
+ if ((Ay >= Py) && (By >= Py)) return;
+ if ((Ay < Py) && (By < Py)) return;
+ if (Ay == By) return;
+ /* Ctach upper y bound */
+ if (Ay == Py) {
+ if (Ax < Px) *wind -= 1;
+ return;
+ } else if (By == Py) {
+ if (Bx < Px) *wind += 1;
+ return;
+ } else {
+ NR::Coord Qx;
+ /* Have to calculate intersection */
+ Qx = Ax + Dx * (Py - Ay) / Dy;
+ if (Qx < Px) {
+ *wind += (Dy > 0.0) ? 1 : -1;
+ }
+ }
+ }
+}
+
+static void
+nr_curve_bbox_wind_distance (NR::Coord x000, NR::Coord y000,
+ NR::Coord x001, NR::Coord y001,
+ NR::Coord x011, NR::Coord y011,
+ NR::Coord x111, NR::Coord y111,
+ NR::Point &pt,
+ NRRect *bbox, int *wind, NR::Coord *best,
+ NR::Coord tolerance)
+{
+ NR::Coord x0, y0, x1, y1, len2;
+ int needdist, needwind, needline;
+
+ const NR::Coord Px = pt[NR::X];
+ const NR::Coord Py = pt[NR::Y];
+
+ needdist = 0;
+ needwind = 0;
+ needline = 0;
+
+ if (bbox) nr_curve_bbox (x000, y000, x001, y001, x011, y011, x111, y111, bbox);
+
+ x0 = MIN (x000, x001);
+ x0 = MIN (x0, x011);
+ x0 = MIN (x0, x111);
+ y0 = MIN (y000, y001);
+ y0 = MIN (y0, y011);
+ y0 = MIN (y0, y111);
+ x1 = MAX (x000, x001);
+ x1 = MAX (x1, x011);
+ x1 = MAX (x1, x111);
+ y1 = MAX (y000, y001);
+ y1 = MAX (y1, y011);
+ y1 = MAX (y1, y111);
+
+ if (best) {
+ /* Quicly adjust to endpoints */
+ len2 = (x000 - Px) * (x000 - Px) + (y000 - Py) * (y000 - Py);
+ if (len2 < (*best * *best)) *best = (NR::Coord) sqrt (len2);
+ len2 = (x111 - Px) * (x111 - Px) + (y111 - Py) * (y111 - Py);
+ if (len2 < (*best * *best)) *best = (NR::Coord) sqrt (len2);
+
+ if (((x0 - Px) < *best) && ((y0 - Py) < *best) && ((Px - x1) < *best) && ((Py - y1) < *best)) {
+ /* Point is inside sloppy bbox */
+ /* Now we have to decide, whether subdivide */
+ /* fixme: (Lauris) */
+ if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {
+ needdist = 1;
+ } else {
+ needline = 1;
+ }
+ }
+ }
+ if (!needdist && wind) {
+ if ((y1 >= Py) && (y0 < Py) && (x0 < Px)) {
+ /* Possible intersection at the left */
+ /* Now we have to decide, whether subdivide */
+ /* fixme: (Lauris) */
+ if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {
+ needwind = 1;
+ } else {
+ needline = 1;
+ }
+ }
+ }
+
+ if (needdist || needwind) {
+ NR::Coord x00t, x0tt, xttt, x1tt, x11t, x01t;
+ NR::Coord y00t, y0tt, yttt, y1tt, y11t, y01t;
+ NR::Coord s, t;
+
+ t = 0.5;
+ s = 1 - t;
+
+ x00t = s * x000 + t * x001;
+ x01t = s * x001 + t * x011;
+ x11t = s * x011 + t * x111;
+ x0tt = s * x00t + t * x01t;
+ x1tt = s * x01t + t * x11t;
+ xttt = s * x0tt + t * x1tt;
+
+ y00t = s * y000 + t * y001;
+ y01t = s * y001 + t * y011;
+ y11t = s * y011 + t * y111;
+ y0tt = s * y00t + t * y01t;
+ y1tt = s * y01t + t * y11t;
+ yttt = s * y0tt + t * y1tt;
+
+ nr_curve_bbox_wind_distance (x000, y000, x00t, y00t, x0tt, y0tt, xttt, yttt, pt, NULL, wind, best, tolerance);
+ nr_curve_bbox_wind_distance (xttt, yttt, x1tt, y1tt, x11t, y11t, x111, y111, pt, NULL, wind, best, tolerance);
+ } else if (1 || needline) {
+ nr_line_wind_distance (x000, y000, x111, y111, pt, wind, best);
+ }
+}
+
+void
+nr_path_matrix_point_bbox_wind_distance (NRBPath *bpath, NR::Matrix const &m, NR::Point &pt,
+ NRRect *bbox, int *wind, NR::Coord *dist,
+ NR::Coord tolerance)
+{
+ NR::Coord x0, y0, x3, y3;
+ const NArtBpath *p;
+
+ if (!bpath->path) {
+ if (wind) *wind = 0;
+ if (dist) *dist = NR_HUGE;
+ return;
+ }
+
+ x0 = y0 = 0.0;
+ x3 = y3 = 0.0;
+
+ for (p = bpath->path; p->code != NR_END; p+= 1) {
+ switch (p->code) {
+ case NR_MOVETO_OPEN:
+ case NR_MOVETO:
+ x0 = m[0] * p->x3 + m[2] * p->y3 + m[4];
+ y0 = m[1] * p->x3 + m[3] * p->y3 + m[5];
+ if (bbox) {
+ bbox->x0 = (NR::Coord) MIN (bbox->x0, x0);
+ bbox->y0 = (NR::Coord) MIN (bbox->y0, y0);
+ bbox->x1 = (NR::Coord) MAX (bbox->x1, x0);
+ bbox->y1 = (NR::Coord) MAX (bbox->y1, y0);
+ }
+ break;
+ case NR_LINETO:
+ x3 = m[0] * p->x3 + m[2] * p->y3 + m[4];
+ y3 = m[1] * p->x3 + m[3] * p->y3 + m[5];
+ if (bbox) {
+ bbox->x0 = (NR::Coord) MIN (bbox->x0, x3);
+ bbox->y0 = (NR::Coord) MIN (bbox->y0, y3);
+ bbox->x1 = (NR::Coord) MAX (bbox->x1, x3);
+ bbox->y1 = (NR::Coord) MAX (bbox->y1, y3);
+ }
+ if (dist || wind) {
+ nr_line_wind_distance (x0, y0, x3, y3, pt, wind, dist);
+ }
+ x0 = x3;
+ y0 = y3;
+ break;
+ case NR_CURVETO:
+ x3 = m[0] * p->x3 + m[2] * p->y3 + m[4];
+ y3 = m[1] * p->x3 + m[3] * p->y3 + m[5];
+ nr_curve_bbox_wind_distance (x0, y0,
+ m[0] * p->x1 + m[2] * p->y1 + m[4],
+ m[1] * p->x1 + m[3] * p->y1 + m[5],
+ m[0] * p->x2 + m[2] * p->y2 + m[4],
+ m[1] * p->x2 + m[3] * p->y2 + m[5],
+ x3, y3,
+ pt,
+ bbox, wind, dist, tolerance);
+ x0 = x3;
+ y0 = y3;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+nr_curve_bbox(NR::Point const p000, NR::Point const p001,
+ NR::Point const p011, NR::Point const p111,
+ NRRect *bbox)
+{
+ using NR::X;
+ using NR::Y;
+
+ nr_curve_bbox(p000[X], p000[Y],
+ p001[X], p001[Y],
+ p011[X], p011[Y],
+ p111[X], p111[Y],
+ bbox);
+}
+
+/* Fast bbox calculation */
+/* Thanks to Nathan Hurst for suggesting it */
+
+static void
+nr_curve_bbox (NR::Coord x000, NR::Coord y000, NR::Coord x001, NR::Coord y001, NR::Coord x011, NR::Coord y011, NR::Coord x111, NR::Coord y111, NRRect *bbox)
+{
+ NR::Coord a, b, c, D;
+
+ bbox->x0 = (NR::Coord) MIN (bbox->x0, x111);
+ bbox->y0 = (NR::Coord) MIN (bbox->y0, y111);
+ bbox->x1 = (NR::Coord) MAX (bbox->x1, x111);
+ bbox->y1 = (NR::Coord) MAX (bbox->y1, y111);
+
+ /*
+ * xttt = s * (s * (s * x000 + t * x001) + t * (s * x001 + t * x011)) + t * (s * (s * x001 + t * x011) + t * (s * x011 + t * x111))
+ * xttt = s * (s2 * x000 + s * t * x001 + t * s * x001 + t2 * x011) + t * (s2 * x001 + s * t * x011 + t * s * x011 + t2 * x111)
+ * xttt = s * (s2 * x000 + 2 * st * x001 + t2 * x011) + t * (s2 * x001 + 2 * st * x011 + t2 * x111)
+ * xttt = s3 * x000 + 2 * s2t * x001 + st2 * x011 + s2t * x001 + 2st2 * x011 + t3 * x111
+ * xttt = s3 * x000 + 3s2t * x001 + 3st2 * x011 + t3 * x111
+ * xttt = s3 * x000 + (1 - s) 3s2 * x001 + (1 - s) * (1 - s) * 3s * x011 + (1 - s) * (1 - s) * (1 - s) * x111
+ * xttt = s3 * x000 + (3s2 - 3s3) * x001 + (3s - 6s2 + 3s3) * x011 + (1 - 2s + s2 - s + 2s2 - s3) * x111
+ * xttt = (x000 - 3 * x001 + 3 * x011 - x111) * s3 +
+ * ( 3 * x001 - 6 * x011 + 3 * x111) * s2 +
+ * ( 3 * x011 - 3 * x111) * s +
+ * ( x111)
+ * xttt' = (3 * x000 - 9 * x001 + 9 * x011 - 3 * x111) * s2 +
+ * ( 6 * x001 - 12 * x011 + 6 * x111) * s +
+ * ( 3 * x011 - 3 * x111)
+ */
+
+ a = 3 * x000 - 9 * x001 + 9 * x011 - 3 * x111;
+ b = 6 * x001 - 12 * x011 + 6 * x111;
+ c = 3 * x011 - 3 * x111;
+
+ /*
+ * s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a;
+ */
+ if (fabs (a) < NR_EPSILON) {
+ /* s = -c / b */
+ if (fabs (b) > NR_EPSILON) {
+ double s, t, xttt;
+ s = -c / b;
+ if ((s > 0.0) && (s < 1.0)) {
+ t = 1.0 - s;
+ xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
+ bbox->x0 = (float) MIN (bbox->x0, xttt);
+ bbox->x1 = (float) MAX (bbox->x1, xttt);
+ }
+ }
+ } else {
+ /* s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */
+ D = b * b - 4 * a * c;
+ if (D >= 0.0) {
+ NR::Coord d, s, t, xttt;
+ /* Have solution */
+ d = sqrt (D);
+ s = (-b + d) / (2 * a);
+ if ((s > 0.0) && (s < 1.0)) {
+ t = 1.0 - s;
+ xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
+ bbox->x0 = (NR::Coord) MIN (bbox->x0, xttt);
+ bbox->x1 = (NR::Coord) MAX (bbox->x1, xttt);
+ }
+ s = (-b - d) / (2 * a);
+ if ((s > 0.0) && (s < 1.0)) {
+ t = 1.0 - s;
+ xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
+ bbox->x0 = (NR::Coord) MIN (bbox->x0, xttt);
+ bbox->x1 = (NR::Coord) MAX (bbox->x1, xttt);
+ }
+ }
+ }
+
+ a = 3 * y000 - 9 * y001 + 9 * y011 - 3 * y111;
+ b = 6 * y001 - 12 * y011 + 6 * y111;
+ c = 3 * y011 - 3 * y111;
+
+ if (fabs (a) < NR_EPSILON) {
+ /* s = -c / b */
+ if (fabs (b) > NR_EPSILON) {
+ double s, t, yttt;
+ s = -c / b;
+ if ((s > 0.0) && (s < 1.0)) {
+ t = 1.0 - s;
+ yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
+ bbox->y0 = (float) MIN (bbox->y0, yttt);
+ bbox->y1 = (float) MAX (bbox->y1, yttt);
+ }
+ }
+ } else {
+ /* s = (-b +/- sqrt (b * b - 4 * a * c)) / 2 * a; */
+ D = b * b - 4 * a * c;
+ if (D >= 0.0) {
+ NR::Coord d, s, t, yttt;
+ /* Have solution */
+ d = sqrt (D);
+ s = (-b + d) / (2 * a);
+ if ((s > 0.0) && (s < 1.0)) {
+ t = 1.0 - s;
+ yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
+ bbox->y0 = (NR::Coord) MIN (bbox->y0, yttt);
+ bbox->y1 = (NR::Coord) MAX (bbox->y1, yttt);
+ }
+ s = (-b - d) / (2 * a);
+ if ((s > 0.0) && (s < 1.0)) {
+ t = 1.0 - s;
+ yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
+ bbox->y0 = (NR::Coord) MIN (bbox->y0, yttt);
+ bbox->y1 = (NR::Coord) MAX (bbox->y1, yttt);
+ }
+ }
+ }
+}
+
+void
+nr_path_matrix_bbox_union(NRBPath const *bpath, NR::Matrix const &m,
+ NRRect *bbox)
+{
+ using NR::X;
+ using NR::Y;
+
+ if (!bpath->path) {
+ return;
+ }
+
+ NR::Point prev0(0, 0);
+ for (NArtBpath const *p = bpath->path; p->code != NR_END; ++p) {
+ switch (p->code) {
+ case NR_MOVETO_OPEN:
+ case NR_MOVETO: {
+ NR::Point const c3(p->x3,
+ p->y3);
+ prev0 = c3 * m;
+ nr_rect_union_pt(bbox, prev0);
+ break;
+ }
+
+ case NR_LINETO: {
+ NR::Point const c3(p->x3,
+ p->y3);
+ NR::Point const endPt( c3 * m );
+ nr_rect_union_pt(bbox, endPt);
+ prev0 = endPt;
+ break;
+ }
+
+ case NR_CURVETO: {
+ NR::Point const c1(p->x1, p->y1);
+ NR::Point const c2(p->x2, p->y2);
+ NR::Point const c3(p->x3, p->y3);
+ NR::Point const endPt( c3 * m );
+ nr_curve_bbox(prev0,
+ c1 * m,
+ c2 * m,
+ endPt,
+ bbox);
+ prev0 = endPt;
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
diff --git a/src/libnr/nr-path.h b/src/libnr/nr-path.h
new file mode 100644
index 000000000..625a0e498
--- /dev/null
+++ b/src/libnr/nr-path.h
@@ -0,0 +1,62 @@
+#ifndef __NR_PATH_H__
+#define __NR_PATH_H__
+
+/*
+ * NArtBpath: Curve component. Adapted from libart.
+ */
+
+/*
+ * libart_lgpl/art_bpath.h copyright information:
+ *
+ * Copyright (C) 1998 Raph Levien
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <libnr/nr-forward.h>
+#include <libnr/nr-coord.h>
+
+NArtBpath* nr_artpath_affine(NArtBpath *s, NR::Matrix const &transform);
+
+//ArtBpath* nr_artpath_to_art_bpath(NArtBpath *s); // this lives in src/extension/internal/gnome.cpp to avoid requiring libart everywhere
+
+struct NRBPath {
+ NArtBpath *path;
+};
+
+NRBPath *nr_path_duplicate_transform(NRBPath *d, NRBPath *s, NRMatrix const *transform);
+
+NRBPath *nr_path_duplicate_transform(NRBPath *d, NRBPath *s, NR::Matrix const transform);
+
+void nr_path_matrix_point_bbox_wind_distance (NRBPath *bpath, NR::Matrix const &m, NR::Point &pt,
+ NRRect *bbox, int *wind, NR::Coord *dist,
+ NR::Coord tolerance);
+
+void nr_path_matrix_bbox_union(NRBPath const *bpath, NR::Matrix const &m, NRRect *bbox);
+
+#endif
+
+/*
+ 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/libnr/nr-pixblock-line.cpp b/src/libnr/nr-pixblock-line.cpp
new file mode 100644
index 000000000..5e025c7eb
--- /dev/null
+++ b/src/libnr/nr-pixblock-line.cpp
@@ -0,0 +1,92 @@
+#define __NR_PIXBLOCK_LINE_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixops.h>
+#include <libnr/nr-pixblock-pixel.h>
+
+void
+nr_pixblock_draw_line_rgba32 (NRPixBlock *d, long x0, long y0, long x1, long y1, short first, unsigned long rgba)
+{
+ long deltax, deltay, xinc1, xinc2, yinc1, yinc2;
+ long den, num, numadd, numpixels;
+ long x, y, curpixel;
+ /* Pixblock */
+ int dbpp;
+ NRPixBlock spb;
+ unsigned char *spx;
+
+ if (x1 >= x0) {
+ deltax = x1 - x0;
+ xinc1 = 1;
+ xinc2 = 1;
+ } else {
+ deltax = x0 - x1;
+ xinc1 = -1;
+ xinc2 = -1;
+ }
+
+ if (y1 >= y0) {
+ deltay = y1 - y0;
+ yinc1 = 1;
+ yinc2 = 1;
+ } else {
+ deltay = y0 - y1;
+ yinc1 = -1;
+ yinc2 = -1;
+ }
+
+ if (deltax >= deltay) {
+ xinc1 = 0;
+ yinc2 = 0;
+ den = deltax;
+ num = deltax / 2;
+ numadd = deltay;
+ numpixels = deltax;
+ } else {
+ xinc2 = 0;
+ yinc1 = 0;
+ den = deltay;
+ num = deltay / 2;
+ numadd = deltax;
+ numpixels = deltay;
+ }
+
+ /* We can be quite sure 1x1 pixblock is TINY */
+ nr_pixblock_setup_fast (&spb, NR_PIXBLOCK_MODE_R8G8B8A8N, 0, 0, 1, 1, 0);
+ spb.empty = 0;
+ spx = NR_PIXBLOCK_PX (&spb);
+ spx[0] = NR_RGBA32_R (rgba);
+ spx[1] = NR_RGBA32_G (rgba);
+ spx[2] = NR_RGBA32_B (rgba);
+ spx[3] = NR_RGBA32_A (rgba);
+
+ dbpp = NR_PIXBLOCK_BPP (d);
+
+ x = x0;
+ y = y0;
+
+ for (curpixel = 0; curpixel <= numpixels; curpixel++) {
+ if ((x >= d->area.x0) && (y >= d->area.y0) && (x < d->area.x1) && (y < d->area.y1)) {
+ nr_compose_pixblock_pixblock_pixel (d, NR_PIXBLOCK_PX (d) + (y - d->area.y0) * d->rs + (x - d->area.x0) * dbpp, &spb, spx);
+ }
+ num += numadd;
+ if (num >= den) {
+ num -= den;
+ x += xinc1;
+ y += yinc1;
+ }
+ x += xinc2;
+ y += yinc2;
+ }
+
+ nr_pixblock_release (&spb);
+}
+
diff --git a/src/libnr/nr-pixblock-line.h b/src/libnr/nr-pixblock-line.h
new file mode 100644
index 000000000..7fd58a0ab
--- /dev/null
+++ b/src/libnr/nr-pixblock-line.h
@@ -0,0 +1,28 @@
+#ifndef __NR_PIXBLOCK_LINE_H__
+#define __NR_PIXBLOCK_LINE_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixblock.h>
+
+void nr_pixblock_draw_line_rgba32 (NRPixBlock *d, long x0, long y0, long x1, long y1, short first, unsigned long rgba);
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-pixblock-pattern.cpp b/src/libnr/nr-pixblock-pattern.cpp
new file mode 100644
index 000000000..03bd688ba
--- /dev/null
+++ b/src/libnr/nr-pixblock-pattern.cpp
@@ -0,0 +1,126 @@
+#define __NR_PIXBLOCK_PATTERN_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+
+#include "nr-pixops.h"
+#include "nr-pixblock-pattern.h"
+
+#define NR_NOISE_SIZE 1024
+
+void
+nr_pixblock_render_gray_noise (NRPixBlock *pb, NRPixBlock *mask)
+{
+ static unsigned char *noise = NULL;
+ static unsigned int seed = 0;
+ unsigned int v;
+ NRRectL clip;
+ int x, y, bpp;
+
+ if (mask) {
+ if (mask->empty) return;
+ nr_rect_l_intersect (&clip, &pb->area, &mask->area);
+ if (nr_rect_l_test_empty (&clip)) return;
+ } else {
+ clip = pb->area;
+ }
+
+ if (!noise) {
+ int i;
+ noise = nr_new (unsigned char, NR_NOISE_SIZE);
+ for (i = 0; i < NR_NOISE_SIZE; i++) noise[i] = (rand () / (RAND_MAX >> 8)) & 0xff;
+ }
+
+ bpp = NR_PIXBLOCK_BPP (pb);
+
+ v = (rand () / (RAND_MAX >> 8)) & 0xff;
+
+ if (mask) {
+ for (y = clip.y0; y < clip.y1; y++) {
+ unsigned char *d, *m;
+ d = NR_PIXBLOCK_PX (pb) + (y - pb->area.y0) * pb->rs + (clip.x0 - pb->area.x0) * bpp;
+ m = NR_PIXBLOCK_PX (mask) + (y - mask->area.y0) * pb->rs + (clip.x0 - mask->area.x0);
+ for (x = clip.x0; x < clip.x1; x++) {
+ v = v ^ noise[seed];
+ switch (pb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ d[0] = (65025 - (255 - m[0]) * (255 - d[0]) + 127) / 255;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = NR_COMPOSEN11 (v, m[0], d[0]);
+ d[1] = NR_COMPOSEN11 (v, m[0], d[1]);
+ d[2] = NR_COMPOSEN11 (v, m[0], d[2]);
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ if (m[0] != 0) {
+ unsigned int ca;
+ ca = NR_A7 (m[0], d[3]);
+ d[0] = NR_COMPOSENNN_A7 (v, m[0], d[0], d[3], ca);
+ d[1] = NR_COMPOSENNN_A7 (v, m[0], d[1], d[3], ca);
+ d[2] = NR_COMPOSENNN_A7 (v, m[0], d[2], d[3], ca);
+ d[3] = (ca + 127) / 255;
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ d[0] = NR_COMPOSENPP (v, m[0], d[0], d[3]);
+ d[1] = NR_COMPOSENPP (v, m[0], d[1], d[3]);
+ d[2] = NR_COMPOSENPP (v, m[0], d[2], d[3]);
+ d[3] = (NR_A7 (d[3], m[0]) + 127) / 255;
+ break;
+ default:
+ break;
+ }
+ d += bpp;
+ m += 1;
+ if (++seed >= NR_NOISE_SIZE) {
+ int i;
+ i = (rand () / (RAND_MAX / NR_NOISE_SIZE)) % NR_NOISE_SIZE;
+ noise[i] ^= v;
+ seed = i % (NR_NOISE_SIZE >> 2);
+ }
+ }
+ }
+ } else {
+ for (y = clip.y0; y < clip.y1; y++) {
+ unsigned char *d;
+ d = NR_PIXBLOCK_PX (pb) + (y - pb->area.y0) * pb->rs + (clip.x0 - pb->area.x0) * bpp;
+ for (x = clip.x0; x < clip.x1; x++) {
+ v = v ^ noise[seed];
+ switch (pb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ d[0] = 255;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = v;
+ d[1] = v;
+ d[2] = v;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ d[0] = v;
+ d[1] = v;
+ d[2] = v;
+ d[3] = 255;
+ default:
+ break;
+ }
+ d += bpp;
+ if (++seed >= NR_NOISE_SIZE) {
+ int i;
+ i = (rand () / (RAND_MAX / NR_NOISE_SIZE)) % NR_NOISE_SIZE;
+ noise[i] ^= v;
+ seed = i % (NR_NOISE_SIZE >> 2);
+ }
+ }
+ }
+ }
+
+ pb->empty = 0;
+}
diff --git a/src/libnr/nr-pixblock-pattern.h b/src/libnr/nr-pixblock-pattern.h
new file mode 100644
index 000000000..463a24379
--- /dev/null
+++ b/src/libnr/nr-pixblock-pattern.h
@@ -0,0 +1,28 @@
+#ifndef __NR_PIXBLOCK_PATTERN_H__
+#define __NR_PIXBLOCK_PATTERN_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixblock.h>
+
+void nr_pixblock_render_gray_noise (NRPixBlock *pb, NRPixBlock *mask);
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-pixblock-pixel.cpp b/src/libnr/nr-pixblock-pixel.cpp
new file mode 100644
index 000000000..c778c0c7f
--- /dev/null
+++ b/src/libnr/nr-pixblock-pixel.cpp
@@ -0,0 +1,230 @@
+#define __NR_PIXBLOCK_PIXEL_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include "nr-pixops.h"
+#include "nr-pixblock-pixel.h"
+
+void
+nr_compose_pixblock_pixblock_pixel (NRPixBlock *dpb, unsigned char *d, const NRPixBlock *spb, const unsigned char *s)
+{
+ if (spb->empty) return;
+
+ if (dpb->empty) {
+ /* Empty destination */
+ switch (dpb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ switch (spb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = 255;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ d[0] = s[3];
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ d[0] = s[3];
+ break;
+ default:
+ break;
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ switch (spb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ d[0] = NR_COMPOSEN11 (s[0], s[3], 255);
+ d[1] = NR_COMPOSEN11 (s[1], s[3], 255);
+ d[2] = NR_COMPOSEN11 (s[2], s[3], 255);
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ d[0] = NR_COMPOSEP11 (s[0], s[3], 255);
+ d[1] = NR_COMPOSEP11 (s[1], s[3], 255);
+ d[2] = NR_COMPOSEP11 (s[2], s[3], 255);
+ break;
+ default:
+ break;
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ switch (spb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = 255;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = s[3];
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ if (s[3] == 0) {
+ d[0] = 255;
+ d[1] = 255;
+ d[2] = 255;
+ } else {
+ d[0] = (s[0] * 255) / s[3];
+ d[1] = (s[1] * 255) / s[3];
+ d[2] = (s[2] * 255) / s[3];
+ }
+ d[3] = s[3];
+ break;
+ default:
+ break;
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ switch (spb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = 255;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ d[0] = NR_PREMUL (s[0], s[3]);
+ d[1] = NR_PREMUL (s[1], s[3]);
+ d[2] = NR_PREMUL (s[2], s[3]);
+ d[3] = s[3];
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ d[3] = s[3];
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ /* Image destination */
+ switch (dpb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ switch (spb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = 255;
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ d[0] = NR_A7_NORMALIZED(s[3],d[0]);
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ d[0] = NR_A7_NORMALIZED(s[3],d[0]);
+ break;
+ default:
+ break;
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ switch (spb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ d[0] = NR_COMPOSEN11 (s[0], s[3], d[0]);
+ d[1] = NR_COMPOSEN11 (s[1], s[3], d[1]);
+ d[2] = NR_COMPOSEN11 (s[2], s[3], d[2]);
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ d[0] = NR_COMPOSEP11 (s[0], s[3], d[0]);
+ d[1] = NR_COMPOSEP11 (s[1], s[3], d[1]);
+ d[2] = NR_COMPOSEP11 (s[2], s[3], d[2]);
+ break;
+ default:
+ break;
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ switch (spb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ if (s[3] != 0) {
+ unsigned int ca;
+ ca = NR_A7 (s[3], d[3]);
+ d[0] = NR_COMPOSENNN_A7 (s[0], s[3], d[0], d[3], ca);
+ d[1] = NR_COMPOSENNN_A7 (s[1], s[3], d[1], d[3], ca);
+ d[2] = NR_COMPOSENNN_A7 (s[2], s[3], d[2], d[3], ca);
+ d[3] = (ca + 127) / 255;
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ if (s[3] != 0) {
+ unsigned int ca;
+ ca = NR_A7 (s[3], d[3]);
+ d[0] = NR_COMPOSEPNN_A7 (s[0], s[3], d[0], d[3], ca);
+ d[1] = NR_COMPOSEPNN_A7 (s[1], s[3], d[0], d[3], ca);
+ d[2] = NR_COMPOSEPNN_A7 (s[2], s[3], d[0], d[3], ca);
+ d[3] = (ca + 127) / 255;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ switch (spb->mode) {
+ case NR_PIXBLOCK_MODE_A8:
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8:
+ d[0] = s[0];
+ d[1] = s[1];
+ d[2] = s[2];
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8N:
+ d[0] = NR_COMPOSENPP (s[0], s[3], d[0], d[3]);
+ d[1] = NR_COMPOSENPP (s[1], s[3], d[1], d[3]);
+ d[2] = NR_COMPOSENPP (s[2], s[3], d[2], d[3]);
+ d[3] = NR_A7_NORMALIZED(s[3],d[3]);
+ break;
+ case NR_PIXBLOCK_MODE_R8G8B8A8P:
+ d[0] = NR_COMPOSEPPP (s[0], s[3], d[0], d[3]);
+ d[1] = NR_COMPOSEPPP (s[1], s[3], d[1], d[3]);
+ d[2] = NR_COMPOSEPPP (s[2], s[3], d[2], d[3]);
+ d[3] = NR_A7_NORMALIZED(s[3],d[3]);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
diff --git a/src/libnr/nr-pixblock-pixel.h b/src/libnr/nr-pixblock-pixel.h
new file mode 100644
index 000000000..d989f53cf
--- /dev/null
+++ b/src/libnr/nr-pixblock-pixel.h
@@ -0,0 +1,28 @@
+#ifndef __NR_PIXBLOCK_PIXEL_H__
+#define __NR_PIXBLOCK_PIXEL_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixblock.h>
+
+void nr_compose_pixblock_pixblock_pixel (NRPixBlock *dpb, unsigned char *d, const NRPixBlock *spb, const unsigned char *s);
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-pixblock.cpp b/src/libnr/nr-pixblock.cpp
new file mode 100644
index 000000000..9b1ff2752
--- /dev/null
+++ b/src/libnr/nr-pixblock.cpp
@@ -0,0 +1,411 @@
+#define __NR_PIXBLOCK_C__
+
+/** \file
+ * \brief Allocation/Setup of NRPixBlock objects. Pixel store functions.
+ *
+ * Authors:
+ * (C) 1999-2002 Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in the Public Domain
+ */
+
+#include "nr-pixblock.h"
+
+/// Size of buffer that needs no allocation (default 4).
+#define NR_TINY_MAX sizeof (unsigned char *)
+
+/**
+ * Pixbuf initialisation using homegrown memory handling ("pixelstore").
+ *
+ * Pixbuf sizes are differentiated into tiny, <4K, <16K, <64K, and more,
+ * with each type having its own method of memory handling. After allocating
+ * memory, the buffer is cleared if the clear flag is set. Intended to
+ * reduce memory fragmentation.
+ * \param pb Pointer to the pixbuf struct.
+ * \param mode Indicates grayscale/RGB/RGBA.
+ * \param clear True if buffer should be cleared.
+ * \pre x1>=x0 && y1>=y0 && pb!=NULL
+ */
+void
+nr_pixblock_setup_fast (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
+{
+ int w, h, bpp;
+ size_t size;
+
+ w = x1 - x0;
+ h = y1 - y0;
+ bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
+
+ size = bpp * w * h;
+
+ if (size <= NR_TINY_MAX) {
+ pb->size = NR_PIXBLOCK_SIZE_TINY;
+ if (clear) memset (pb->data.p, 0x0, size);
+ } else if (size <= 4096) {
+ pb->size = NR_PIXBLOCK_SIZE_4K;
+ pb->data.px = nr_pixelstore_4K_new (clear, 0x0);
+ } else if (size <= 16384) {
+ pb->size = NR_PIXBLOCK_SIZE_16K;
+ pb->data.px = nr_pixelstore_16K_new (clear, 0x0);
+ } else if (size <= 65536) {
+ pb->size = NR_PIXBLOCK_SIZE_64K;
+ pb->data.px = nr_pixelstore_64K_new (clear, 0x0);
+ } else if (size <= 262144) {
+ pb->size = NR_PIXBLOCK_SIZE_256K;
+ pb->data.px = nr_pixelstore_256K_new (clear, 0x0);
+ } else if (size <= 1048576) {
+ pb->size = NR_PIXBLOCK_SIZE_1M;
+ pb->data.px = nr_pixelstore_1M_new (clear, 0x0);
+ } else {
+ pb->size = NR_PIXBLOCK_SIZE_BIG;
+ pb->data.px = nr_new (unsigned char, size);
+ if (clear) memset (pb->data.px, 0x0, size);
+ }
+
+ pb->mode = mode;
+ pb->empty = 1;
+ pb->area.x0 = x0;
+ pb->area.y0 = y0;
+ pb->area.x1 = x1;
+ pb->area.y1 = y1;
+ pb->rs = bpp * w;
+}
+
+/**
+ * Pixbuf initialisation using nr_new.
+ *
+ * After allocating memory, the buffer is cleared if the clear flag is set.
+ * \param pb Pointer to the pixbuf struct.
+ * \param mode Indicates grayscale/RGB/RGBA.
+ * \param clear True if buffer should be cleared.
+ * \pre x1>=x0 && y1>=y0 && pb!=NULL
+ */
+void
+nr_pixblock_setup (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
+{
+ int w, h, bpp;
+ size_t size;
+
+ w = x1 - x0;
+ h = y1 - y0;
+ bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
+
+ size = bpp * w * h;
+
+ if (size <= NR_TINY_MAX) {
+ pb->size = NR_PIXBLOCK_SIZE_TINY;
+ if (clear) memset (pb->data.p, 0x0, size);
+ } else {
+ pb->size = NR_PIXBLOCK_SIZE_BIG;
+ pb->data.px = nr_new (unsigned char, size);
+ if (clear) memset (pb->data.px, 0x0, size);
+ }
+
+ pb->mode = mode;
+ pb->empty = 1;
+ pb->area.x0 = x0;
+ pb->area.y0 = y0;
+ pb->area.x1 = x1;
+ pb->area.y1 = y1;
+ pb->rs = bpp * w;
+}
+
+/**
+ * Pixbuf initialisation with preset values.
+ *
+ * After copying all parameters into the NRPixBlock struct, the pixel buffer is cleared if the clear flag is set.
+ * \param pb Pointer to the pixbuf struct.
+ * \param mode Indicates grayscale/RGB/RGBA.
+ * \param clear True if buffer should be cleared.
+ * \pre x1>=x0 && y1>=y0 && pb!=NULL
+ */
+void
+nr_pixblock_setup_extern (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, unsigned char *px, int rs, bool empty, bool clear)
+{
+ int w, bpp;
+
+ w = x1 - x0;
+ bpp = (mode == NR_PIXBLOCK_MODE_A8) ? 1 : (mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4;
+
+ pb->size = NR_PIXBLOCK_SIZE_STATIC;
+ pb->mode = mode;
+ pb->empty = empty;
+ pb->area.x0 = x0;
+ pb->area.y0 = y0;
+ pb->area.x1 = x1;
+ pb->area.y1 = y1;
+ pb->data.px = px;
+ pb->rs = rs;
+
+ g_assert (pb->data.px != NULL);
+ if (clear) {
+ if (rs == bpp * w) {
+ /// \todo How do you recognise if
+ /// px was an uncleared tiny buffer?
+ if (pb->data.px)
+ memset (pb->data.px, 0x0, bpp * (y1 - y0) * w);
+ } else {
+ int y;
+ for (y = y0; y < y1; y++) {
+ memset (pb->data.px + (y - y0) * rs, 0x0, bpp * w);
+ }
+ }
+ }
+}
+
+/**
+ * Frees memory taken by pixel data in NRPixBlock.
+ * \param pb Pointer to pixblock.
+ * \pre pb and pb->data.px point to valid addresses.
+ *
+ * According to pb->size, one of the functions for freeing the pixelstore
+ * is called. May be called regardless of how pixbuf was set up.
+ */
+void
+nr_pixblock_release (NRPixBlock *pb)
+{
+ switch (pb->size) {
+ case NR_PIXBLOCK_SIZE_TINY:
+ break;
+ case NR_PIXBLOCK_SIZE_4K:
+ nr_pixelstore_4K_free (pb->data.px);
+ break;
+ case NR_PIXBLOCK_SIZE_16K:
+ nr_pixelstore_16K_free (pb->data.px);
+ break;
+ case NR_PIXBLOCK_SIZE_64K:
+ nr_pixelstore_64K_free (pb->data.px);
+ break;
+ case NR_PIXBLOCK_SIZE_256K:
+ nr_pixelstore_256K_free (pb->data.px);
+ break;
+ case NR_PIXBLOCK_SIZE_1M:
+ nr_pixelstore_1M_free (pb->data.px);
+ break;
+ case NR_PIXBLOCK_SIZE_BIG:
+ nr_free (pb->data.px);
+ break;
+ case NR_PIXBLOCK_SIZE_STATIC:
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Allocates NRPixBlock and sets it up.
+ *
+ * \return Pointer to fresh pixblock.
+ * Calls nr_new() and nr_pixblock_setup().
+ */
+NRPixBlock *
+nr_pixblock_new (NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear)
+{
+ NRPixBlock *pb;
+
+ pb = nr_new (NRPixBlock, 1);
+
+ nr_pixblock_setup (pb, mode, x0, y0, x1, y1, clear);
+
+ return pb;
+}
+
+/**
+ * Frees all memory taken by pixblock.
+ *
+ * \return NULL
+ */
+NRPixBlock *
+nr_pixblock_free (NRPixBlock *pb)
+{
+ nr_pixblock_release (pb);
+
+ nr_free (pb);
+
+ return NULL;
+}
+
+/* PixelStore operations */
+
+#define NR_4K_BLOCK 32
+static unsigned char **nr_4K_px = NULL;
+static unsigned int nr_4K_len = 0;
+static unsigned int nr_4K_size = 0;
+
+unsigned char *
+nr_pixelstore_4K_new (bool clear, unsigned char val)
+{
+ unsigned char *px;
+
+ if (nr_4K_len != 0) {
+ nr_4K_len -= 1;
+ px = nr_4K_px[nr_4K_len];
+ } else {
+ px = nr_new (unsigned char, 4096);
+ }
+
+ if (clear) memset (px, val, 4096);
+
+ return px;
+}
+
+void
+nr_pixelstore_4K_free (unsigned char *px)
+{
+ if (nr_4K_len == nr_4K_size) {
+ nr_4K_size += NR_4K_BLOCK;
+ nr_4K_px = nr_renew (nr_4K_px, unsigned char *, nr_4K_size);
+ }
+
+ nr_4K_px[nr_4K_len] = px;
+ nr_4K_len += 1;
+}
+
+#define NR_16K_BLOCK 32
+static unsigned char **nr_16K_px = NULL;
+static unsigned int nr_16K_len = 0;
+static unsigned int nr_16K_size = 0;
+
+unsigned char *
+nr_pixelstore_16K_new (bool clear, unsigned char val)
+{
+ unsigned char *px;
+
+ if (nr_16K_len != 0) {
+ nr_16K_len -= 1;
+ px = nr_16K_px[nr_16K_len];
+ } else {
+ px = nr_new (unsigned char, 16384);
+ }
+
+ if (clear) memset (px, val, 16384);
+
+ return px;
+}
+
+void
+nr_pixelstore_16K_free (unsigned char *px)
+{
+ if (nr_16K_len == nr_16K_size) {
+ nr_16K_size += NR_16K_BLOCK;
+ nr_16K_px = nr_renew (nr_16K_px, unsigned char *, nr_16K_size);
+ }
+
+ nr_16K_px[nr_16K_len] = px;
+ nr_16K_len += 1;
+}
+
+#define NR_64K_BLOCK 32
+static unsigned char **nr_64K_px = NULL;
+static unsigned int nr_64K_len = 0;
+static unsigned int nr_64K_size = 0;
+
+unsigned char *
+nr_pixelstore_64K_new (bool clear, unsigned char val)
+{
+ unsigned char *px;
+
+ if (nr_64K_len != 0) {
+ nr_64K_len -= 1;
+ px = nr_64K_px[nr_64K_len];
+ } else {
+ px = nr_new (unsigned char, 65536);
+ }
+
+ if (clear) memset (px, val, 65536);
+
+ return px;
+}
+
+void
+nr_pixelstore_64K_free (unsigned char *px)
+{
+ if (nr_64K_len == nr_64K_size) {
+ nr_64K_size += NR_64K_BLOCK;
+ nr_64K_px = nr_renew (nr_64K_px, unsigned char *, nr_64K_size);
+ }
+
+ nr_64K_px[nr_64K_len] = px;
+ nr_64K_len += 1;
+}
+
+#define NR_256K_BLOCK 32
+#define NR_256K 262144
+static unsigned char **nr_256K_px = NULL;
+static unsigned int nr_256K_len = 0;
+static unsigned int nr_256K_size = 0;
+
+unsigned char *
+nr_pixelstore_256K_new (bool clear, unsigned char val)
+{
+ unsigned char *px;
+
+ if (nr_256K_len != 0) {
+ nr_256K_len -= 1;
+ px = nr_256K_px[nr_256K_len];
+ } else {
+ px = nr_new (unsigned char, NR_256K);
+ }
+
+ if (clear) memset (px, val, NR_256K);
+
+ return px;
+}
+
+void
+nr_pixelstore_256K_free (unsigned char *px)
+{
+ if (nr_256K_len == nr_256K_size) {
+ nr_256K_size += NR_256K_BLOCK;
+ nr_256K_px = nr_renew (nr_256K_px, unsigned char *, nr_256K_size);
+ }
+
+ nr_256K_px[nr_256K_len] = px;
+ nr_256K_len += 1;
+}
+
+#define NR_1M_BLOCK 32
+#define NR_1M 1048576
+static unsigned char **nr_1M_px = NULL;
+static unsigned int nr_1M_len = 0;
+static unsigned int nr_1M_size = 0;
+
+unsigned char *
+nr_pixelstore_1M_new (bool clear, unsigned char val)
+{
+ unsigned char *px;
+
+ if (nr_1M_len != 0) {
+ nr_1M_len -= 1;
+ px = nr_1M_px[nr_1M_len];
+ } else {
+ px = nr_new (unsigned char, NR_1M);
+ }
+
+ if (clear) memset (px, val, NR_1M);
+
+ return px;
+}
+
+void
+nr_pixelstore_1M_free (unsigned char *px)
+{
+ if (nr_1M_len == nr_1M_size) {
+ nr_1M_size += NR_1M_BLOCK;
+ nr_1M_px = nr_renew (nr_1M_px, unsigned char *, nr_1M_size);
+ }
+
+ nr_1M_px[nr_1M_len] = px;
+ nr_1M_len += 1;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-pixblock.h b/src/libnr/nr-pixblock.h
new file mode 100644
index 000000000..2a13dd921
--- /dev/null
+++ b/src/libnr/nr-pixblock.h
@@ -0,0 +1,95 @@
+#ifndef __NR_PIXBLOCK_H__
+#define __NR_PIXBLOCK_H__
+
+/** \file
+ * \brief Pixel block structure. Used for low-level rendering.
+ *
+ * Authors:
+ * (C) 1999-2002 Lauris Kaplinski <lauris@kaplinski.com>
+ * (C) 2005 Ralf Stephan <ralf@ark.in-berlin.de> (some cleanup)
+ *
+ * This code is in the Public Domain.
+ */
+
+#include <libnr/nr-rect-l.h>
+#include <libnr/nr-forward.h>
+
+/// Size indicator. Hardcoded to max. 3 bits.
+typedef enum {
+ NR_PIXBLOCK_SIZE_TINY, ///< Fits in (unsigned char *)
+ NR_PIXBLOCK_SIZE_4K, ///< Pixelstore
+ NR_PIXBLOCK_SIZE_16K, ///< Pixelstore
+ NR_PIXBLOCK_SIZE_64K, ///< Pixelstore
+ NR_PIXBLOCK_SIZE_256K, ///< Pixelstore
+ NR_PIXBLOCK_SIZE_1M, ///< Pixelstore
+ NR_PIXBLOCK_SIZE_BIG, ///< Normally allocated
+ NR_PIXBLOCK_SIZE_STATIC ///< Externally managed
+} NR_PIXBLOCK_SIZE;
+
+/// Mode indicator. Hardcoded to max. 2 bits.
+typedef enum {
+ NR_PIXBLOCK_MODE_A8, ///< Grayscale
+ NR_PIXBLOCK_MODE_R8G8B8, ///< 8 bit RGB
+ NR_PIXBLOCK_MODE_R8G8B8A8N, ///< Normal 8 bit RGBA
+ NR_PIXBLOCK_MODE_R8G8B8A8P ///< Premultiplied 8 bit RGBA
+} NR_PIXBLOCK_MODE;
+
+/// The pixel block struct.
+struct NRPixBlock {
+ NR_PIXBLOCK_SIZE size : 3; ///< Size indicator
+ NR_PIXBLOCK_MODE mode : 2; ///< Mode indicator
+ bool empty : 1; ///< Empty flag
+ unsigned int rs; ///< Size of line in bytes
+ NRRectL area;
+ union {
+ unsigned char *px; ///< Pointer to buffer
+ unsigned char p[sizeof (unsigned char *)]; ///< Tiny buffer
+ } data;
+};
+
+/// Returns number of bytes per pixel (1, 3, or 4).
+inline int
+NR_PIXBLOCK_BPP (NRPixBlock *pb)
+{
+ return ((pb->mode == NR_PIXBLOCK_MODE_A8) ? 1 :
+ (pb->mode == NR_PIXBLOCK_MODE_R8G8B8) ? 3 : 4);
+}
+
+/// Returns pointer to pixel data.
+inline unsigned char*
+NR_PIXBLOCK_PX (NRPixBlock *pb)
+{
+ return ((pb->size == NR_PIXBLOCK_SIZE_TINY) ?
+ pb->data.p : pb->data.px);
+}
+
+void nr_pixblock_setup (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear);
+void nr_pixblock_setup_fast (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear);
+void nr_pixblock_setup_extern (NRPixBlock *pb, NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, unsigned char *px, int rs, bool empty, bool clear);
+void nr_pixblock_release (NRPixBlock *pb);
+
+NRPixBlock *nr_pixblock_new (NR_PIXBLOCK_MODE mode, int x0, int y0, int x1, int y1, bool clear);
+NRPixBlock *nr_pixblock_free (NRPixBlock *pb);
+
+unsigned char *nr_pixelstore_4K_new (bool clear, unsigned char val);
+void nr_pixelstore_4K_free (unsigned char *px);
+unsigned char *nr_pixelstore_16K_new (bool clear, unsigned char val);
+void nr_pixelstore_16K_free (unsigned char *px);
+unsigned char *nr_pixelstore_64K_new (bool clear, unsigned char val);
+void nr_pixelstore_64K_free (unsigned char *px);
+unsigned char *nr_pixelstore_256K_new (bool clear, unsigned char val);
+void nr_pixelstore_256K_free (unsigned char *px);
+unsigned char *nr_pixelstore_1M_new (bool clear, unsigned char val);
+void nr_pixelstore_1M_free (unsigned char *px);
+
+#endif
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-pixops.h b/src/libnr/nr-pixops.h
new file mode 100644
index 000000000..32fc1e1cd
--- /dev/null
+++ b/src/libnr/nr-pixops.h
@@ -0,0 +1,46 @@
+#ifndef __NR_PIXOPS_H__
+#define __NR_PIXOPS_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * This code is in public domain
+ */
+
+#define NR_RGBA32_R(v) (unsigned char) (((v) >> 24) & 0xff)
+#define NR_RGBA32_G(v) (unsigned char) (((v) >> 16) & 0xff)
+#define NR_RGBA32_B(v) (unsigned char) (((v) >> 8) & 0xff)
+#define NR_RGBA32_A(v) (unsigned char) ((v) & 0xff)
+
+#define FAST_DIVIDE_BY_255(v) ((((v) << 8) + (v) + 257) >> 16)
+#define NR_PREMUL(c,a) (FAST_DIVIDE_BY_255(((c) * (a) + 127)))
+#define NR_PREMUL_SINGLE(c) (FAST_DIVIDE_BY_255((c) + 127))
+#define NR_A7(fa,ba) (65025 - (255 - fa) * (255 - ba))
+#define NR_A7_NORMALIZED(fa,ba) (FAST_DIVIDE_BY_255((65025 - (255 - (fa)) * (255 - (ba))) + 127))
+#define NR_COMPOSENNN_A7(fc,fa,bc,ba,a) (((255 - (fa)) * (bc) * (ba) + (fa) * (fc) * 255 + 127) / a)
+#define NR_COMPOSEPNN_A7(fc,fa,bc,ba,a) (((255 - (fa)) * (bc) * (ba) + (fc) * 65025 + 127) / a)
+#define NR_COMPOSENNP(fc,fa,bc,ba) (((255 - (fa)) * (bc) * (ba) + (fa) * (fc) * 255 + 32512) / 65025)
+#define NR_COMPOSEPNP(fc,fa,bc,ba) (((255 - (fa)) * (bc) * (ba) + (fc) * 65025 + 32512) / 65025)
+#define NR_COMPOSENPP(fc,fa,bc,ba) (FAST_DIVIDE_BY_255((255 - (fa)) * (bc) + (fa) * (fc) + 127))
+#define NR_COMPOSEPPP(fc,fa,bc,ba) (FAST_DIVIDE_BY_255((255 - (fa)) * (bc) + (fc) * 255 + 127))
+#define NR_COMPOSEP11(fc,fa,bc) (FAST_DIVIDE_BY_255((255 - (fa)) * (bc) + (fc) * 255 + 127))
+#define NR_COMPOSEN11(fc,fa,bc) (FAST_DIVIDE_BY_255((255 - (fa)) * (bc) + (fc) * (fa) + 127))
+
+#define INK_COMPOSE(f,a,b) ( ( ((guchar) b) * ((guchar) (0xff - a)) + ((guchar) ((b ^ ~f) + b/4 - (b>127? 63 : 0))) * ((guchar) a) ) >>8)
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-point-fns-test.cpp b/src/libnr/nr-point-fns-test.cpp
new file mode 100644
index 000000000..11181436b
--- /dev/null
+++ b/src/libnr/nr-point-fns-test.cpp
@@ -0,0 +1,107 @@
+#include <cassert>
+#include <cmath>
+#include <glib/gmacros.h>
+#include <stdlib.h>
+
+#include "utest/utest.h"
+#include "libnr/nr-point-fns.h"
+#include "isnan.h"
+
+using NR::Point;
+
+int main(int argc, char *argv[])
+{
+ utest_start("nr-point-fns");
+
+ Point const p3n4(3.0, -4.0);
+ Point const p0(0.0, 0.0);
+ double const small = pow(2.0, -1070);
+ double const inf = 1e400;
+ double const nan = inf - inf;
+
+ Point const small_left(-small, 0.0);
+ Point const small_n3_4(-3.0 * small, 4.0 * small);
+ Point const part_nan(3., nan);
+ Point const inf_left(-inf, 5.0);
+
+ assert(isNaN(nan));
+ assert(!isNaN(small));
+
+ UTEST_TEST("L1") {
+ UTEST_ASSERT( NR::L1(p0) == 0.0 );
+ UTEST_ASSERT( NR::L1(p3n4) == 7.0 );
+ UTEST_ASSERT( NR::L1(small_left) == small );
+ UTEST_ASSERT( NR::L1(inf_left) == inf );
+ UTEST_ASSERT( NR::L1(small_n3_4) == 7.0 * small );
+ UTEST_ASSERT(isNaN(NR::L1(part_nan)));
+ }
+
+ UTEST_TEST("L2") {
+ UTEST_ASSERT( NR::L2(p0) == 0.0 );
+ UTEST_ASSERT( NR::L2(p3n4) == 5.0 );
+ UTEST_ASSERT( NR::L2(small_left) == small );
+ UTEST_ASSERT( NR::L2(inf_left) == inf );
+ UTEST_ASSERT( NR::L2(small_n3_4) == 5.0 * small );
+ UTEST_ASSERT(isNaN(NR::L2(part_nan)));
+ }
+
+ UTEST_TEST("LInfty") {
+ UTEST_ASSERT( NR::LInfty(p0) == 0.0 );
+ UTEST_ASSERT( NR::LInfty(p3n4) == 4.0 );
+ UTEST_ASSERT( NR::LInfty(small_left) == small );
+ UTEST_ASSERT( NR::LInfty(inf_left) == inf );
+ UTEST_ASSERT( NR::LInfty(small_n3_4) == 4.0 * small );
+ UTEST_ASSERT(isNaN(NR::LInfty(part_nan)));
+ }
+
+ UTEST_TEST("is_zero") {
+ UTEST_ASSERT(NR::is_zero(p0));
+ UTEST_ASSERT(!NR::is_zero(p3n4));
+ UTEST_ASSERT(!NR::is_zero(small_left));
+ UTEST_ASSERT(!NR::is_zero(inf_left));
+ UTEST_ASSERT(!NR::is_zero(small_n3_4));
+ UTEST_ASSERT(!NR::is_zero(part_nan));
+ }
+
+ UTEST_TEST("atan2") {
+ UTEST_ASSERT( NR::atan2(p3n4) == atan2(-4.0, 3.0) );
+ UTEST_ASSERT( NR::atan2(small_left) == atan2(0.0, -1.0) );
+ UTEST_ASSERT( NR::atan2(small_n3_4) == atan2(4.0, -3.0) );
+ }
+
+ UTEST_TEST("unit_vector") {
+ UTEST_ASSERT( NR::unit_vector(p3n4) == Point(.6, -0.8) );
+ UTEST_ASSERT( NR::unit_vector(small_left) == Point(-1.0, 0.0) );
+ UTEST_ASSERT( NR::unit_vector(small_n3_4) == Point(-.6, 0.8) );
+ }
+
+ UTEST_TEST("is_unit_vector") {
+ UTEST_ASSERT(!NR::is_unit_vector(p3n4));
+ UTEST_ASSERT(!NR::is_unit_vector(small_left));
+ UTEST_ASSERT(!NR::is_unit_vector(small_n3_4));
+ UTEST_ASSERT(!NR::is_unit_vector(part_nan));
+ UTEST_ASSERT(!NR::is_unit_vector(inf_left));
+ UTEST_ASSERT(!NR::is_unit_vector(Point(.5, 0.5)));
+ UTEST_ASSERT(NR::is_unit_vector(Point(.6, -0.8)));
+ UTEST_ASSERT(NR::is_unit_vector(Point(-.6, 0.8)));
+ UTEST_ASSERT(NR::is_unit_vector(Point(-1.0, 0.0)));
+ UTEST_ASSERT(NR::is_unit_vector(Point(1.0, 0.0)));
+ UTEST_ASSERT(NR::is_unit_vector(Point(0.0, -1.0)));
+ UTEST_ASSERT(NR::is_unit_vector(Point(0.0, 1.0)));
+ }
+
+ return ( utest_end()
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE );
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-point-fns-test.h b/src/libnr/nr-point-fns-test.h
new file mode 100644
index 000000000..509d9d2fa
--- /dev/null
+++ b/src/libnr/nr-point-fns-test.h
@@ -0,0 +1,141 @@
+// nr-point-fns-test.h
+#include <cxxtest/TestSuite.h>
+
+#include <cassert>
+#include <cmath>
+#include <glib/gmacros.h>
+#include <stdlib.h>
+
+#include "libnr/nr-point-fns.h"
+#include "isnan.h"
+
+using NR::Point;
+
+class NrPointFnsTest : public CxxTest::TestSuite
+{
+public:
+ NrPointFnsTest() :
+ setupValid(true),
+ p3n4( 3.0, -4.0 ),
+ p0( 0.0, 0.0 ),
+ small( pow( 2.0, -1070 ) ),
+ inf( 1e400 ),
+ nan( inf - inf ),
+ small_left( -small, 0.0 ),
+ small_n3_4( -3.0 * small, 4.0 * small ),
+ part_nan( 3., nan ),
+ inf_left( -inf, 5.0 )
+ {
+ TS_ASSERT( isNaN(nan) );
+ TS_ASSERT( !isNaN(small) );
+
+ setupValid &= isNaN(nan);
+ setupValid &= !isNaN(small);
+ }
+ virtual ~NrPointFnsTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static NrPointFnsTest *createSuite() { return new NrPointFnsTest(); }
+ static void destroySuite( NrPointFnsTest *suite ) { delete suite; }
+
+// Called before each test in this suite
+ void setUp()
+ {
+ TS_ASSERT( setupValid );
+ }
+
+ bool setupValid;
+ Point const p3n4;
+ Point const p0;
+ double const small;
+ double const inf;
+ double const nan;
+
+ Point const small_left;
+ Point const small_n3_4;
+ Point const part_nan;
+ Point const inf_left;
+
+
+ void testL1(void)
+ {
+ TS_ASSERT_EQUALS( NR::L1(p0), 0.0 );
+ TS_ASSERT_EQUALS( NR::L1(p3n4), 7.0 );
+ TS_ASSERT_EQUALS( NR::L1(small_left), small );
+ TS_ASSERT_EQUALS( NR::L1(inf_left), inf );
+ TS_ASSERT_EQUALS( NR::L1(small_n3_4), 7.0 * small );
+ TS_ASSERT(isNaN(NR::L1(part_nan)));
+ }
+
+ void testL2(void)
+ {
+ TS_ASSERT_EQUALS( NR::L2(p0), 0.0 );
+ TS_ASSERT_EQUALS( NR::L2(p3n4), 5.0 );
+ TS_ASSERT_EQUALS( NR::L2(small_left), small );
+ TS_ASSERT_EQUALS( NR::L2(inf_left), inf );
+ TS_ASSERT_EQUALS( NR::L2(small_n3_4), 5.0 * small );
+ TS_ASSERT( isNaN(NR::L2(part_nan)) );
+ }
+
+ void testLInfty(void)
+ {
+ TS_ASSERT_EQUALS( NR::LInfty(p0), 0.0 );
+ TS_ASSERT_EQUALS( NR::LInfty(p3n4), 4.0 );
+ TS_ASSERT_EQUALS( NR::LInfty(small_left), small );
+ TS_ASSERT_EQUALS( NR::LInfty(inf_left), inf );
+ TS_ASSERT_EQUALS( NR::LInfty(small_n3_4), 4.0 * small );
+ TS_ASSERT( isNaN(NR::LInfty(part_nan)) );
+ }
+
+ void testIsZero(void)
+ {
+ TS_ASSERT( NR::is_zero(p0) );
+ TS_ASSERT( !NR::is_zero(p3n4) );
+ TS_ASSERT( !NR::is_zero(small_left) );
+ TS_ASSERT( !NR::is_zero(inf_left) );
+ TS_ASSERT( !NR::is_zero(small_n3_4) );
+ TS_ASSERT( !NR::is_zero(part_nan) );
+ }
+
+ void testAtan2(void)
+ {
+ TS_ASSERT_EQUALS( NR::atan2(p3n4), atan2(-4.0, 3.0) );
+ TS_ASSERT_EQUALS( NR::atan2(small_left), atan2(0.0, -1.0) );
+ TS_ASSERT_EQUALS( NR::atan2(small_n3_4), atan2(4.0, -3.0) );
+ }
+
+ void testUnitVector(void)
+ {
+ TS_ASSERT_EQUALS( NR::unit_vector(p3n4), Point(.6, -0.8) );
+ TS_ASSERT_EQUALS( NR::unit_vector(small_left), Point(-1.0, 0.0) );
+ TS_ASSERT_EQUALS( NR::unit_vector(small_n3_4), Point(-.6, 0.8) );
+ }
+
+ void testIsUnitVector(void)
+ {
+ TS_ASSERT( !NR::is_unit_vector(p3n4) );
+ TS_ASSERT( !NR::is_unit_vector(small_left) );
+ TS_ASSERT( !NR::is_unit_vector(small_n3_4) );
+ TS_ASSERT( !NR::is_unit_vector(part_nan) );
+ TS_ASSERT( !NR::is_unit_vector(inf_left) );
+ TS_ASSERT( !NR::is_unit_vector(Point(.5, 0.5)) );
+ TS_ASSERT( NR::is_unit_vector(Point(.6, -0.8)) );
+ TS_ASSERT( NR::is_unit_vector(Point(-.6, 0.8)) );
+ TS_ASSERT( NR::is_unit_vector(Point(-1.0, 0.0)) );
+ TS_ASSERT( NR::is_unit_vector(Point(1.0, 0.0)) );
+ TS_ASSERT( NR::is_unit_vector(Point(0.0, -1.0)) );
+ TS_ASSERT( NR::is_unit_vector(Point(0.0, 1.0)) );
+ }
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-point-fns.cpp b/src/libnr/nr-point-fns.cpp
new file mode 100644
index 000000000..a92c2b00b
--- /dev/null
+++ b/src/libnr/nr-point-fns.cpp
@@ -0,0 +1,74 @@
+#include <libnr/nr-point-fns.h>
+#include <isnan.h>
+
+using NR::Point;
+
+/** Compute the L infinity, or maximum, norm of \a p. */
+NR::Coord NR::LInfty(Point const &p) {
+ NR::Coord const a(fabs(p[0]));
+ NR::Coord const b(fabs(p[1]));
+ return ( a < b || isNaN(b)
+ ? b
+ : a );
+}
+
+/** Returns true iff p is a zero vector, i.e.\ Point(0, 0).
+ *
+ * (NaN is considered non-zero.)
+ */
+bool
+NR::is_zero(Point const &p)
+{
+ return ( p[0] == 0 &&
+ p[1] == 0 );
+}
+
+bool
+NR::is_unit_vector(Point const &p)
+{
+ return fabs(1.0 - L2(p)) <= 1e-4;
+ /* The tolerance of 1e-4 is somewhat arbitrary. NR::Point::normalize is believed to return
+ points well within this tolerance. I'm not aware of any callers that want a small
+ tolerance; most callers would be ok with a tolerance of 0.25. */
+}
+
+NR::Coord NR::atan2(Point const p) {
+ return std::atan2(p[NR::Y], p[NR::X]);
+}
+
+/** Returns a version of \a a scaled to be a unit vector (within rounding error).
+ *
+ * The current version tries to handle infinite coordinates gracefully,
+ * but it's not clear that any callers need that.
+ *
+ * \pre a != Point(0, 0).
+ * \pre Neither coordinate is NaN.
+ * \post L2(ret) very near 1.0.
+ */
+Point NR::unit_vector(Point const &a)
+{
+ Point ret(a);
+ ret.normalize();
+ return ret;
+}
+
+NR::Point abs(NR::Point const &b)
+{
+ NR::Point ret;
+ for ( int i = 0 ; i < 2 ; i++ ) {
+ ret[i] = fabs(b[i]);
+ }
+ return ret;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-point-fns.h b/src/libnr/nr-point-fns.h
new file mode 100644
index 000000000..9f9a8f9e2
--- /dev/null
+++ b/src/libnr/nr-point-fns.h
@@ -0,0 +1,104 @@
+#ifndef __NR_POINT_OPS_H__
+#define __NR_POINT_OPS_H__
+
+#include <libnr/nr-point-ops.h>
+#include <libnr/nr-dim2.h>
+#include <libnr/nr-macros.h>
+
+namespace NR {
+
+/** Compute the L1 norm, or manhattan distance, of \a p. */
+inline Coord L1(Point const &p) {
+ Coord d = 0;
+ for ( int i = 0 ; i < 2 ; i++ ) {
+ d += fabs(p[i]);
+ }
+ return d;
+}
+
+/** Compute the L2, or euclidean, norm of \a p. */
+inline Coord L2(Point const &p) {
+ return hypot(p[0], p[1]);
+}
+
+extern double LInfty(Point const &p);
+
+bool is_zero(Point const &p);
+
+bool is_unit_vector(Point const &p);
+
+extern double atan2(Point const p);
+
+inline bool point_equalp(Point const &a, Point const &b, double const eps)
+{
+ return ( NR_DF_TEST_CLOSE(a[X], b[X], eps) &&
+ NR_DF_TEST_CLOSE(a[Y], b[Y], eps) );
+}
+
+/** Returns p * NR::rotate_degrees(90), but more efficient.
+ *
+ * Angle direction in Inkscape code: If you use the traditional mathematics convention that y
+ * increases upwards, then positive angles are anticlockwise as per the mathematics convention. If
+ * you take the common non-mathematical convention that y increases downwards, then positive angles
+ * are clockwise, as is common outside of mathematics.
+ *
+ * There is no rot_neg90 function: use -rot90(p) instead.
+ */
+inline Point rot90(Point const &p)
+{
+ return Point(-p[Y], p[X]);
+}
+
+/** Given two points and a parameter t \in [0, 1], return a point
+ * proportionally from a to b by t. */
+inline Point Lerp(double const t, Point const a, Point const b)
+{
+ return ( ( 1 - t ) * a
+ + t * b );
+}
+
+Point unit_vector(Point const &a);
+
+inline Coord dot(Point const &a, Point const &b)
+{
+ Coord ret = 0;
+ for ( int i = 0 ; i < 2 ; i++ ) {
+ ret += a[i] * b[i];
+ }
+ return ret;
+}
+
+inline Coord distance (Point const &a, Point const &b)
+{
+ Coord ret = 0;
+ for ( int i = 0 ; i < 2 ; i++ ) {
+ ret += (a[i] - b[i]) * (a[i] - b[i]);
+ }
+ return sqrt (ret);
+}
+
+/** Defined as dot(a, b.cw()). */
+inline Coord cross(Point const &a, Point const &b)
+{
+ Coord ret = 0;
+ ret -= a[0] * b[1];
+ ret += a[1] * b[0];
+ return ret;
+}
+
+Point abs(Point const &b);
+
+} /* namespace NR */
+
+#endif /* !__NR_POINT_OPS_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-point-l.h b/src/libnr/nr-point-l.h
new file mode 100644
index 000000000..8ddfd5e6f
--- /dev/null
+++ b/src/libnr/nr-point-l.h
@@ -0,0 +1,95 @@
+#ifndef SEEN_NR_POINT_L_H
+#define SEEN_NR_POINT_L_H
+
+#include <stdexcept>
+#include <libnr/nr-i-coord.h>
+#include <libnr/nr-point.h>
+
+struct NRPointL {
+ NR::ICoord x, y;
+};
+
+namespace NR {
+
+class IPoint {
+public:
+ IPoint()
+ { }
+
+ IPoint(ICoord x, ICoord y) {
+ _pt[X] = x;
+ _pt[Y] = y;
+ }
+
+ IPoint(NRPointL const &p) {
+ _pt[X] = p.x;
+ _pt[Y] = p.y;
+ }
+
+ IPoint(IPoint const &p) {
+ for (unsigned i = 0; i < 2; ++i) {
+ _pt[i] = p._pt[i];
+ }
+ }
+
+ IPoint &operator=(IPoint const &p) {
+ for (unsigned i = 0; i < 2; ++i) {
+ _pt[i] = p._pt[i];
+ }
+ return *this;
+ }
+
+ operator Point() {
+ return Point(_pt[X], _pt[Y]);
+ }
+
+ ICoord operator[](unsigned i) const throw(std::out_of_range) {
+ if ( i > Y ) {
+ throw std::out_of_range("index out of range");
+ }
+ return _pt[i];
+ }
+
+ ICoord &operator[](unsigned i) throw(std::out_of_range) {
+ if ( i > Y ) {
+ throw std::out_of_range("index out of range");
+ }
+ return _pt[i];
+ }
+
+ ICoord operator[](Dim2 d) const throw() { return _pt[d]; }
+ ICoord &operator[](Dim2 d) throw() { return _pt[d]; }
+
+ IPoint &operator+=(IPoint const &o) {
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ _pt[i] += o._pt[i];
+ }
+ return *this;
+ }
+
+ IPoint &operator-=(IPoint const &o) {
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ _pt[i] -= o._pt[i];
+ }
+ return *this;
+ }
+
+private:
+ ICoord _pt[2];
+};
+
+
+} // namespace NR
+
+#endif /* !SEEN_NR_POINT_L_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-point-matrix-ops.h b/src/libnr/nr-point-matrix-ops.h
new file mode 100644
index 000000000..58e06fc6d
--- /dev/null
+++ b/src/libnr/nr-point-matrix-ops.h
@@ -0,0 +1,47 @@
+/** \file operator functions over (NR::Point, NR::Matrix). */
+#ifndef SEEN_NR_POINT_MATRIX_OPS_H
+#define SEEN_NR_POINT_MATRIX_OPS_H
+
+#include "libnr/nr-point.h"
+#include "libnr/nr-matrix.h"
+
+namespace NR {
+
+inline Point operator*(Point const &v, Matrix const &m)
+{
+#if 1 /* Which code makes it easier to see what's happening? */
+ NR::Point const xform_col0(m[0],
+ m[2]);
+ NR::Point const xform_col1(m[1],
+ m[3]);
+ NR::Point const xlate(m[4], m[5]);
+ return ( Point(dot(v, xform_col0),
+ dot(v, xform_col1))
+ + xlate );
+#else
+ return Point(v[X] * m[0] + v[Y] * m[2] + m[4],
+ v[X] * m[1] + v[Y] * m[3] + m[5]);
+#endif
+}
+
+inline Point &Point::operator*=(Matrix const &m)
+{
+ *this = *this * m;
+ return *this;
+}
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_POINT_MATRIX_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-point-ops.h b/src/libnr/nr-point-ops.h
new file mode 100644
index 000000000..03d61fb15
--- /dev/null
+++ b/src/libnr/nr-point-ops.h
@@ -0,0 +1,88 @@
+/* operator functions for NR::Point. */
+#ifndef SEEN_NR_POINT_OPS_H
+#define SEEN_NR_POINT_OPS_H
+
+#include <libnr/nr-point.h>
+
+namespace NR {
+
+inline Point operator+(Point const &a, Point const &b)
+{
+ Point ret;
+ for (int i = 0; i < 2; i++) {
+ ret[i] = a[i] + b[i];
+ }
+ return ret;
+}
+
+inline Point operator-(Point const &a, Point const &b)
+{
+ Point ret;
+ for (int i = 0; i < 2; i++) {
+ ret[i] = a[i] - b[i];
+ }
+ return ret;
+}
+
+/** This is a rotation (sort of). */
+inline Point operator^(Point const &a, Point const &b)
+{
+ Point const ret(a[0] * b[0] - a[1] * b[1],
+ a[1] * b[0] + a[0] * b[1]);
+ return ret;
+}
+
+inline Point operator-(Point const &a)
+{
+ Point ret;
+ for(unsigned i = 0; i < 2; i++) {
+ ret[i] = -a[i];
+ }
+ return ret;
+}
+
+inline Point operator*(double const s, Point const &b)
+{
+ Point ret;
+ for(int i = 0; i < 2; i++) {
+ ret[i] = s * b[i];
+ }
+ return ret;
+}
+
+inline Point operator/(Point const &b, double const d)
+{
+ Point ret;
+ for(int i = 0; i < 2; i++) {
+ ret[i] = b[i] / d;
+ }
+ return ret;
+}
+
+
+inline bool operator==(Point const &a, Point const &b)
+{
+ return ( ( a[X] == b[X] ) && ( a[Y] == b[Y] ) );
+}
+
+inline bool operator!=(Point const &a, Point const &b)
+{
+ return ( ( a[X] != b[X] ) || ( a[Y] != b[Y] ) );
+}
+
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_POINT_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-point.h b/src/libnr/nr-point.h
new file mode 100644
index 000000000..f67a66800
--- /dev/null
+++ b/src/libnr/nr-point.h
@@ -0,0 +1,159 @@
+#ifndef SEEN_NR_POINT_H
+#define SEEN_NR_POINT_H
+
+/** \file
+ * Cartesian point class.
+ */
+
+//#include <math.h>
+//#include <stdexcept>
+#include <iostream>
+//#include <iomanip>
+
+#include <libnr/nr-coord.h>
+#include <libnr/nr-dim2.h>
+
+//#include "round.h"
+#include "decimal-round.h"
+
+/// A NRPoint consists of x and y coodinates.
+/// \todo
+/// This class appears to be obsoleted out in favour of NR::Point.
+struct NRPoint {
+ NR::Coord x, y;
+};
+
+namespace NR {
+
+class Matrix;
+
+/// Cartesian point.
+class Point {
+public:
+ inline Point()
+ { _pt[X] = _pt[Y] = 0; }
+
+ inline Point(Coord x, Coord y) {
+ _pt[X] = x;
+ _pt[Y] = y;
+ }
+
+ inline Point(NRPoint const &p) {
+ _pt[X] = p.x;
+ _pt[Y] = p.y;
+ }
+
+ inline Point(Point const &p) {
+ for (unsigned i = 0; i < 2; ++i) {
+ _pt[i] = p._pt[i];
+ }
+ }
+
+ inline Point &operator=(Point const &p) {
+ for (unsigned i = 0; i < 2; ++i) {
+ _pt[i] = p._pt[i];
+ }
+ return *this;
+ }
+
+ inline Coord operator[](unsigned i) const {
+ return _pt[i];
+ }
+
+ inline Coord &operator[](unsigned i) {
+ return _pt[i];
+ }
+
+ Coord operator[](Dim2 d) const throw() { return _pt[d]; }
+ Coord &operator[](Dim2 d) throw() { return _pt[d]; }
+
+ /** Return a point like this point but rotated -90 degrees.
+ (If the y axis grows downwards and the x axis grows to the
+ right, then this is 90 degrees counter-clockwise.)
+ **/
+ Point ccw() const {
+ return Point(_pt[Y], -_pt[X]);
+ }
+
+ /** Return a point like this point but rotated +90 degrees.
+ (If the y axis grows downwards and the x axis grows to the
+ right, then this is 90 degrees clockwise.)
+ **/
+ Point cw() const {
+ return Point(-_pt[Y], _pt[X]);
+ }
+
+ /**
+ \brief A function to lower the precision of the point
+ \param places The number of decimal places that should be in
+ the final number.
+ */
+ inline void round (int places = 0) {
+ _pt[X] = (Coord)(Inkscape::decimal_round((double)_pt[X], places));
+ _pt[Y] = (Coord)(Inkscape::decimal_round((double)_pt[Y], places));
+ return;
+ }
+
+ void normalize();
+
+ inline Point &operator+=(Point const &o) {
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ _pt[i] += o._pt[i];
+ }
+ return *this;
+ }
+
+ inline Point &operator-=(Point const &o) {
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ _pt[i] -= o._pt[i];
+ }
+ return *this;
+ }
+
+ inline Point &operator/=(double const s) {
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ _pt[i] /= s;
+ }
+ return *this;
+ }
+
+ inline Point &operator*=(double const s) {
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ _pt[i] *= s;
+ }
+ return *this;
+ }
+
+ Point &operator*=(Matrix const &m);
+
+ inline int operator == (const Point &in_pnt) {
+ return ((_pt[X] == in_pnt[X]) && (_pt[Y] == in_pnt[Y]));
+ }
+
+ friend inline std::ostream &operator<< (std::ostream &out_file, const NR::Point &in_pnt);
+
+private:
+ Coord _pt[2];
+};
+
+/** A function to print out the Point. It just prints out the coords
+ on the given output stream */
+inline std::ostream &operator<< (std::ostream &out_file, const NR::Point &in_pnt) {
+ out_file << "X: " << in_pnt[X] << " Y: " << in_pnt[Y];
+ return out_file;
+}
+
+} /* namespace NR */
+
+#endif /* !SEEN_NR_POINT_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rect-l.cpp b/src/libnr/nr-rect-l.cpp
new file mode 100644
index 000000000..40db67a1e
--- /dev/null
+++ b/src/libnr/nr-rect-l.cpp
@@ -0,0 +1,22 @@
+#include <libnr/nr-rect-l.h>
+
+namespace NR {
+
+IRect::IRect(Rect const &r) :
+ _min(int(floor(r.min()[X])), int(floor(r.min()[Y]))),
+ _max(int(ceil(r.min()[X])), int(ceil(r.min()[Y])))
+{
+}
+
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rect-l.h b/src/libnr/nr-rect-l.h
new file mode 100644
index 000000000..756a537b0
--- /dev/null
+++ b/src/libnr/nr-rect-l.h
@@ -0,0 +1,131 @@
+#ifndef SEEN_NR_RECT_L_H
+#define SEEN_NR_RECT_L_H
+
+#include <libnr/nr-i-coord.h>
+
+struct NRRectL {
+ NR::ICoord x0, y0, x1, y1;
+};
+
+#include <libnr/nr-rect.h>
+#include <libnr/nr-point-l.h>
+
+namespace NR {
+
+
+class IRect {
+public:
+ IRect(const NRRectL& r) : _min(r.x0, r.y0), _max(r.x1, r.y1) {}
+ IRect(const IRect& r) : _min(r._min), _max(r._max) {}
+ IRect(const IPoint &p0, const IPoint &p1);
+
+ /** as not all Rects are representable by IRects this gives the smallest IRect that contains
+ * r. */
+ IRect(const Rect& r);
+
+ operator Rect() {
+ return Rect(Point(_min), Point(_max));
+ }
+
+ const IPoint &min() const { return _min; }
+ const IPoint &max() const { return _max; }
+
+ /** returns a vector from min to max. */
+ IPoint dimensions() const;
+
+ /** does this rectangle have zero area? */
+ bool isEmpty() const {
+ return isEmpty<X>() && isEmpty<Y>();
+ }
+
+ bool intersects(const IRect &r) const {
+ return intersects<X>(r) && intersects<Y>(r);
+ }
+ bool contains(const IRect &r) const {
+ return contains<X>(r) && contains<Y>(r);
+ }
+ bool contains(const IPoint &p) const {
+ return contains<X>(p) && contains<Y>(p);
+ }
+
+ ICoord maxExtent() const {
+ return MAX(extent<X>(), extent<Y>());
+ }
+
+ ICoord extent(Dim2 axis) const {
+ switch (axis) {
+ case X: return extent<X>();
+ case Y: return extent<Y>();
+ };
+ }
+
+ ICoord extent(unsigned i) const throw(std::out_of_range) {
+ switch (i) {
+ case 0: return extent<X>();
+ case 1: return extent<Y>();
+ default: throw std::out_of_range("Dimension out of range");
+ };
+ }
+
+ /** Translates the rectangle by p. */
+ void offset(IPoint p);
+
+ /** Makes this rectangle large enough to include the point p. */
+ void expandTo(IPoint p);
+
+ /** Makes this rectangle large enough to include the rectangle r. */
+ void expandTo(const IRect &r);
+
+ /** Returns the set of points shared by both rectangles. */
+ static Maybe<IRect> intersection(const IRect &a, const IRect &b);
+
+ /** Returns the smallest rectangle that encloses both rectangles. */
+ static IRect union_bounds(const IRect &a, const IRect &b);
+
+private:
+ IRect() {}
+
+ template <NR::Dim2 axis>
+ ICoord extent() const {
+ return _max[axis] - _min[axis];
+ }
+
+ template <Dim2 axis>
+ bool isEmpty() const {
+ return !( _min[axis] < _max[axis] );
+ }
+
+ template <Dim2 axis>
+ bool intersects(const IRect &r) const {
+ return _max[axis] >= r._min[axis] && _min[axis] <= r._max[axis];
+ }
+
+ template <Dim2 axis>
+ bool contains(const IRect &r) const {
+ return contains(r._min) && contains(r._max);
+ }
+
+ template <Dim2 axis>
+ bool contains(const IPoint &p) const {
+ return p[axis] >= _min[axis] && p[axis] <= _max[axis];
+ }
+
+ IPoint _min, _max;
+};
+
+
+
+} // namespace NR
+
+#endif /* !SEEN_NR_RECT_L_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rect-ops.h b/src/libnr/nr-rect-ops.h
new file mode 100644
index 000000000..870091a94
--- /dev/null
+++ b/src/libnr/nr-rect-ops.h
@@ -0,0 +1,51 @@
+#ifndef SEEN_NR_RECT_OPS_H
+#define SEEN_NR_RECT_OPS_H
+
+/*
+ * Rect operators
+ *
+ * Copyright 2004 MenTaLguY <mental@rydia.net>,
+ * bulia byak <buliabyak@users.sf.net>
+ *
+ * This code is licensed under the GNU GPL; see COPYING for more information.
+ */
+
+#include <libnr/nr-rect.h>
+
+namespace NR {
+
+inline Rect expand(Rect const &r, double by) {
+ NR::Point const p(by, by);
+ return Rect(r.min() + p, r.max() - p);
+}
+
+inline Rect expand(Rect const &r, NR::Point by) {
+ return Rect(r.min() + by, r.max() - by);
+}
+
+#if 0
+inline ConvexHull operator*(Rect const &r, Matrix const &m) {
+ /* FIXME: no mention of m. Should probably be made non-inline. */
+ ConvexHull points(r.corner(0));
+ for ( unsigned i = 1 ; i < 4 ; i++ ) {
+ points.add(r.corner(i));
+ }
+ return points;
+}
+#endif
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_RECT_OPS_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 :
diff --git a/src/libnr/nr-rect.cpp b/src/libnr/nr-rect.cpp
new file mode 100644
index 000000000..0a8287bde
--- /dev/null
+++ b/src/libnr/nr-rect.cpp
@@ -0,0 +1,233 @@
+#define __NR_RECT_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include "nr-rect-l.h"
+
+/**
+ * \param r0 Rectangle.
+ * \param r1 Another rectangle.
+ * \param d Filled in with the intersection of r0 and r1.
+ * \return d.
+ */
+
+NRRectL *nr_rect_l_intersect(NRRectL *d, const NRRectL *r0, const NRRectL *r1)
+{
+ NR::ICoord t;
+ t = std::max(r0->x0, r1->x0);
+ d->x1 = std::min(r0->x1, r1->x1);
+ d->x0 = t;
+ t = std::max(r0->y0, r1->y0);
+ d->y1 = std::min(r0->y1, r1->y1);
+ d->y0 = t;
+
+ return d;
+}
+
+NRRect *
+nr_rect_d_intersect (NRRect *d, const NRRect *r0, const NRRect *r1)
+{
+ NR::Coord t;
+ t = MAX (r0->x0, r1->x0);
+ d->x1 = MIN (r0->x1, r1->x1);
+ d->x0 = t;
+ t = MAX (r0->y0, r1->y0);
+ d->y1 = MIN (r0->y1, r1->y1);
+ d->y0 = t;
+
+ return d;
+}
+
+NRRect *
+nr_rect_d_union (NRRect *d, const NRRect *r0, const NRRect *r1)
+{
+ if (NR_RECT_DFLS_TEST_EMPTY (r0)) {
+ if (NR_RECT_DFLS_TEST_EMPTY (r1)) {
+ nr_rect_d_set_empty (d);
+ } else {
+ *d = *r1;
+ }
+ } else {
+ if (NR_RECT_DFLS_TEST_EMPTY (r1)) {
+ *d = *r0;
+ } else {
+ NR::Coord t;
+ t = MIN (r0->x0, r1->x0);
+ d->x1 = MAX (r0->x1, r1->x1);
+ d->x0 = t;
+ t = MIN (r0->y0, r1->y0);
+ d->y1 = MAX (r0->y1, r1->y1);
+ d->y0 = t;
+ }
+ }
+ return d;
+}
+
+NRRectL *
+nr_rect_l_union (NRRectL *d, const NRRectL *r0, const NRRectL *r1)
+{
+ if (NR_RECT_DFLS_TEST_EMPTY (r0)) {
+ if (NR_RECT_DFLS_TEST_EMPTY (r1)) {
+ nr_rect_l_set_empty (d);
+ } else {
+ *d = *r1;
+ }
+ } else {
+ if (NR_RECT_DFLS_TEST_EMPTY (r1)) {
+ *d = *r0;
+ } else {
+ NR::ICoord t;
+ t = MIN (r0->x0, r1->x0);
+ d->x1 = MAX (r0->x1, r1->x1);
+ d->x0 = t;
+ t = MIN (r0->y0, r1->y0);
+ d->y1 = MAX (r0->y1, r1->y1);
+ d->y0 = t;
+ }
+ }
+ return d;
+}
+
+NRRect *
+nr_rect_union_pt(NRRect *dst, NR::Point const &p)
+{
+ using NR::X;
+ using NR::Y;
+
+ return nr_rect_d_union_xy(dst, p[X], p[Y]);
+}
+
+NRRect *
+nr_rect_d_union_xy (NRRect *d, NR::Coord x, NR::Coord y)
+{
+ if ((d->x0 <= d->x1) && (d->y0 <= d->y1)) {
+ d->x0 = MIN (d->x0, x);
+ d->y0 = MIN (d->y0, y);
+ d->x1 = MAX (d->x1, x);
+ d->y1 = MAX (d->y1, y);
+ } else {
+ d->x0 = d->x1 = x;
+ d->y0 = d->y1 = y;
+ }
+ return d;
+}
+
+NRRect *
+nr_rect_d_matrix_transform(NRRect *d, NRRect const *const s, NR::Matrix const &m)
+{
+ using NR::X;
+ using NR::Y;
+
+ if (nr_rect_d_test_empty(s)) {
+ nr_rect_d_set_empty(d);
+ } else {
+ NR::Point const c00(NR::Point(s->x0, s->y0) * m);
+ NR::Point const c01(NR::Point(s->x0, s->y1) * m);
+ NR::Point const c10(NR::Point(s->x1, s->y0) * m);
+ NR::Point const c11(NR::Point(s->x1, s->y1) * m);
+ d->x0 = std::min(std::min(c00[X], c01[X]),
+ std::min(c10[X], c11[X]));
+ d->y0 = std::min(std::min(c00[Y], c01[Y]),
+ std::min(c10[Y], c11[Y]));
+ d->x1 = std::max(std::max(c00[X], c01[X]),
+ std::max(c10[X], c11[X]));
+ d->y1 = std::max(std::max(c00[Y], c01[Y]),
+ std::max(c10[Y], c11[Y]));
+ }
+ return d;
+}
+
+NRRect *
+nr_rect_d_matrix_transform(NRRect *d, NRRect const *s, NRMatrix const *m)
+{
+ return nr_rect_d_matrix_transform(d, s, *m);
+}
+
+namespace NR {
+
+Rect::Rect(const Point &p0, const Point &p1)
+: _min(MIN(p0[X], p1[X]), MIN(p0[Y], p1[Y])),
+ _max(MAX(p0[X], p1[X]), MAX(p0[Y], p1[Y])) {}
+
+/** returns the four corners of the rectangle in the correct winding order */
+Point Rect::corner(unsigned i) const {
+ switch (i % 4) {
+ case 0:
+ return _min;
+ case 1:
+ return Point(_max[X], _min[Y]);
+ case 2:
+ return _max;
+ default: /* i.e. 3 */
+ return Point(_min[X], _max[Y]);
+ }
+}
+
+/** returns the midpoint of this rectangle */
+Point Rect::midpoint() const {
+ return ( _min + _max ) / 2;
+}
+
+/** returns a vector from topleft to bottom right. */
+Point Rect::dimensions() const {
+ return _max - _min;
+}
+
+/** Translates the rectangle by p. */
+void Rect::offset(Point p) {
+ _min += p;
+ _max += p;
+}
+
+/** Makes this rectangle large enough to include the point p. */
+void Rect::expandTo(Point p) {
+ for ( int i=0 ; i < 2 ; i++ ) {
+ _min[i] = MIN(_min[i], p[i]);
+ _max[i] = MAX(_max[i], p[i]);
+ }
+}
+
+/** Returns the set of points shared by both rectangles. */
+Maybe<Rect> Rect::intersection(const Rect &a, const Rect &b) {
+ Rect r;
+ for ( int i=0 ; i < 2 ; i++ ) {
+ r._min[i] = MAX(a._min[i], b._min[i]);
+ r._max[i] = MIN(a._max[i], b._max[i]);
+
+ if ( r._min[i] > r._max[i] ) {
+ return Nothing();
+ }
+ }
+ return r;
+}
+
+/** returns the smallest rectangle containing both rectangles */
+Rect Rect::union_bounds(const Rect &a, const Rect &b) {
+ Rect r;
+ for ( int i=0; i < 2 ; i++ ) {
+ r._min[i] = MIN(a._min[i], b._min[i]);
+ r._max[i] = MAX(a._max[i], b._max[i]);
+ }
+ return r;
+}
+
+} // namespace NR
+
+
+/*
+ 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/libnr/nr-rect.h b/src/libnr/nr-rect.h
new file mode 100644
index 000000000..219fbd1ac
--- /dev/null
+++ b/src/libnr/nr-rect.h
@@ -0,0 +1,255 @@
+#ifndef LIBNR_NR_RECT_H_SEEN
+#define LIBNR_NR_RECT_H_SEEN
+
+/** \file
+ * Definitions of NRRect and NR::Rect types, and some associated functions \& macros.
+ */
+/*
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * MenTaLguY <mental@rydia.net>
+ *
+ * This code is in public domain
+ */
+
+
+#include <stdexcept>
+
+#include "libnr/nr-values.h"
+#include <libnr/nr-coord.h>
+#include <libnr/nr-i-coord.h>
+#include <libnr/nr-dim2.h>
+#include <libnr/nr-point.h>
+#include <libnr/nr-maybe.h>
+#include <libnr/nr-point-matrix-ops.h>
+
+struct NRMatrix;
+namespace NR {
+ struct Matrix;
+}
+
+/* NULL rect is infinite */
+
+struct NRRect {
+ NR::Coord x0, y0, x1, y1;
+};
+
+inline bool empty(NRRect const &r)
+{
+ return ( ( r.x0 > r.x1 ) ||
+ ( r.y0 > r.y1 ) );
+}
+
+#define nr_rect_d_set_empty(r) (*(r) = NR_RECT_EMPTY)
+#define nr_rect_l_set_empty(r) (*(r) = NR_RECT_L_EMPTY)
+
+#define nr_rect_d_test_empty(r) ((r) && NR_RECT_DFLS_TEST_EMPTY(r))
+#define nr_rect_l_test_empty(r) ((r) && NR_RECT_DFLS_TEST_EMPTY(r))
+
+#define nr_rect_d_test_intersect(r0,r1) \
+ (!nr_rect_d_test_empty(r0) && !nr_rect_d_test_empty(r1) && \
+ !((r0) && (r1) && !NR_RECT_DFLS_TEST_INTERSECT(r0, r1)))
+#define nr_rect_l_test_intersect(r0,r1) \
+ (!nr_rect_l_test_empty(r0) && !nr_rect_l_test_empty(r1) && \
+ !((r0) && (r1) && !NR_RECT_DFLS_TEST_INTERSECT(r0, r1)))
+
+#define nr_rect_d_point_d_test_inside(r,p) ((p) && (!(r) || (!NR_RECT_DF_TEST_EMPTY(r) && NR_RECT_DF_POINT_DF_TEST_INSIDE(r,p))))
+
+/* NULL values are OK for r0 and r1, but not for d */
+NRRect *nr_rect_d_intersect(NRRect *d, NRRect const *r0, NRRect const *r1);
+NRRectL *nr_rect_l_intersect(NRRectL *d, NRRectL const *r0, NRRectL const *r1);
+
+NRRect *nr_rect_d_union(NRRect *d, NRRect const *r0, NRRect const *r1);
+NRRectL *nr_rect_l_union(NRRectL *d, NRRectL const *r0, NRRectL const *r1);
+
+NRRect *nr_rect_union_pt(NRRect *dst, NR::Point const &p);
+NRRect *nr_rect_d_union_xy(NRRect *d, NR::Coord x, NR::Coord y);
+NRRectL *nr_rect_l_union_xy(NRRectL *d, NR::ICoord x, NR::ICoord y);
+
+NRRect *nr_rect_d_matrix_transform(NRRect *d, NRRect const *s, NR::Matrix const &m);
+NRRect *nr_rect_d_matrix_transform(NRRect *d, NRRect const *s, NRMatrix const *m);
+
+namespace NR {
+
+/** A rectangle is always aligned to the X and Y axis. This means it
+ * can be defined using only 4 coordinates, and determining
+ * intersection is very efficient. The points inside a rectangle are
+ * min[dim] <= _pt[dim] <= max[dim]. Emptiness, however, is defined
+ * as having zero area, meaning an empty rectangle may still contain
+ * points. Infinities are also permitted. */
+class Rect {
+public:
+ Rect(NRRect const &r) : _min(r.x0, r.y0), _max(r.x1, r.y1) {}
+ Rect(Rect const &r) : _min(r._min), _max(r._max) {}
+ Rect(Point const &p0, Point const &p1);
+
+ Point const &min() const { return _min; }
+ Point const &max() const { return _max; }
+
+ /** returns the four corners of the rectangle in order
+ * (clockwise if +Y is up, anticlockwise if +Y is down) */
+ Point corner(unsigned i) const;
+
+ /** returns a vector from min to max. */
+ Point dimensions() const;
+
+ /** returns the midpoint of this rect. */
+ Point midpoint() const;
+
+ /** does this rectangle have zero area? */
+ bool isEmpty() const {
+ return isEmpty<X>() || isEmpty<Y>();
+ }
+
+ bool intersects(Rect const &r) const {
+ return intersects<X>(r) && intersects<Y>(r);
+ }
+ bool contains(Rect const &r) const {
+ return contains<X>(r) && contains<Y>(r);
+ }
+ bool contains(Point const &p) const {
+ return contains<X>(p) && contains<Y>(p);
+ }
+
+ double area() const {
+ return extent<X>() * extent<Y>();
+ }
+
+ double maxExtent() const {
+ return MAX(extent<X>(), extent<Y>());
+ }
+
+ double extent(Dim2 const axis) const {
+ switch (axis) {
+ case X: return extent<X>();
+ case Y: return extent<Y>();
+ default: g_error("invalid axis value %d", (int) axis); return 0;
+ };
+ }
+
+ double extent(unsigned i) const throw(std::out_of_range) {
+ switch (i) {
+ case 0: return extent<X>();
+ case 1: return extent<Y>();
+ default: throw std::out_of_range("Dimension out of range");
+ };
+ }
+
+ /**
+ \brief Remove some precision from the Rect
+ \param places The number of decimal places left in the end
+
+ This function just calls round on the \c _min and \c _max points.
+ */
+ inline void round(int places = 0) {
+ _min.round(places);
+ _max.round(places);
+ return;
+ }
+
+ /** Translates the rectangle by p. */
+ void offset(Point p);
+
+ /** Makes this rectangle large enough to include the point p. */
+ void expandTo(Point p);
+
+ /** Makes this rectangle large enough to include the rectangle r. */
+ void expandTo(Rect const &r);
+
+ inline void move_left (gdouble by) {
+ _min[NR::X] += by;
+ }
+ inline void move_right (gdouble by) {
+ _max[NR::X] += by;
+ }
+ inline void move_top (gdouble by) {
+ _min[NR::Y] += by;
+ }
+ inline void move_bottom (gdouble by) {
+ _max[NR::Y] += by;
+ }
+
+ /** Returns the set of points shared by both rectangles. */
+ static Maybe<Rect> intersection(Rect const &a, Rect const &b);
+
+ /** Returns the smallest rectangle that encloses both rectangles. */
+ static Rect union_bounds(Rect const &a, Rect const &b);
+
+ /** Scales the rect by s, with origin at 0, 0 */
+ inline Rect operator*(double const s) const {
+ return Rect(s * min(), s * max());
+ }
+
+ /** Transforms the rect by m. Note that it gives correct results only for scales and translates */
+ inline Rect operator*(Matrix const m) const {
+ return Rect(_min * m, _max * m);
+ }
+
+ inline bool operator==(Rect const &in_rect) {
+ return ((this->min() == in_rect.min()) && (this->max() == in_rect.max()));
+ }
+
+ friend inline std::ostream &operator<<(std::ostream &out_file, NR::Rect const &in_rect);
+
+private:
+ Rect() {}
+
+ template <NR::Dim2 axis>
+ double extent() const {
+ return _max[axis] - _min[axis];
+ }
+
+ template <Dim2 axis>
+ bool isEmpty() const {
+ return !( _min[axis] < _max[axis] );
+ }
+
+ template <Dim2 axis>
+ bool intersects(Rect const &r) const {
+ return _max[axis] >= r._min[axis] && _min[axis] <= r._max[axis];
+ }
+
+ template <Dim2 axis>
+ bool contains(Rect const &r) const {
+ return contains(r._min) && contains(r._max);
+ }
+
+ template <Dim2 axis>
+ bool contains(Point const &p) const {
+ return p[axis] >= _min[axis] && p[axis] <= _max[axis];
+ }
+
+ Point _min, _max;
+
+ /* evil, but temporary */
+ friend class Maybe<Rect>;
+};
+
+/** A function to print out the rectange if sent to an output
+ stream. */
+inline std::ostream
+&operator<<(std::ostream &out_file, NR::Rect const &in_rect)
+{
+ out_file << "Rectangle:\n";
+ out_file << "\tMin Point -> " << in_rect.min() << "\n";
+ out_file << "\tMax Point -> " << in_rect.max() << "\n";
+
+ return out_file;
+}
+
+} /* namespace NR */
+
+
+#endif /* !LIBNR_NR_RECT_H_SEEN */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-render.h b/src/libnr/nr-render.h
new file mode 100644
index 000000000..84215b7a3
--- /dev/null
+++ b/src/libnr/nr-render.h
@@ -0,0 +1,25 @@
+#ifndef __NR_RENDER_H__
+#define __NR_RENDER_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixblock.h>
+
+struct NRRenderer;
+
+typedef void (* NRRenderFunc) (NRRenderer *r, NRPixBlock *pb, NRPixBlock *m);
+
+struct NRRenderer {
+ NRRenderFunc render;
+};
+
+#define nr_render(r,pb,m) ((NRRenderer *) (r))->render ((NRRenderer *) (r), (pb), (m))
+
+#endif
diff --git a/src/libnr/nr-rotate-fns-test.cpp b/src/libnr/nr-rotate-fns-test.cpp
new file mode 100644
index 000000000..45b2450e0
--- /dev/null
+++ b/src/libnr/nr-rotate-fns-test.cpp
@@ -0,0 +1,43 @@
+#include <cmath>
+#include <glib/gmacros.h>
+
+#include <libnr/nr-rotate-fns.h>
+#include <utest/utest.h>
+
+int main(int argc, char *argv[])
+{
+ utest_start("rotate-fns");
+
+ UTEST_TEST("rotate_degrees") {
+ double const d[] = {
+ 0, 90, 180, 270, 360, 45, 45.01, 44.99, 134, 135, 136, 314, 315, 317, 359, 361
+ };
+ for (unsigned i = 0; i < G_N_ELEMENTS(d); ++i) {
+ double const degrees = d[i];
+ NR::rotate const rot(rotate_degrees(degrees));
+ NR::rotate const rot_approx( M_PI * ( degrees / 180. ) );
+ UTEST_ASSERT(rotate_equalp(rot, rot_approx, 1e-12));
+
+ NR::rotate const rot_inv(rotate_degrees(-degrees));
+ NR::rotate const rot_compl(rotate_degrees(360 - degrees));
+ UTEST_ASSERT(rotate_equalp(rot_inv, rot_compl, 1e-12));
+
+ UTEST_ASSERT(!rotate_equalp(rot, rotate_degrees(degrees + 1), 1e-5));
+ }
+ }
+
+ return ( utest_end()
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE );
+}
+
+/*
+ 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/libnr/nr-rotate-fns-test.h b/src/libnr/nr-rotate-fns-test.h
new file mode 100644
index 000000000..e3bfe3043
--- /dev/null
+++ b/src/libnr/nr-rotate-fns-test.h
@@ -0,0 +1,54 @@
+#include <cxxtest/TestSuite.h>
+
+#include <cmath>
+#include <glib/gmacros.h>
+
+#include <libnr/nr-rotate-fns.h>
+
+class NrRotateFnsTest : public CxxTest::TestSuite
+{
+public:
+
+ NrRotateFnsTest()
+ {
+ }
+ virtual ~NrRotateFnsTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static NrRotateFnsTest *createSuite() { return new NrRotateFnsTest(); }
+ static void destroySuite( NrRotateFnsTest *suite ) { delete suite; }
+
+
+
+ void testRotateDegrees(void)
+ {
+ double const d[] = {
+ 0, 90, 180, 270, 360, 45, 45.01, 44.99, 134, 135, 136, 314, 315, 317, 359, 361
+ };
+ for ( unsigned i = 0; i < G_N_ELEMENTS(d); ++i ) {
+ double const degrees = d[i];
+ NR::rotate const rot(rotate_degrees(degrees));
+ NR::rotate const rot_approx( M_PI * ( degrees / 180. ) );
+ TS_ASSERT( rotate_equalp(rot, rot_approx, 1e-12) );
+
+ NR::rotate const rot_inv(rotate_degrees(-degrees));
+ NR::rotate const rot_compl(rotate_degrees(360 - degrees));
+ TS_ASSERT( rotate_equalp(rot_inv, rot_compl, 1e-12) );
+
+ TS_ASSERT( !rotate_equalp(rot, rotate_degrees(degrees + 1), 1e-5) );
+ }
+ }
+
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rotate-fns.cpp b/src/libnr/nr-rotate-fns.cpp
new file mode 100644
index 000000000..f2669c20c
--- /dev/null
+++ b/src/libnr/nr-rotate-fns.cpp
@@ -0,0 +1,66 @@
+/** \file
+ * Functions to/from NR::rotate.
+ */
+#include <glib.h>
+#include "libnr/nr-rotate-ops.h"
+#include "libnr/nr-rotate-fns.h"
+
+/**
+ * Returns a rotation matrix corresponding by the specified angle about the origin.
+ *
+ * Angle direction in Inkscape code: If you use the traditional mathematics convention that y
+ * increases upwards, then positive angles are anticlockwise as per the mathematics convention. If
+ * you take the common non-mathematical convention that y increases downwards, then positive angles
+ * are clockwise, as is common outside of mathematics.
+ */
+NR::rotate
+rotate_degrees(double degrees)
+{
+ if (degrees < 0) {
+ return rotate_degrees(-degrees).inverse();
+ }
+
+ double const degrees0 = degrees;
+ if (degrees >= 360) {
+ degrees = fmod(degrees, 360);
+ }
+
+ NR::rotate ret(1., 0.);
+
+ if (degrees >= 180) {
+ NR::rotate const rot180(-1., 0.);
+ degrees -= 180;
+ ret = rot180;
+ }
+
+ if (degrees >= 90) {
+ NR::rotate const rot90(0., 1.);
+ degrees -= 90;
+ ret *= rot90;
+ }
+
+ if (degrees == 45) {
+ NR::rotate const rot45(M_SQRT1_2, M_SQRT1_2);
+ ret *= rot45;
+ } else {
+ double const radians = M_PI * ( degrees / 180 );
+ ret *= NR::rotate(cos(radians), sin(radians));
+ }
+
+ NR::rotate const raw_ret( M_PI * ( degrees0 / 180 ) );
+ g_return_val_if_fail(rotate_equalp(ret, raw_ret, 1e-8),
+ raw_ret);
+ return ret;
+}
+
+
+/*
+ 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/libnr/nr-rotate-fns.h b/src/libnr/nr-rotate-fns.h
new file mode 100644
index 000000000..bd075114c
--- /dev/null
+++ b/src/libnr/nr-rotate-fns.h
@@ -0,0 +1,29 @@
+#ifndef SEEN_NR_ROTATE_FNS_H
+#define SEEN_NR_ROTATE_FNS_H
+
+/** \file
+ * Declarations for rotation functions.
+ */
+
+#include <libnr/nr-rotate.h>
+#include <libnr/nr-point-fns.h>
+
+inline bool rotate_equalp(NR::rotate const &a, NR::rotate const &b, double const eps)
+{
+ return point_equalp(a.vec, b.vec, eps);
+}
+
+NR::rotate rotate_degrees(double degrees);
+
+#endif /* !SEEN_NR_ROTATE_FNS_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 :
diff --git a/src/libnr/nr-rotate-matrix-ops.cpp b/src/libnr/nr-rotate-matrix-ops.cpp
new file mode 100644
index 000000000..dd3851643
--- /dev/null
+++ b/src/libnr/nr-rotate-matrix-ops.cpp
@@ -0,0 +1,19 @@
+#include <libnr/nr-matrix-ops.h>
+
+NR::Matrix
+operator*(NR::rotate const &a, NR::Matrix const &b)
+{
+ return NR::Matrix(a) * b;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rotate-matrix-ops.h b/src/libnr/nr-rotate-matrix-ops.h
new file mode 100644
index 000000000..d2f0eadba
--- /dev/null
+++ b/src/libnr/nr-rotate-matrix-ops.h
@@ -0,0 +1,21 @@
+#ifndef SEEN_LIBNR_NR_ROTATE_MATRIX_OPS_H
+#define SEEN_LIBNR_NR_ROTATE_MATRIX_OPS_H
+
+#include <libnr/nr-forward.h>
+
+
+NR::Matrix operator*(NR::rotate const &a, NR::Matrix const &b);
+
+
+#endif /* !SEEN_LIBNR_NR_ROTATE_MATRIX_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rotate-ops.h b/src/libnr/nr-rotate-ops.h
new file mode 100644
index 000000000..4b60b9d0c
--- /dev/null
+++ b/src/libnr/nr-rotate-ops.h
@@ -0,0 +1,43 @@
+#ifndef SEEN_NR_ROTATE_OPS_H
+#define SEEN_NR_ROTATE_OPS_H
+#include <libnr/nr-rotate.h>
+
+namespace NR {
+
+inline Point operator*(Point const &v, rotate const &r)
+{
+ return Point(r.vec[X] * v[X] - r.vec[Y] * v[Y],
+ r.vec[Y] * v[X] + r.vec[X] * v[Y]);
+}
+
+inline rotate operator*(rotate const &a, rotate const &b)
+{
+ return rotate( a.vec * b );
+}
+
+inline rotate &rotate::operator*=(rotate const &b)
+{
+ *this = *this * b;
+ return *this;
+}
+
+inline rotate operator/(rotate const &numer, rotate const &denom)
+{
+ return numer * denom.inverse();
+}
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_ROTATE_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rotate-test.cpp b/src/libnr/nr-rotate-test.cpp
new file mode 100644
index 000000000..63b9d1737
--- /dev/null
+++ b/src/libnr/nr-rotate-test.cpp
@@ -0,0 +1,89 @@
+#include <cmath>
+#include <utest/utest.h>
+#include <libnr/nr-matrix.h>
+#include <libnr/nr-matrix-fns.h> /* identity, matrix_equalp */
+#include <libnr/nr-matrix-ops.h>
+#include <libnr/nr-point-fns.h>
+#include <libnr/nr-point-matrix-ops.h>
+#include <libnr/nr-rotate.h>
+#include <libnr/nr-rotate-fns.h>
+#include <libnr/nr-rotate-ops.h>
+using NR::X;
+using NR::Y;
+
+int main(int argc, char *argv[])
+{
+ utest_start("rotate");
+
+ NR::Matrix const m_id(NR::identity());
+ NR::rotate const r_id(0.0);
+ NR::rotate const rot234(.234);
+ UTEST_TEST("constructors, comparisons") {
+ UTEST_ASSERT( r_id == r_id );
+ UTEST_ASSERT( rot234 == rot234 );
+ UTEST_ASSERT( rot234 != r_id );
+ UTEST_ASSERT( r_id == NR::rotate(NR::Point(1.0, 0.0)) );
+ UTEST_ASSERT( NR::Matrix(r_id) == m_id );
+ UTEST_ASSERT( NR::Matrix(r_id).test_identity() );
+
+ UTEST_ASSERT(rotate_equalp(rot234, NR::rotate(NR::Point(cos(.234), sin(.234))), 1e-12));
+ }
+
+ UTEST_TEST("operator=") {
+ NR::rotate rot234_eq(r_id);
+ rot234_eq = rot234;
+ UTEST_ASSERT( rot234 == rot234_eq );
+ UTEST_ASSERT( rot234_eq != r_id );
+ }
+
+ UTEST_TEST("inverse") {
+ UTEST_ASSERT( r_id.inverse() == r_id );
+ UTEST_ASSERT( rot234.inverse() == NR::rotate(-.234) );
+ }
+
+ NR::Point const b(-2.0, 3.0);
+ NR::rotate const rot180(NR::Point(-1.0, 0.0));
+ UTEST_TEST("operator*(Point, rotate)") {
+ UTEST_ASSERT( b * r_id == b );
+ UTEST_ASSERT( b * rot180 == -b );
+ UTEST_ASSERT( b * rot234 == b * NR::Matrix(rot234) );
+ UTEST_ASSERT(point_equalp(b * NR::rotate(M_PI / 2),
+ NR::rot90(b),
+ 1e-14));
+ UTEST_ASSERT( b * rotate_degrees(90.) == NR::rot90(b) );
+ }
+
+ UTEST_TEST("operator*(rotate, rotate)") {
+ UTEST_ASSERT( r_id * r_id == r_id );
+ UTEST_ASSERT( rot180 * rot180 == r_id );
+ UTEST_ASSERT( rot234 * r_id == rot234 );
+ UTEST_ASSERT( r_id * rot234 == rot234 );
+ UTEST_ASSERT(rotate_equalp(rot234 * rot234.inverse(), r_id, 1e-14));
+ UTEST_ASSERT(rotate_equalp(rot234.inverse() * rot234, r_id, 1e-14));
+ UTEST_ASSERT(rotate_equalp(( NR::rotate(0.25) * NR::rotate(.5) ),
+ NR::rotate(.75),
+ 1e-10));
+ }
+
+ UTEST_TEST("operator/(rotate, rotate)") {
+ UTEST_ASSERT( rot234 / r_id == rot234 );
+ UTEST_ASSERT( rot234 / rot180 == rot234 * rot180 );
+ UTEST_ASSERT(rotate_equalp(rot234 / rot234, r_id, 1e-14));
+ UTEST_ASSERT(rotate_equalp(r_id / rot234, rot234.inverse(), 1e-14));
+ }
+
+ return ( utest_end()
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE );
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rotate-test.h b/src/libnr/nr-rotate-test.h
new file mode 100644
index 000000000..252b25022
--- /dev/null
+++ b/src/libnr/nr-rotate-test.h
@@ -0,0 +1,112 @@
+#include <cxxtest/TestSuite.h>
+
+#include <cmath>
+#include <libnr/nr-matrix.h>
+#include <libnr/nr-matrix-ops.h>
+#include <libnr/nr-matrix-fns.h> /* identity, matrix_equalp */
+#include <libnr/nr-point-fns.h>
+#include <libnr/nr-rotate.h>
+#include <libnr/nr-rotate-fns.h>
+#include <libnr/nr-rotate-ops.h>
+using NR::X;
+using NR::Y;
+
+
+class NrRotateTest : public CxxTest::TestSuite
+{
+public:
+
+ NrRotateTest() :
+ m_id( NR::identity() ),
+ r_id( 0.0 ),
+ rot234( .234 ),
+ b( -2.0, 3.0 ),
+ rot180( NR::Point(-1.0, 0.0) )
+ {
+ }
+ virtual ~NrRotateTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static NrRotateTest *createSuite() { return new NrRotateTest(); }
+ static void destroySuite( NrRotateTest *suite ) { delete suite; }
+
+ NR::Matrix const m_id;
+ NR::rotate const r_id;
+ NR::rotate const rot234;
+ NR::Point const b;
+ NR::rotate const rot180;
+
+
+
+
+ void testCtorsCompares(void)
+ {
+ TS_ASSERT_EQUALS( r_id, r_id );
+ TS_ASSERT_EQUALS( rot234, rot234 );
+ TS_ASSERT_DIFFERS( rot234, r_id );
+ TS_ASSERT_EQUALS( r_id, NR::rotate(NR::Point(1.0, 0.0)) );
+ TS_ASSERT_EQUALS( NR::Matrix(r_id), m_id );
+ TS_ASSERT( NR::Matrix(r_id).test_identity() );
+
+ TS_ASSERT(rotate_equalp(rot234, NR::rotate(NR::Point(cos(.234), sin(.234))), 1e-12));
+ }
+
+ void testAssignmentOp(void)
+ {
+ NR::rotate rot234_eq(r_id);
+ rot234_eq = rot234;
+ TS_ASSERT_EQUALS( rot234, rot234_eq );
+ TS_ASSERT_DIFFERS( rot234_eq, r_id );
+ }
+
+ void testInverse(void)
+ {
+ TS_ASSERT_EQUALS( r_id.inverse(), r_id );
+ TS_ASSERT_EQUALS( rot234.inverse(), NR::rotate(-.234) );
+ }
+
+ void testOpStarPointRotate(void)
+ {
+ TS_ASSERT_EQUALS( b * r_id, b );
+ TS_ASSERT_EQUALS( b * rot180, -b );
+ TS_ASSERT_EQUALS( b * rot234, b * NR::Matrix(rot234) );
+ TS_ASSERT(point_equalp(b * NR::rotate(M_PI / 2),
+ NR::rot90(b),
+ 1e-14));
+ TS_ASSERT_EQUALS( b * rotate_degrees(90.), NR::rot90(b) );
+ }
+
+ void testOpStarRotateRotate(void)
+ {
+ TS_ASSERT_EQUALS( r_id * r_id, r_id );
+ TS_ASSERT_EQUALS( rot180 * rot180, r_id );
+ TS_ASSERT_EQUALS( rot234 * r_id, rot234 );
+ TS_ASSERT_EQUALS( r_id * rot234, rot234 );
+ TS_ASSERT( rotate_equalp(rot234 * rot234.inverse(), r_id, 1e-14) );
+ TS_ASSERT( rotate_equalp(rot234.inverse() * rot234, r_id, 1e-14) );
+ TS_ASSERT( rotate_equalp(( NR::rotate(0.25) * NR::rotate(.5) ),
+ NR::rotate(.75),
+ 1e-10) );
+ }
+
+ void testOpDivRotateRotate(void)
+ {
+ TS_ASSERT_EQUALS( rot234 / r_id, rot234 );
+ TS_ASSERT_EQUALS( rot234 / rot180, rot234 * rot180 );
+ TS_ASSERT( rotate_equalp(rot234 / rot234, r_id, 1e-14) );
+ TS_ASSERT( rotate_equalp(r_id / rot234, rot234.inverse(), 1e-14) );
+ }
+
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-rotate.h b/src/libnr/nr-rotate.h
new file mode 100644
index 000000000..051372ce6
--- /dev/null
+++ b/src/libnr/nr-rotate.h
@@ -0,0 +1,66 @@
+#ifndef SEEN_NR_ROTATE_H
+#define SEEN_NR_ROTATE_H
+
+/** \file
+ * Rotation about the origin.
+ */
+
+#include <libnr/nr-point.h>
+#include <libnr/nr-point-fns.h>
+#include <libnr/nr-point-ops.h>
+
+namespace NR {
+
+/** Notionally an NR::Matrix corresponding to rotation about the origin.
+ Behaves like NR::Matrix for multiplication.
+**/
+class rotate {
+public:
+ Point vec;
+
+private:
+ rotate();
+
+public:
+ explicit rotate(Coord theta);
+ explicit rotate(Point const &p) : vec(p) {}
+ explicit rotate(Coord const x, Coord const y) : vec(x, y) {}
+
+ bool operator==(rotate const &o) const {
+ return vec == o.vec;
+ }
+
+ bool operator!=(rotate const &o) const {
+ return vec != o.vec;
+ }
+
+ inline rotate &operator*=(rotate const &b);
+ /* Defined in nr-rotate-ops.h. */
+
+ rotate inverse() const {
+ /** \todo
+ * In the usual case that vec is a unit vector (within rounding error),
+ * dividing by len_sq is either a noop or numerically harmful.
+ * Make a unit_rotate class (or the like) that knows its length is 1.
+ */
+ double const len_sq = dot(vec, vec);
+ return rotate( Point(vec[X], -vec[Y])
+ / len_sq );
+ }
+};
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_ROTATE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-scale-matrix-ops.cpp b/src/libnr/nr-scale-matrix-ops.cpp
new file mode 100644
index 000000000..ce5120079
--- /dev/null
+++ b/src/libnr/nr-scale-matrix-ops.cpp
@@ -0,0 +1,26 @@
+#include "libnr/nr-matrix-ops.h"
+
+NR::Matrix
+operator*(NR::scale const &s, NR::Matrix const &m)
+{
+ using NR::X; using NR::Y;
+ NR::Matrix ret(m);
+ ret[0] *= s[X];
+ ret[1] *= s[X];
+ ret[2] *= s[Y];
+ ret[3] *= s[Y];
+ assert_close( ret, NR::Matrix(s) * m );
+ return ret;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-scale-matrix-ops.h b/src/libnr/nr-scale-matrix-ops.h
new file mode 100644
index 000000000..bf1b498c9
--- /dev/null
+++ b/src/libnr/nr-scale-matrix-ops.h
@@ -0,0 +1,13 @@
+#ifndef SEEN_LIBNR_NR_SCALE_MATRIX_OPS_H
+#define SEEN_LIBNR_NR_SCALE_MATRIX_OPS_H
+/** \file
+ * Declarations (and definition if inline) of operator
+ * blah (NR::scale, NR::Matrix).
+ */
+
+#include "libnr/nr-forward.h"
+
+NR::Matrix operator*(NR::scale const &s, NR::Matrix const &m);
+
+
+#endif /* !SEEN_LIBNR_NR_SCALE_MATRIX_OPS_H */
diff --git a/src/libnr/nr-scale-ops.h b/src/libnr/nr-scale-ops.h
new file mode 100644
index 000000000..da1fea64c
--- /dev/null
+++ b/src/libnr/nr-scale-ops.h
@@ -0,0 +1,40 @@
+#ifndef SEEN_NR_SCALE_OPS_H
+#define SEEN_NR_SCALE_OPS_H
+
+#include <libnr/nr-scale.h>
+
+namespace NR {
+
+inline Point operator*(Point const &p, scale const &s)
+{
+ return Point(p[X] * s[X],
+ p[Y] * s[Y]);
+}
+
+inline scale operator*(scale const &a, scale const &b)
+{
+ return scale(a[X] * b[X],
+ a[Y] * b[Y]);
+}
+
+inline scale operator/(scale const &numer, scale const &denom)
+{
+ return scale(numer[X] / denom[X],
+ numer[Y] / denom[Y]);
+}
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_SCALE_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-scale-test.cpp b/src/libnr/nr-scale-test.cpp
new file mode 100644
index 000000000..cc255d02a
--- /dev/null
+++ b/src/libnr/nr-scale-test.cpp
@@ -0,0 +1,71 @@
+#include <utest/utest.h>
+#include <libnr/nr-scale.h>
+#include <libnr/nr-scale-ops.h>
+using NR::X;
+using NR::Y;
+
+int main(int argc, char *argv[])
+{
+ utest_start("NR::scale");
+
+ NR::scale const sa(1.5, 2.0);
+ UTEST_TEST("x,y constructor and operator[] const") {
+ UTEST_ASSERT(sa[X] == 1.5);
+ UTEST_ASSERT(sa[Y] == 2.0);
+ UTEST_ASSERT(sa[0u] == 1.5);
+ UTEST_ASSERT(sa[1u] == 2.0);
+ }
+
+ NR::Point const b(-2.0, 3.0);
+ NR::scale const sb(b);
+
+ UTEST_TEST("copy constructor, operator==, operator!=") {
+ NR::scale const sa_copy(sa);
+ UTEST_ASSERT( sa == sa_copy );
+ UTEST_ASSERT(!( sa != sa_copy ));
+ UTEST_ASSERT( sa != sb );
+ }
+
+ UTEST_TEST("operator=") {
+ NR::scale sa_eq(sb);
+ sa_eq = sa;
+ UTEST_ASSERT( sa == sa_eq );
+ }
+
+ UTEST_TEST("point constructor") {
+ UTEST_ASSERT(sb[X] == b[X]);
+ UTEST_ASSERT(sb[Y] == b[Y]);
+ }
+
+ UTEST_TEST("operator*(Point, scale)") {
+ NR::Point const ab( b * sa );
+ UTEST_ASSERT( ab == NR::Point(-3.0, 6.0) );
+ }
+
+ UTEST_TEST("operator*(scale, scale)") {
+ NR::scale const sab( sa * sb );
+ UTEST_ASSERT( sab == NR::scale(-3.0, 6.0) );
+ }
+
+ UTEST_TEST("operator/(scale, scale)") {
+ NR::scale const sa_b( sa / sb );
+ NR::scale const exp_sa_b(-0.75, 2./3.);
+ UTEST_ASSERT( sa_b[0] == exp_sa_b[0] );
+ UTEST_ASSERT( fabs( sa_b[1] - exp_sa_b[1] ) < 1e-10 );
+ }
+
+ return ( utest_end()
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE );
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-scale-test.h b/src/libnr/nr-scale-test.h
new file mode 100644
index 000000000..dc5e6ef28
--- /dev/null
+++ b/src/libnr/nr-scale-test.h
@@ -0,0 +1,92 @@
+#include <cxxtest/TestSuite.h>
+
+#include <libnr/nr-scale.h>
+#include <libnr/nr-scale-ops.h>
+using NR::X;
+using NR::Y;
+
+class NrScaleTest : public CxxTest::TestSuite
+{
+public:
+
+ NrScaleTest() :
+ sa( 1.5, 2.0 ),
+ b( -2.0, 3.0 ),
+ sb( b )
+ {
+ }
+ virtual ~NrScaleTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static NrScaleTest *createSuite() { return new NrScaleTest(); }
+ static void destroySuite( NrScaleTest *suite ) { delete suite; }
+
+ NR::scale const sa;
+ NR::Point const b;
+ NR::scale const sb;
+
+
+
+ void testXY_CtorArrayOperator(void)
+ {
+ TS_ASSERT_EQUALS( sa[X], 1.5 );
+ TS_ASSERT_EQUALS( sa[Y], 2.0 );
+ TS_ASSERT_EQUALS( sa[0u], 1.5 );
+ TS_ASSERT_EQUALS( sa[1u], 2.0 );
+ }
+
+
+ void testCopyCtor_AssignmentOp_NotEquals(void)
+ {
+ NR::scale const sa_copy(sa);
+ TS_ASSERT_EQUALS( sa, sa_copy );
+ TS_ASSERT(!( sa != sa_copy ));
+ TS_ASSERT( sa != sb );
+ }
+
+ void testAssignmentOp(void)
+ {
+ NR::scale sa_eq(sb);
+ sa_eq = sa;
+ TS_ASSERT_EQUALS( sa, sa_eq );
+ }
+
+ void testPointCtor(void)
+ {
+ TS_ASSERT_EQUALS( sb[X], b[X] );
+ TS_ASSERT_EQUALS( sb[Y], b[Y] );
+ }
+
+ void testOpStarPointScale(void)
+ {
+ NR::Point const ab( b * sa );
+ TS_ASSERT_EQUALS( ab, NR::Point(-3.0, 6.0) );
+ }
+
+ void testOpStarScaleScale(void)
+ {
+ NR::scale const sab( sa * sb );
+ TS_ASSERT_EQUALS( sab, NR::scale(-3.0, 6.0) );
+ }
+
+ void testOpDivScaleScale(void)
+ {
+ NR::scale const sa_b( sa / sb );
+ NR::scale const exp_sa_b(-0.75, 2./3.);
+ TS_ASSERT_EQUALS( sa_b[0], exp_sa_b[0] );
+// TS_ASSERT_EQUALS( fabs( sa_b[1] - exp_sa_b[1] ) < 1e-10 );
+ TS_ASSERT_DELTA( sa_b[1], exp_sa_b[1], 1e-10 );
+ }
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-scale-translate-ops.cpp b/src/libnr/nr-scale-translate-ops.cpp
new file mode 100644
index 000000000..911c92e5b
--- /dev/null
+++ b/src/libnr/nr-scale-translate-ops.cpp
@@ -0,0 +1,19 @@
+#include "libnr/nr-matrix-translate-ops.h"
+
+NR::Matrix
+operator*(NR::scale const &s, NR::translate const &t)
+{
+ return NR::Matrix(s) * t;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-scale-translate-ops.h b/src/libnr/nr-scale-translate-ops.h
new file mode 100644
index 000000000..2f6f23c2c
--- /dev/null
+++ b/src/libnr/nr-scale-translate-ops.h
@@ -0,0 +1,20 @@
+#ifndef SEEN_LIBNR_NR_SCALE_TRANSLATE_OPS_H
+#define SEEN_LIBNR_NR_SCALE_TRANSLATE_OPS_H
+
+#include "libnr/nr-forward.h"
+
+NR::Matrix operator*(NR::scale const &s, NR::translate const &t);
+
+
+#endif /* !SEEN_LIBNR_NR_SCALE_TRANSLATE_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-scale.h b/src/libnr/nr-scale.h
new file mode 100644
index 000000000..c9de871de
--- /dev/null
+++ b/src/libnr/nr-scale.h
@@ -0,0 +1,50 @@
+#ifndef SEEN_NR_SCALE_H
+#define SEEN_NR_SCALE_H
+#include <libnr/nr-point.h>
+#include <libnr/nr-point-ops.h>
+
+namespace NR {
+
+class scale {
+private:
+ Point _p;
+
+private:
+ scale();
+
+public:
+ explicit scale(Point const &p) : _p(p) {}
+ scale(double const x, double const y) : _p(x, y) {}
+ explicit scale(double const s) : _p(s, s) {}
+ inline Coord operator[](Dim2 const d) const { return _p[d]; }
+ inline Coord operator[](unsigned const d) const { return _p[d]; }
+ inline Coord &operator[](Dim2 const d) { return _p[d]; }
+ inline Coord &operator[](unsigned const d) { return _p[d]; }
+
+ bool operator==(scale const &o) const {
+ return _p == o._p;
+ }
+
+ bool operator!=(scale const &o) const {
+ return _p != o._p;
+ }
+ scale inverse() const {
+ return scale(1/_p[0], 1/_p[1]);
+ }
+};
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_SCALE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-svp-private.h b/src/libnr/nr-svp-private.h
new file mode 100644
index 000000000..dbddcd7ca
--- /dev/null
+++ b/src/libnr/nr-svp-private.h
@@ -0,0 +1,30 @@
+#ifndef __NR_SVP_PRIVATE_H__
+#define __NR_SVP_PRIVATE_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-svp.h>
+
+#define NR_QUANT_X 16.0F
+#define NR_QUANT_Y 16.0F
+#define NR_COORD_X_FROM_ART(v) (floor (NR_QUANT_X * (v) + 0.5F) / NR_QUANT_X)
+#define NR_COORD_Y_FROM_ART(v) (floor (NR_QUANT_Y * (v) + 0.5F) / NR_QUANT_Y)
+#define NR_COORD_TO_ART(v) (v)
+
+/* NRVertex */
+
+NRVertex *nr_vertex_new (void);
+NRVertex *nr_vertex_new_xy (NR::Coord x, NR::Coord y);
+void nr_vertex_free_one (NRVertex *v);
+void nr_vertex_free_list (NRVertex *v);
+
+NRVertex *nr_vertex_reverse_list (NRVertex *v);
+
+#endif
diff --git a/src/libnr/nr-svp-render.cpp b/src/libnr/nr-svp-render.cpp
new file mode 100644
index 000000000..0c3b391d5
--- /dev/null
+++ b/src/libnr/nr-svp-render.cpp
@@ -0,0 +1,615 @@
+#define __NR_SVP_RENDER_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#define NR_SVPSEG_Y0(s,i) ((s)->points[(s)->segments[i].start].y)
+#define NR_SVPSEG_Y1(s,i) ((s)->points[(s)->segments[i].start + (s)->segments[i].length - 1].y)
+
+#define noNR_VERBOSE
+
+#include "nr-svp-render.h"
+
+static void nr_svp_render (NRSVP *svp, unsigned char *px, unsigned int bpp, unsigned int rs, int x0, int y0, int x1, int y1,
+ void (* run) (unsigned char *px, int len, int c0_24, int s0_24, void *data), void *data);
+
+static void nr_svp_run_A8_OR (unsigned char *d, int len, int c0_24, int s0_24, void *data);
+
+/* Renders graymask of svl into buffer */
+
+void
+nr_pixblock_render_svp_mask_or (NRPixBlock *d, NRSVP *svp)
+{
+ nr_svp_render (svp, NR_PIXBLOCK_PX (d), 1, d->rs,
+ d->area.x0, d->area.y0, d->area.x1, d->area.y1,
+ nr_svp_run_A8_OR, NULL);
+}
+
+static void
+nr_svp_run_A8_OR (unsigned char *d, int len, int c0_24, int s0_24, void *data)
+{
+ if ((c0_24 >= 0xff0000) && (s0_24 == 0x0)) {
+ /* Simple copy */
+ while (len > 0) {
+ d[0] = 255;
+ d += 1;
+ len -= 1;
+ }
+ } else {
+ while (len > 0) {
+ unsigned int ca, da;
+ /* Draw */
+ ca = c0_24 >> 16;
+ da = 65025 - (255 - ca) * (255 - d[0]);
+ d[0] = (da + 127) / 255;
+ d += 1;
+ c0_24 += s0_24;
+ c0_24 = CLAMP (c0_24, 0, 16777216);
+ len -= 1;
+ }
+ }
+}
+
+
+struct NRRun {
+ NRRun *next;
+ NR::Coord x0, y0, x1, y1;
+ float step;
+ float final;
+ NR::Coord x;
+ NR::Coord value;
+};
+
+static NRRun *nr_run_new (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, int wind);
+static NRRun *nr_run_free_one (NRRun *run);
+static void nr_run_free_list (NRRun *run);
+static NRRun *nr_run_insort (NRRun *start, NRRun *run);
+
+struct NRSlice {
+ NRSlice *next;
+ int wind;
+ NRPoint *points;
+ unsigned int current;
+ unsigned int last;
+ NR::Coord x;
+ NR::Coord y;
+ NR::Coord stepx;
+};
+
+static NRSlice *nr_slice_new (int wind, NRPoint *points, unsigned int length, NR::Coord y);
+static NRSlice *nr_slice_free_one (NRSlice *s);
+static void nr_slice_free_list (NRSlice *s);
+static NRSlice *nr_slice_insort (NRSlice *start, NRSlice *slice);
+static int nr_slice_compare (NRSlice *l, NRSlice *r);
+
+static void
+nr_svp_render (NRSVP *svp, unsigned char *px, unsigned int bpp, unsigned int rs, int iX0, int iY0, int iX1, int iY1,
+ void (* run) (unsigned char *px, int len, int c0_24, int s0_24, void *data), void *data)
+{
+ NR::Coord dX0, dY0, dX1, dY1;
+ NRSlice *slices;
+ unsigned int sidx;
+ int ystart;
+ unsigned char *rowbuffer;
+ int iy0;
+
+ if (!svp || !svp->length) return;
+
+ /* Find starting pixel row */
+ /* g_assert (svl->bbox.y0 == svl->vertex->y); */
+ sidx = 0;
+ while (NR_SVPSEG_IS_FLAT (svp, sidx) && (sidx < svp->length)) sidx += 1;
+ if (sidx >= svp->length) return;
+ ystart = (int) floor (NR_SVPSEG_Y0 (svp, sidx));
+ if (ystart > iY0) {
+ if (ystart >= iY1) return;
+ px += (ystart - iY0) * rs;
+ iY0 = ystart;
+ }
+
+ dX0 = iX0;
+ dY0 = iY0;
+ dX1 = iX1;
+ dY1 = iY1;
+
+ /* Construct initial slice list */
+ slices = NULL;
+ while (sidx < svp->length) {
+ if (!NR_SVPSEG_IS_FLAT (svp, sidx)) {
+ NRSVPSegment *seg;
+ /* It is real renderable segment */
+ /* Postpone if starts above initial slice */
+ if (NR_SVPSEG_Y0 (svp, sidx) > dY0) break;
+ seg = svp->segments + sidx;
+ if (seg->wind && (NR_SVPSEG_Y1 (svp, sidx) > dY0)) {
+ /* We are renderable and cross initial slice */
+ NRSlice *newslice;
+ newslice = nr_slice_new (seg->wind, svp->points + seg->start, seg->length, dY0);
+ slices = nr_slice_insort (slices, newslice);
+ }
+ }
+ sidx += 1;
+ }
+ if (!slices && (sidx >= svp->length)) return;
+
+ /* Row buffer */
+ /* fixme: not needed */
+ rowbuffer = px;
+
+ /* Main iteration */
+ for (iy0 = iY0; iy0 < iY1; iy0 += 1) {
+ NR::Coord dy0, dy1;
+ NRSlice *ss, *cs;
+ NRRun *runs;
+ int xstart;
+ float globalval;
+ unsigned char *d;
+ int ix0;
+
+ dy0 = iy0;
+ dy1 = dy0 + 1.0;
+
+ /* Add possible new svls to slice list */
+ while (sidx < svp->length) {
+ if (!NR_SVPSEG_IS_FLAT (svp, sidx)) {
+ NRSVPSegment *seg;
+ /* It is real renderable segment */
+ /* Postpone if starts above ending slice */
+ if (NR_SVPSEG_Y0 (svp, sidx) > dy1) break;
+ seg = svp->segments + sidx;
+ if (seg->wind) {
+ NR::Coord y;
+ NRSlice *newslice;
+ /* We are renderable */
+ /* fixme: we should use safely nsvl->vertex->y here */
+ y = MAX (dy0, NR_SVPSEG_Y0 (svp, sidx));
+ newslice = nr_slice_new (seg->wind, svp->points + seg->start, seg->length, y);
+ slices = nr_slice_insort (slices, newslice);
+ }
+ }
+ sidx += 1;
+ }
+ /* Construct runs, stretching slices */
+ /* fixme: This step can be optimized by continuing long runs and adding only new ones (Lauris) */
+ runs = NULL;
+ ss = NULL;
+ cs = slices;
+ while (cs) {
+ /* g_assert (cs->y >= y0); */
+ /* g_assert (cs->y < (y + 1)); */
+ while ((cs->y < dy1) && (cs->current < cs->last)) {
+ NR::Coord rx0, ry0, rx1, ry1;
+ NRRun * newrun;
+ rx0 = cs->x;
+ ry0 = cs->y;
+ if (cs->points[cs->current + 1].y > dy1) {
+ /* The same slice continues */
+ rx1 = rx0 + (dy1 - ry0) * cs->stepx;
+ ry1 = dy1;
+ cs->x = rx1;
+ cs->y = ry1;
+ } else {
+ /* Subpixel height run */
+ cs->current += 1;
+ rx1 = cs->points[cs->current].x;
+ ry1 = cs->points[cs->current].y;
+ cs->x = rx1;
+ cs->y = ry1;
+ if (cs->current < cs->last) {
+ cs->stepx = (cs->points[cs->current + 1].x - rx1) / (cs->points[cs->current + 1].y - ry1);
+ }
+ }
+ newrun = nr_run_new (rx0, ry0, rx1, ry1, cs->wind);
+ /* fixme: we should use walking forward/backward instead */
+ runs = nr_run_insort (runs, newrun);
+ }
+ if (cs->current < cs->last) {
+ ss = cs;
+ cs = cs->next;
+ } else {
+ /* Slice is exhausted */
+ cs = nr_slice_free_one (cs);
+ if (ss) {
+ ss->next = cs;
+ } else {
+ slices = cs;
+ }
+ }
+ }
+ /* Slices are expanded to next scanline */
+ /* Run list is generated */
+ /* Globalval is the sum of all finished runs */
+ globalval = 0.0;
+ if ((runs) && (dX0 < runs->x0)) {
+ /* First run starts right from x0 */
+ xstart = (int) floor (runs->x0);
+ } else {
+ NRRun *sr, *cr;
+ /* First run starts left from x0 */
+ xstart = iX0;
+ sr = NULL;
+ cr = runs;
+ while ((cr) && (cr->x0 < dX0)) {
+ if (cr->x1 <= dX0) {
+ globalval += cr->final;
+ /* Remove exhausted current run */
+ cr = nr_run_free_one (cr);
+ if (sr) {
+ sr->next = cr;
+ } else {
+ runs = cr;
+ }
+ } else {
+ cr->x = dX0;
+ cr->value = (dX0 - cr->x0) * cr->step;
+ sr = cr;
+ cr = cr->next;
+ }
+ }
+ }
+
+ /* Running buffer */
+ d = rowbuffer + bpp * (xstart - iX0);
+
+ for (ix0 = xstart; (runs) && (ix0 < iX1); ix0++) {
+ NR::Coord dx0, dx1;
+ int ix1;
+ NRRun *sr, *cr;
+ float localval;
+ unsigned int fill;
+ float fillstep;
+ int rx1;
+ int c24;
+
+ dx0 = ix0;
+ dx1 = dx0 + 1.0;
+ ix1 = ix0 + 1;
+
+ /* process runs */
+ localval = globalval;
+ sr = NULL;
+ cr = runs;
+ fill = TRUE;
+ fillstep = 0.0;
+ rx1 = iX1;
+ while ((cr) && (cr->x0 < dx1)) {
+ if (cr->x1 <= dx1) {
+ /* Run ends here */
+ /* No fill */
+ fill = FALSE;
+ /* Continue with final value */
+ globalval += cr->final;
+ /* Add initial trapezoid */
+ localval += (float) (0.5 * (cr->x1 - cr->x) * (cr->value + cr->final));
+ /* Add final rectangle */
+ localval += (float) ((dx1 - cr->x1) * cr->final);
+ /* Remove exhausted run */
+ cr = nr_run_free_one (cr);
+ if (sr) {
+ sr->next = cr;
+ } else {
+ runs = cr;
+ }
+ } else {
+ /* Run continues through xnext */
+ if (fill) {
+ if (cr->x0 > ix0) {
+ fill = FALSE;
+ } else {
+ rx1 = MIN (rx1, (int) floor (cr->x1));
+ fillstep += cr->step;
+ }
+ }
+ localval += (float) ((dx1 - cr->x) * (cr->value + (dx1 - cr->x) * cr->step / 2.0));
+ cr->x = dx1;
+ cr->value = (dx1 - cr->x0) * cr->step;
+ sr = cr;
+ cr = cr->next;
+ }
+ }
+ if (fill) {
+ if (cr) rx1 = MIN (rx1, (int) floor (cr->x0));
+ }
+#ifdef NR_VERBOSE
+ if ((localval < -0.01) || (localval > 1.01)) {
+ printf ("Weird localval %g : gv %g\n", localval, globalval); // localizing ok
+ }
+#endif
+ localval = CLAMP (localval, 0.0F, 1.0F);
+ c24 = (int) floor (16777215 * localval + 0.5);
+ if (fill && (rx1 > ix1)) {
+ NRRun *r;
+ int s24;
+ s24 = (int) floor (16777215 * fillstep + 0.5);
+ if ((s24 != 0) || (c24 > 65535)) {
+ run (d, rx1 - ix0, c24, s24, data);
+ }
+ /* We have to rewind run positions as well */
+ for (r = runs; r && (r->x0 < dx1); r = r->next) {
+ r->x = rx1;
+ r->value = (rx1 - r->x0) * r->step;
+ }
+ d += bpp * (rx1 - ix0);
+ ix0 = rx1 - 1;
+ } else {
+ run (d, 1, c24, 0, data);
+ d += bpp;
+ }
+ }
+ nr_run_free_list (runs);
+ rowbuffer += rs;
+ }
+ if (slices) nr_slice_free_list (slices);
+}
+
+/* Slices */
+
+#define NR_SLICE_ALLOC_SIZE 32
+static NRSlice *ffslice = NULL;
+
+static NRSlice *
+nr_slice_new (int wind, NRPoint *points, unsigned int length, NR::Coord y)
+{
+ NRSlice *s;
+ NRPoint *p;
+
+ /* g_assert (svl); */
+ /* g_assert (svl->vertex); */
+ /* fixme: not sure, whether correct */
+ /* g_assert (y == NR_COORD_SNAP (y)); */
+ /* Slices startpoints are included, endpoints excluded */
+ /* g_return_val_if_fail (y >= svl->bbox.y0, NULL); */
+ /* g_return_val_if_fail (y < svl->bbox.y1, NULL); */
+
+ s = ffslice;
+
+ if (s == NULL) {
+ int i;
+ s = nr_new (NRSlice, NR_SLICE_ALLOC_SIZE);
+ for (i = 1; i < (NR_SLICE_ALLOC_SIZE - 1); i++) s[i].next = &s[i + 1];
+ s[NR_SLICE_ALLOC_SIZE - 1].next = NULL;
+ ffslice = s + 1;
+ } else {
+ ffslice = s->next;
+ }
+
+ s->next = NULL;
+ s->wind = wind;
+ s->points = points;
+ s->current = 0;
+ s->last = length - 1;
+
+ while ((s->current < s->last) && (s->points[s->current + 1].y <= y)) s->current += 1;
+ p = s->points + s->current;
+
+ if (s->points[s->current].y == y) {
+ s->x = p[0].x;
+ } else {
+ s->x = p[0].x + (p[1].x - p[0].x) * (y - p[0].y) / (p[1].y - p[0].y);
+ }
+ s->y = y;
+ s->stepx = (p[1].x - p[0].x) / (p[1].y - p[0].y);
+
+ return s;
+}
+
+static NRSlice *
+nr_slice_free_one (NRSlice *slice)
+{
+ NRSlice *next;
+ next = slice->next;
+ slice->next = ffslice;
+ ffslice = slice;
+ return next;
+}
+
+static void
+nr_slice_free_list (NRSlice *slice)
+{
+ NRSlice *l;
+
+ if (!slice) return;
+
+ for (l = slice; l->next != NULL; l = l->next);
+
+ l->next = ffslice;
+ ffslice = slice;
+}
+
+static NRSlice *
+nr_slice_insort (NRSlice * start, NRSlice * slice)
+{
+ NRSlice * s, * l;
+
+ if (!start) return slice;
+ if (!slice) return start;
+
+ if (nr_slice_compare (slice, start) <= 0) {
+ slice->next = start;
+ return slice;
+ }
+
+ s = start;
+ for (l = start->next; l != NULL; l = l->next) {
+ if (nr_slice_compare (slice, l) <= 0) {
+ slice->next = l;
+ s->next = slice;
+ return start;
+ }
+ s = l;
+ }
+
+ slice->next = NULL;
+ s->next = slice;
+
+ return start;
+}
+
+static int
+nr_slice_compare (NRSlice *l, NRSlice *r)
+{
+ if (l->y == r->y) {
+ if (l->x < r->x) return -1;
+ if (l->x > r->x) return 1;
+ if (l->stepx < r->stepx) return -1;
+ if (l->stepx > r->stepx) return 1;
+ } else if (l->y > r->y) {
+ unsigned int pidx;
+ NRPoint *p;
+ NR::Coord x, ldx, rdx;
+ /* This is bitch - we have to determine r values at l->y */
+ pidx = 0;
+ while ((pidx < r->last) && (r->points[pidx + 1].y <= l->y)) pidx += 1;
+ /* If v is last vertex, r ends before l starts */
+ if (pidx >= r->last) return 1;
+ p = r->points + pidx;
+ if (p[0].y == l->y) {
+ x = p[0].x;
+ } else {
+ x = p[0].x + (p[1].x - p[0].x) * (l->y - p[0].y) / (p[1].y - p[0].y);
+ }
+ if (l->x < x) return -1;
+ if (l->x > x) return 1;
+ ldx = l->stepx * (p[1].y - p[0].y);
+ rdx = p[1].x - p[0].x;
+ if (ldx < rdx) return -1;
+ if (ldx > rdx) return 1;
+ } else {
+ unsigned int pidx;
+ NRPoint *p;
+ NR::Coord x, ldx, rdx;
+ /* This is bitch - we have to determine l value at r->y */
+ pidx = 0;
+ while ((pidx < l->last) && (l->points[pidx + 1].y <= r->y)) pidx += 1;
+ /* If v is last vertex, l ends before r starts */
+ if (pidx >= l->last) return 1;
+ p = l->points + pidx;
+ if (p[0].y == r->y) {
+ x = p[0].x;
+ } else {
+ x = p[0].x + (p[1].x - p[0].x) * (r->y - p[0].y) / (p[1].y - p[0].y);
+ }
+ if (x < r->x) return -1;
+ if (x > r->x) return 1;
+ ldx = l->stepx * (p[1].y - p[0].y);
+ rdx = p[1].x - p[0].x;
+ if (ldx < rdx) return -1;
+ if (ldx > rdx) return 1;
+ }
+ return 0;
+}
+
+/*
+ * Memory management stuff follows (remember goals?)
+ */
+
+#define NR_RUN_ALLOC_SIZE 32
+static NRRun *ffrun = NULL;
+
+static NRRun *
+nr_run_new (NR::Coord x0, NR::Coord y0, NR::Coord x1, NR::Coord y1, int wind)
+{
+ NRRun * r;
+
+ r = ffrun;
+
+ if (r == NULL) {
+ int i;
+ r = nr_new (NRRun, NR_RUN_ALLOC_SIZE);
+ for (i = 1; i < (NR_RUN_ALLOC_SIZE - 1); i++) (r + i)->next = (r + i + 1);
+ (r + NR_RUN_ALLOC_SIZE - 1)->next = NULL;
+ ffrun = r + 1;
+ } else {
+ ffrun = r->next;
+ }
+
+ r->next = NULL;
+
+ if (x0 <= x1) {
+ r->x0 = x0;
+ r->x1 = x1;
+ r->y0 = y0;
+ r->y1 = y1;
+ r->step = (x0 == x1) ? 0.0F : (float) (wind * (y1 - y0) / (x1 - x0));
+ } else {
+ r->x0 = x1;
+ r->x1 = x0;
+ r->y0 = y1;
+ r->y1 = y0;
+ r->step = (float) (wind * (y1 - y0) / (x0 - x1));
+ }
+
+ r->final = (float) (wind * (y1 - y0));
+
+ r->value = 0.0;
+ r->x = r->x0;
+
+ return r;
+}
+
+static NRRun *
+nr_run_free_one (NRRun *run)
+{
+ NRRun *next;
+ next = run->next;
+ run->next = ffrun;
+ ffrun = run;
+ return next;
+}
+
+static void
+nr_run_free_list (NRRun * run)
+{
+ NRRun * l;
+
+ if (!run) return;
+
+ for (l = run; l->next != NULL; l = l->next);
+ l->next = ffrun;
+ ffrun = run;
+}
+
+static NRRun *
+nr_run_insort (NRRun * start, NRRun * run)
+{
+ NRRun * s, * l;
+
+ if (!start) return run;
+ if (!run) return start;
+
+ if (run->x0 < start->x0) {
+ run->next = start;
+ return run;
+ }
+
+ s = start;
+ for (l = start->next; l != NULL; l = l->next) {
+ if (run->x0 < l->x0) {
+ run->next = l;
+ s->next = run;
+ return start;
+ }
+ s = l;
+ }
+
+ s->next = run;
+
+ return start;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-svp-render.h b/src/libnr/nr-svp-render.h
new file mode 100644
index 000000000..1638fb286
--- /dev/null
+++ b/src/libnr/nr-svp-render.h
@@ -0,0 +1,30 @@
+#ifndef __NR_SVP_RENDER_H__
+#define __NR_SVP_RENDER_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-pixblock.h>
+#include <libnr/nr-svp.h>
+
+/* Renders graymask of svp into buffer */
+void nr_pixblock_render_svp_mask_or (NRPixBlock *d, NRSVP *svp);
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-svp.cpp b/src/libnr/nr-svp.cpp
new file mode 100644
index 000000000..a1484397a
--- /dev/null
+++ b/src/libnr/nr-svp.cpp
@@ -0,0 +1,180 @@
+#define __NR_SVP_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#define noNR_VERBOSE
+
+#define NR_SVP_LENGTH_MAX 128
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+# include <ieeefp.h>
+#endif
+
+#include "nr-rect.h"
+#include "nr-svp-private.h"
+
+/* Sorted vector paths */
+
+void
+nr_svp_free (NRSVP *svp)
+{
+ if (svp->points) nr_free (svp->points);
+ free (svp);
+}
+
+void
+nr_svp_bbox (NRSVP *svp, NRRect *bbox, unsigned int clear)
+{
+ unsigned int sidx;
+ float x0, y0, x1, y1;
+
+ x0 = y0 = NR_HUGE;
+ x1 = y1 = -NR_HUGE;
+
+ for (sidx = 0; sidx < svp->length; sidx++) {
+ NRSVPSegment *seg;
+ seg = svp->segments + sidx;
+ if (seg->length) {
+ x0 = MIN (x0, seg->x0);
+ y0 = MIN (y0, svp->points[seg->start].y);
+ x1 = MAX (x1, seg->x1);
+ y1 = MAX (y1, svp->points[seg->start + seg->length - 1].y);
+ }
+ }
+
+ if ((x1 > x0) && (y1 > y0)) {
+ if (clear || (bbox->x1 <= bbox->x0) || (bbox->y1 <= bbox->y0)) {
+ bbox->x0 = x0;
+ bbox->y0 = y0;
+ bbox->x1 = x1;
+ bbox->y1 = y1;
+ } else {
+ bbox->x0 = MIN (bbox->x0, x0);
+ bbox->y0 = MIN (bbox->y0, y0);
+ bbox->x1 = MAX (bbox->x1, x1);
+ bbox->y1 = MAX (bbox->y1, y1);
+ }
+ }
+}
+
+/* NRVertex */
+
+#define NR_VERTEX_ALLOC_SIZE 4096
+static NRVertex *ffvertex = NULL;
+
+NRVertex *
+nr_vertex_new (void)
+{
+ NRVertex * v;
+#ifndef NR_VERTEX_ALLOC
+
+ v = ffvertex;
+
+ if (v == NULL) {
+ int i;
+ v = nr_new (NRVertex, NR_VERTEX_ALLOC_SIZE);
+ for (i = 1; i < (NR_VERTEX_ALLOC_SIZE - 1); i++) v[i].next = &v[i + 1];
+ v[NR_VERTEX_ALLOC_SIZE - 1].next = NULL;
+ ffvertex = v + 1;
+ } else {
+ ffvertex = v->next;
+ }
+#else
+ v = nr_new (NRVertex, 1);
+#endif
+
+ v->next = NULL;
+
+ return v;
+}
+
+NRVertex *
+nr_vertex_new_xy (NR::Coord x, NR::Coord y)
+{
+ NRVertex * v;
+
+ if (!finite(x) || !finite(y)) {
+ g_critical("nr_vertex_new_xy: BUG: Coordinates are not finite");
+ x = y = 0;
+ } else if (!( fabs(x) < 1e17 && fabs(y) < 1e17 )) {
+ g_critical("nr_vertex_new_xy: Coordinates out of range");
+ x = y = 0;
+ }
+
+ v = nr_vertex_new ();
+
+ v->x = x;
+ v->y = y;
+
+ return v;
+}
+
+void
+nr_vertex_free_one (NRVertex * v)
+{
+#ifndef NR_VERTEX_ALLOC
+ v->next = ffvertex;
+ ffvertex = v;
+#else
+ nr_free (v);
+#endif
+}
+
+void
+nr_vertex_free_list (NRVertex * v)
+{
+#ifndef NR_VERTEX_ALLOC
+ NRVertex * l;
+ for (l = v; l->next != NULL; l = l->next);
+ l->next = ffvertex;
+ ffvertex = v;
+#else
+ NRVertex *l, *n;
+ l = v;
+ while (l) {
+ n = l->next;
+ nr_free (l);
+ l = n;
+ }
+#endif
+}
+
+NRVertex *
+nr_vertex_reverse_list (NRVertex * v)
+{
+ NRVertex * p;
+
+ p = NULL;
+
+ while (v) {
+ NRVertex * n;
+ n = v->next;
+ v->next = p;
+ p = v;
+ v = n;
+ }
+
+ return p;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-svp.h b/src/libnr/nr-svp.h
new file mode 100644
index 000000000..ca1521f29
--- /dev/null
+++ b/src/libnr/nr-svp.h
@@ -0,0 +1,65 @@
+#ifndef __NR_SVP_H__
+#define __NR_SVP_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+/* Sorted vector paths */
+
+#include <glib/gtypes.h>
+#include <libnr/nr-coord.h>
+
+struct NRPoint;
+
+struct NRSVPSegment {
+ gint16 wind;
+ guint16 length;
+ guint32 start;
+ float x0, x1;
+};
+
+struct NRSVPFlat {
+ gint16 wind;
+ guint16 length;
+ float y;
+ float x0, x1;
+};
+
+struct NRSVP {
+ unsigned int length;
+ NRPoint *points;
+ NRSVPSegment segments[1];
+};
+
+#define NR_SVPSEG_IS_FLAT(s,i) (!(s)->segments[i].length)
+
+void nr_svp_free (NRSVP *svp);
+
+void nr_svp_bbox (NRSVP *svp, NRRect *bbox, unsigned int clear);
+
+/* Sorted vertex lists */
+
+struct NRVertex {
+ NRVertex *next;
+ NR::Coord x, y;
+};
+
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-matrix-ops.cpp b/src/libnr/nr-translate-matrix-ops.cpp
new file mode 100644
index 000000000..d03caf4ff
--- /dev/null
+++ b/src/libnr/nr-translate-matrix-ops.cpp
@@ -0,0 +1,27 @@
+#include "libnr/nr-matrix-ops.h"
+
+namespace NR {
+
+Matrix
+operator*(translate const &t, Matrix const &m)
+{
+ Matrix ret(m);
+ ret[4] += m[0] * t[X] + m[2] * t[Y];
+ ret[5] += m[1] * t[X] + m[3] * t[Y];
+ assert_close( ret, Matrix(t) * m );
+ return ret;
+}
+
+} /* namespace NR */
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-matrix-ops.h b/src/libnr/nr-translate-matrix-ops.h
new file mode 100644
index 000000000..aceb123d1
--- /dev/null
+++ b/src/libnr/nr-translate-matrix-ops.h
@@ -0,0 +1,22 @@
+#ifndef SEEN_LIBNR_NR_TRANSLATE_MATRIX_OPS_H
+#define SEEN_LIBNR_NR_TRANSLATE_MATRIX_OPS_H
+
+#include "libnr/nr-forward.h"
+
+namespace NR {
+Matrix operator*(translate const &t, Matrix const &m);
+}
+
+
+#endif /* !SEEN_LIBNR_NR_TRANSLATE_MATRIX_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-ops.h b/src/libnr/nr-translate-ops.h
new file mode 100644
index 000000000..14ab6d1ed
--- /dev/null
+++ b/src/libnr/nr-translate-ops.h
@@ -0,0 +1,43 @@
+#ifndef SEEN_NR_TRANSLATE_OPS_H
+#define SEEN_NR_TRANSLATE_OPS_H
+
+#include <libnr/nr-translate.h>
+#include <libnr/nr-point-ops.h>
+
+namespace NR {
+
+inline bool operator==(translate const &a, translate const &b)
+{
+ return a.offset == b.offset;
+}
+
+inline bool operator!=(translate const &a, translate const &b)
+{
+ return !( a == b );
+}
+
+inline translate operator*(translate const &a, translate const &b)
+{
+ return translate( a.offset + b.offset );
+}
+
+inline Point operator*(Point const &v, translate const &t)
+{
+ return t.offset + v;
+}
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_TRANSLATE_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-rotate-ops.cpp b/src/libnr/nr-translate-rotate-ops.cpp
new file mode 100644
index 000000000..35f60c10d
--- /dev/null
+++ b/src/libnr/nr-translate-rotate-ops.cpp
@@ -0,0 +1,20 @@
+#include <libnr/nr-matrix-translate-ops.h>
+#include <libnr/nr-rotate-ops.h>
+
+NR::Matrix
+operator*(NR::translate const &a, NR::rotate const &b)
+{
+ return NR::Matrix(b) * NR::translate(a.offset * b);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-rotate-ops.h b/src/libnr/nr-translate-rotate-ops.h
new file mode 100644
index 000000000..0716f21cc
--- /dev/null
+++ b/src/libnr/nr-translate-rotate-ops.h
@@ -0,0 +1,21 @@
+#ifndef SEEN_LIBNR_NR_TRANSLATE_ROTATE_OPS_H
+#define SEEN_LIBNR_NR_TRANSLATE_ROTATE_OPS_H
+
+#include <libnr/nr-forward.h>
+
+
+NR::Matrix operator*(NR::translate const &a, NR::rotate const &b);
+
+
+#endif /* !SEEN_LIBNR_NR_TRANSLATE_ROTATE_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-scale-ops.cpp b/src/libnr/nr-translate-scale-ops.cpp
new file mode 100644
index 000000000..b995bbad5
--- /dev/null
+++ b/src/libnr/nr-translate-scale-ops.cpp
@@ -0,0 +1,25 @@
+#include "libnr/nr-matrix-ops.h"
+
+NR::Matrix
+operator*(NR::translate const &t, NR::scale const &s)
+{
+ using NR::X; using NR::Y;
+
+ NR::Matrix ret(s);
+ ret[4] = t[X] * s[X];
+ ret[5] = t[Y] * s[Y];
+ assert_close( ret, NR::Matrix(t) * NR::Matrix(s) );
+ return ret;
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-scale-ops.h b/src/libnr/nr-translate-scale-ops.h
new file mode 100644
index 000000000..c72665857
--- /dev/null
+++ b/src/libnr/nr-translate-scale-ops.h
@@ -0,0 +1,20 @@
+#ifndef SEEN_LIBNR_NR_TRANSLATE_SCALE_OPS_H
+#define SEEN_LIBNR_NR_TRANSLATE_SCALE_OPS_H
+
+#include "libnr/nr-forward.h"
+
+NR::Matrix operator*(NR::translate const &t, NR::scale const &s);
+
+
+#endif /* !SEEN_LIBNR_NR_TRANSLATE_SCALE_OPS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-test.cpp b/src/libnr/nr-translate-test.cpp
new file mode 100644
index 000000000..9e1ef1166
--- /dev/null
+++ b/src/libnr/nr-translate-test.cpp
@@ -0,0 +1,65 @@
+#include <utest/utest.h>
+#include <libnr/nr-point-ops.h>
+#include <libnr/nr-matrix.h>
+#include <libnr/nr-matrix-fns.h>
+#include <libnr/nr-matrix-ops.h>
+#include <libnr/nr-point-matrix-ops.h>
+#include <libnr/nr-translate.h>
+#include <libnr/nr-translate-ops.h>
+using NR::X;
+using NR::Y;
+
+
+int main(int argc, char *argv[])
+{
+ utest_start("translate");
+
+ NR::Point const b(-2.0, 3.0);
+ NR::translate const tb(b);
+ NR::translate const tc(-3.0, -2.0);
+ UTEST_TEST("constructors, operator[]") {
+ UTEST_ASSERT( tc[X] == -3.0 && tc[Y] == -2.0 );
+ UTEST_ASSERT( tb[0] == b[X] && tb[1] == b[Y] );
+ }
+
+ UTEST_TEST("operator=") {
+ NR::translate tb_eq(tc);
+ tb_eq = tb;
+ UTEST_ASSERT( tb == tb_eq );
+ UTEST_ASSERT( tb_eq != tc );
+ }
+
+ NR::translate const tbc( tb * tc );
+ UTEST_TEST("operator*(translate, translate)") {
+ UTEST_ASSERT( tbc.offset == NR::Point(-5.0, 1.0) );
+ UTEST_ASSERT( tbc.offset == ( tc * tb ).offset );
+ UTEST_ASSERT( NR::Matrix(tbc) == NR::Matrix(tb) * NR::Matrix(tc) );
+ }
+
+ UTEST_TEST("operator*(Point, translate)") {
+ UTEST_ASSERT( tbc.offset == b * tc );
+ UTEST_ASSERT( b * tc == b * NR::Matrix(tc) );
+ }
+
+ NR::translate const t_id(0.0, 0.0);
+ NR::Matrix const m_id(NR::identity());
+ UTEST_TEST("identity") {
+ UTEST_ASSERT( b * t_id == b );
+ UTEST_ASSERT( NR::Matrix(t_id) == m_id );
+ }
+
+ return ( utest_end()
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE );
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate-test.h b/src/libnr/nr-translate-test.h
new file mode 100644
index 000000000..20b537a45
--- /dev/null
+++ b/src/libnr/nr-translate-test.h
@@ -0,0 +1,87 @@
+#include <cxxtest/TestSuite.h>
+
+#include <libnr/nr-point-ops.h>
+#include <libnr/nr-matrix.h>
+#include <libnr/nr-matrix-fns.h>
+#include <libnr/nr-matrix-ops.h>
+#include <libnr/nr-point-matrix-ops.h>
+#include <libnr/nr-translate.h>
+#include <libnr/nr-translate-ops.h>
+using NR::X;
+using NR::Y;
+
+class NrTranslateTest : public CxxTest::TestSuite
+{
+public:
+
+ NrTranslateTest() :
+ b( -2.0, 3.0 ),
+ tb( b ),
+ tc( -3.0, -2.0 ),
+ tbc( tb * tc ),
+ t_id( 0.0, 0.0 ),
+ m_id( NR::identity() )
+ {
+ }
+ virtual ~NrTranslateTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static NrTranslateTest *createSuite() { return new NrTranslateTest(); }
+ static void destroySuite( NrTranslateTest *suite ) { delete suite; }
+
+ NR::Point const b;
+ NR::translate const tb;
+ NR::translate const tc;
+ NR::translate const tbc;
+ NR::translate const t_id;
+ NR::Matrix const m_id;
+
+
+ void testCtorsArrayOperator(void)
+ {
+ TS_ASSERT_EQUALS( tc[X], -3.0 );
+ TS_ASSERT_EQUALS( tc[Y], -2.0 );
+
+ TS_ASSERT_EQUALS( tb[0], b[X] );
+ TS_ASSERT_EQUALS( tb[1], b[Y] );
+ }
+
+ void testAssignmentOperator(void)
+ {
+ NR::translate tb_eq(tc);
+ tb_eq = tb;
+ TS_ASSERT_EQUALS( tb, tb_eq );
+ TS_ASSERT_DIFFERS( tb_eq, tc );
+ }
+
+ void testOpStarTranslateTranslate(void)
+ {
+ TS_ASSERT_EQUALS( tbc.offset, NR::Point(-5.0, 1.0) );
+ TS_ASSERT_EQUALS( tbc.offset, ( tc * tb ).offset );
+ TS_ASSERT_EQUALS( NR::Matrix(tbc), NR::Matrix(tb) * NR::Matrix(tc) );
+ }
+
+ void testOpStarPointTranslate(void)
+ {
+ TS_ASSERT_EQUALS( tbc.offset, b * tc );
+ TS_ASSERT_EQUALS( b * tc, b * NR::Matrix(tc) );
+ }
+
+ void testIdentity(void)
+ {
+ TS_ASSERT_EQUALS( b * t_id, b );
+ TS_ASSERT_EQUALS( NR::Matrix(t_id), m_id );
+ }
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-translate.h b/src/libnr/nr-translate.h
new file mode 100644
index 000000000..c1ea927e0
--- /dev/null
+++ b/src/libnr/nr-translate.h
@@ -0,0 +1,34 @@
+#ifndef SEEN_NR_TRANSLATE_H
+#define SEEN_NR_TRANSLATE_H
+
+#include <libnr/nr-point.h>
+
+namespace NR {
+
+class translate {
+public:
+ Point offset;
+private:
+ translate();
+public:
+ explicit translate(Point const &p) : offset(p) {}
+ explicit translate(Coord const x, Coord const y) : offset(x, y) {}
+ Coord operator[](Dim2 const dim) const { return offset[dim]; }
+ Coord operator[](unsigned const dim) const { return offset[dim]; }
+};
+
+} /* namespace NR */
+
+
+#endif /* !SEEN_NR_TRANSLATE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-types-test.cpp b/src/libnr/nr-types-test.cpp
new file mode 100644
index 000000000..87a35fc68
--- /dev/null
+++ b/src/libnr/nr-types-test.cpp
@@ -0,0 +1,105 @@
+#include "../utest/utest.h"
+#include <libnr/nr-types.h>
+#include <libnr/nr-point-fns.h>
+#include <cmath>
+using NR::Point;
+using NR::X;
+using NR::Y;
+
+
+int main(int argc, char *argv[]) {
+ utest_start("Basic NR::Point operations");
+
+ UTEST_TEST("X,Y values") {
+ UTEST_ASSERT(X == 0);
+ UTEST_ASSERT(Y == 1);
+ }
+
+ NR::Point const a(1.5, 2.0);
+ UTEST_TEST("x,y constructor and operator[] const") {
+ UTEST_ASSERT(a[X] == 1.5);
+ UTEST_ASSERT(a[Y] == 2.0);
+ }
+
+ NR::Point const b(-2.0, 3.0);
+
+ UTEST_TEST("copy constructor") {
+ NR::Point a_copy(a);
+ UTEST_ASSERT(a == a_copy);
+ UTEST_ASSERT(!(a != a_copy));
+ }
+
+ UTEST_TEST("non-const operator[]") {
+ NR::Point a_copy(a);
+ a_copy[X] = -2.0;
+ UTEST_ASSERT(a_copy != a);
+ UTEST_ASSERT(a_copy != b);
+ a_copy[Y] = 3.0;
+ UTEST_ASSERT(a_copy == b);
+ }
+
+ NR::Point const ab(-0.5, 5.0);
+ UTEST_TEST("binary +, -") {
+ UTEST_ASSERT(a != b);
+ UTEST_ASSERT(a + b == ab);
+ UTEST_ASSERT(ab - a == b);
+ UTEST_ASSERT(ab - b == a);
+ UTEST_ASSERT(ab + a != b);
+ }
+
+ UTEST_TEST("unary-") {
+ UTEST_ASSERT(-a == Point(-a[X], -a[Y]));
+ }
+
+ UTEST_TEST("scale, divide") {
+ UTEST_ASSERT(-a == -1.0 * a);
+ UTEST_ASSERT(a + a + a == 3.0 * a);
+ UTEST_ASSERT(a / .5 == 2.0 * a);
+ }
+
+ UTEST_TEST("dot") {
+ UTEST_ASSERT( dot(a, b) == ( a[X] * b[X] +
+ a[Y] * b[Y] ) );
+ UTEST_ASSERT( dot(a, NR::rot90(a)) == 0.0 );
+ UTEST_ASSERT( dot(-a, NR::rot90(a)) == 0.0 );
+ }
+
+ double const small = pow(2.0, -1070);
+
+ Point const small_left(-small, 0.0);
+ Point const smallish_3_neg4(3.0 * small, -4.0 * small);
+
+ UTEST_TEST("L1, L2, LInfty norms") {
+ UTEST_ASSERT(L1(small_left) == small);
+ UTEST_ASSERT(L2(small_left) == small);
+ UTEST_ASSERT(LInfty(small_left) == small);
+
+ UTEST_ASSERT(L1(smallish_3_neg4) == 7.0 * small);
+ UTEST_ASSERT(L2(smallish_3_neg4) == 5.0 * small);
+ UTEST_ASSERT(LInfty(smallish_3_neg4) == 4.0 * small);
+ }
+
+ UTEST_TEST("operator+=") {
+ Point x(a);
+ x += b;
+ UTEST_ASSERT(x == ab);
+ }
+
+ UTEST_TEST("operator/=") {
+ Point x(a);
+ x /= .5;
+ UTEST_ASSERT(x == a + a);
+ }
+
+ UTEST_TEST("normalize") {
+ Point x(small_left);
+ x.normalize();
+ UTEST_ASSERT(x == Point(-1.0, 0.0));
+
+ x = smallish_3_neg4;
+ x.normalize();
+ UTEST_ASSERT(x == Point(0.6, -0.8));
+ }
+
+ return utest_end() ? 0 : 1;
+}
diff --git a/src/libnr/nr-types-test.h b/src/libnr/nr-types-test.h
new file mode 100644
index 000000000..d51db1be7
--- /dev/null
+++ b/src/libnr/nr-types-test.h
@@ -0,0 +1,145 @@
+// nr-types-test.h
+#include <cxxtest/TestSuite.h>
+
+#include "libnr/nr-types.h"
+#include "libnr/nr-point-fns.h"
+#include <cmath>
+using NR::Point;
+using NR::X;
+using NR::Y;
+
+class NrTypesTest : public CxxTest::TestSuite
+{
+public:
+ NrTypesTest() :
+ a( 1.5, 2.0 ),
+ b(-2.0, 3.0),
+ ab(-0.5, 5.0),
+ small(pow(2.0, -1070)),
+ small_left(-small, 0.0),
+ smallish_3_neg4(3.0 * small, -4.0 * small)
+ {}
+ virtual ~NrTypesTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static NrTypesTest *createSuite() { return new NrTypesTest(); }
+ static void destroySuite( NrTypesTest *suite ) { delete suite; }
+
+ NR::Point const a;
+ NR::Point const b;
+ NR::Point const ab;
+ double const small;
+ Point const small_left;
+ Point const smallish_3_neg4;
+
+
+ void testXYValues( void )
+ {
+ TS_ASSERT_EQUALS( X, 0 );
+ TS_ASSERT_EQUALS( Y, 1 );
+ }
+
+ void testXYCtorAndArrayConst(void)
+ {
+ TS_ASSERT_EQUALS( a[X], 1.5 );
+ TS_ASSERT_EQUALS( a[Y], 2.0 );
+ }
+
+ void testCopyCtor(void)
+ {
+ NR::Point a_copy(a);
+
+ TS_ASSERT_EQUALS( a, a_copy );
+ TS_ASSERT( !(a != a_copy) );
+ }
+
+ void testNonConstArrayOperator(void)
+ {
+ NR::Point a_copy(a);
+ a_copy[X] = -2.0;
+ TS_ASSERT_DIFFERS( a_copy, a );
+ TS_ASSERT_DIFFERS( a_copy, b );
+ a_copy[Y] = 3.0;
+ TS_ASSERT_EQUALS( a_copy, b );
+ }
+
+ void testBinaryPlusMinus(void)
+ {
+ TS_ASSERT_DIFFERS( a, b );
+ TS_ASSERT_EQUALS( a + b, ab );
+ TS_ASSERT_EQUALS( ab - a, b );
+ TS_ASSERT_EQUALS( ab - b, a );
+ TS_ASSERT_DIFFERS( ab + a, b );
+ }
+
+ void testUnaryMinus(void)
+ {
+ TS_ASSERT_EQUALS( -a, Point(-a[X], -a[Y]) );
+ }
+
+ void tetScaleDivide(void)
+ {
+ TS_ASSERT_EQUALS( -a, -1.0 * a );
+ TS_ASSERT_EQUALS( a + a + a, 3.0 * a );
+ TS_ASSERT_EQUALS( a / .5, 2.0 * a );
+ }
+
+ void testDot(void)
+ {
+ TS_ASSERT_EQUALS( dot(a, b), ( a[X] * b[X] +
+ a[Y] * b[Y] ) );
+ TS_ASSERT_EQUALS( dot(a, NR::rot90(a)), 0.0 );
+ TS_ASSERT_EQUALS( dot(-a, NR::rot90(a)), 0.0 );
+ }
+
+ void testL1L2LInftyNorms(void)
+ {
+ // TODO look at TS_ASSERT_DELTA
+
+ TS_ASSERT_EQUALS( L1(small_left), small );
+ TS_ASSERT_EQUALS( L2(small_left), small );
+ TS_ASSERT_EQUALS( LInfty(small_left), small );
+
+ TS_ASSERT_EQUALS( L1(smallish_3_neg4), 7.0 * small );
+ TS_ASSERT_EQUALS( L2(smallish_3_neg4), 5.0 * small );
+ TS_ASSERT_EQUALS( LInfty(smallish_3_neg4), 4.0 * small );
+ }
+
+ void testOperatorPlusEquals(void)
+ {
+ Point x(a);
+ x += b;
+ TS_ASSERT_EQUALS( x, ab );
+ }
+
+ void tetOperatorDivEquals(void)
+ {
+ Point x(a);
+ x /= .5;
+ TS_ASSERT_EQUALS( x, a + a );
+ }
+
+ void testNormalize(void)
+ {
+ Point x(small_left);
+ x.normalize();
+ TS_ASSERT_EQUALS( x, Point(-1.0, 0.0) );
+
+ x = smallish_3_neg4;
+ x.normalize();
+ TS_ASSERT_EQUALS( x, Point(0.6, -0.8) );
+ }
+
+};
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-types.cpp b/src/libnr/nr-types.cpp
new file mode 100644
index 000000000..98054a551
--- /dev/null
+++ b/src/libnr/nr-types.cpp
@@ -0,0 +1,68 @@
+/** \file
+ * Implements NR::Point::normalize()
+ */
+
+#include <libnr/nr-types.h>
+
+#include "isnan.h" //temporary fix for isnan()
+
+/** Scales this vector to make it a unit vector (within rounding error).
+ *
+ * The current version tries to handle infinite coordinates gracefully,
+ * but it's not clear that any callers need that.
+ *
+ * \pre *this != Point(0, 0).
+ * \pre Neither coordinate is NaN.
+ * \post L2(*this) very near 1.0.
+ */
+void NR::Point::normalize() {
+ double len = hypot(_pt[0], _pt[1]);
+ g_return_if_fail(len != 0);
+ g_return_if_fail(!isNaN(len));
+ static double const inf = 1e400;
+ if(len != inf) {
+ *this /= len;
+ } else {
+ unsigned n_inf_coords = 0;
+ /* Delay updating pt in case neither coord is infinite. */
+ NR::Point tmp;
+ for ( unsigned i = 0 ; i < 2 ; ++i ) {
+ if ( _pt[i] == inf ) {
+ ++n_inf_coords;
+ tmp[i] = 1.0;
+ } else if ( _pt[i] == -inf ) {
+ ++n_inf_coords;
+ tmp[i] = -1.0;
+ } else {
+ tmp[i] = 0.0;
+ }
+ }
+ switch (n_inf_coords) {
+ case 0:
+ /* Can happen if both coords are near +/-DBL_MAX. */
+ *this /= 4.0;
+ len = hypot(_pt[0], _pt[1]);
+ g_assert(len != inf);
+ *this /= len;
+ break;
+
+ case 1:
+ *this = tmp;
+ break;
+
+ case 2:
+ *this = sqrt(0.5) * tmp;
+ break;
+ }
+ }
+}
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-types.h b/src/libnr/nr-types.h
new file mode 100644
index 000000000..4802f5e0c
--- /dev/null
+++ b/src/libnr/nr-types.h
@@ -0,0 +1,47 @@
+#ifndef __NR_TYPES_H__
+#define __NR_TYPES_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Class-ifying NRPoint, Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This code is in public domain
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libnr/nr-coord.h>
+#include <libnr/nr-dim2.h>
+#include <libnr/nr-i-coord.h>
+#include <libnr/nr-matrix.h>
+#include <libnr/nr-point.h>
+#include <libnr/nr-point-l.h>
+#include <libnr/nr-point-ops.h>
+#include <libnr/nr-rect.h>
+#include <libnr/nr-rect-l.h>
+
+namespace NR {
+
+class Rect;
+class Matrix;
+
+} /* namespace NR */
+
+
+#endif /* !__NR_TYPES_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr-values.cpp b/src/libnr/nr-values.cpp
new file mode 100644
index 000000000..bb310cc49
--- /dev/null
+++ b/src/libnr/nr-values.cpp
@@ -0,0 +1,23 @@
+#define __NR_VALUES_C__
+
+#include <libnr/nr-rect-l.h>
+
+
+/*
+The following predefined objects are for reference
+and comparison.
+*/
+NRMatrix NR_MATRIX_IDENTITY =
+ {{1.0, 0.0, 0.0, 1.0, 0.0, 0.0}};
+NRRect NR_RECT_EMPTY =
+ {NR_HUGE, NR_HUGE, -NR_HUGE, -NR_HUGE};
+NRRectL NR_RECT_L_EMPTY =
+ {NR_HUGE_L, NR_HUGE_L, -NR_HUGE_L, -NR_HUGE_L};
+NRRectL NR_RECT_S_EMPTY =
+ {NR_HUGE_S, NR_HUGE_S, -NR_HUGE_S, -NR_HUGE_S};
+
+/** component_vectors[i] is like $e_i$ in common mathematical usage;
+ or equivalently $I_i$ (where $I$ is the identity matrix). */
+NR::Point const component_vectors[] = {NR::Point(1., 0.),
+ NR::Point(0., 1.)};
+
diff --git a/src/libnr/nr-values.h b/src/libnr/nr-values.h
new file mode 100644
index 000000000..7fa00d809
--- /dev/null
+++ b/src/libnr/nr-values.h
@@ -0,0 +1,45 @@
+#ifndef __NR_VALUES_H__
+#define __NR_VALUES_H__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#include <libnr/nr-forward.h>
+
+#define NR_EPSILON 1e-18
+
+#define NR_HUGE 1e18
+#define NR_HUGE_L (0x7fffffff)
+#define NR_HUGE_S (0x7fff)
+
+/*
+The following predefined objects are for reference
+and comparison. They are defined in nr-values.cpp
+*/
+extern NRMatrix NR_MATRIX_IDENTITY;
+extern NRRect NR_RECT_EMPTY;
+extern NRRectL NR_RECT_L_EMPTY;
+extern NRRectL NR_RECT_S_EMPTY;
+
+/** component_vectors[i] has 1.0 at position i, and 0.0 elsewhere
+ (i.e. in the other position). */
+extern NR::Point const component_vectors[2];
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/libnr/nr_config.h.mingw b/src/libnr/nr_config.h.mingw
new file mode 100644
index 000000000..6992cc6fc
--- /dev/null
+++ b/src/libnr/nr_config.h.mingw
@@ -0,0 +1,12 @@
+#define NR_SIZEOF_CHAR 1
+#define NR_SIZEOF_SHORT 2
+#define NR_SIZEOF_INT 4
+#define NR_SIZEOF_LONG 4
+
+typedef signed char NRByte;
+typedef unsigned char NRUByte;
+typedef signed short NRShort;
+typedef unsigned short NRUShort;
+typedef signed int NRLong;
+typedef unsigned long NRULong;
+
diff --git a/src/libnr/nr_config.h.win32 b/src/libnr/nr_config.h.win32
new file mode 100644
index 000000000..e0bfbda3f
--- /dev/null
+++ b/src/libnr/nr_config.h.win32
@@ -0,0 +1,14 @@
+#define NR_SIZEOF_CHAR 1
+#define NR_SIZEOF_SHORT 2
+#define NR_SIZEOF_INT 4
+#define NR_SIZEOF_LONG 4
+
+typedef signed char NRByte;
+typedef unsigned char NRUByte;
+typedef signed short NRShort;
+typedef unsigned short NRUShort;
+typedef signed int NRLong;
+typedef unsigned long NRULong;
+
+
+
diff --git a/src/libnr/nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP.S b/src/libnr/nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP.S
new file mode 100644
index 000000000..db2cbec5a
--- /dev/null
+++ b/src/libnr/nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP.S
@@ -0,0 +1,125 @@
+ .file "nr-compose.c"
+
+# Ensure Inkscape is execshield protected
+ .section .note.GNU-stack
+ .previous
+
+ .text
+ .align 2
+.globl nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP
+ .type nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP,@function
+
+/*
+ * This code is in public domain
+ *
+ * c 32(%ebp)
+ * srs 28(%ebp)
+ * spx 24(%ebp)
+ * rs 20(%ebp)
+ * h 16(%ebp)
+ * w 12(%ebp)
+ * px 8(%ebp)
+ * r -8(%ebp)
+ * g -12(%ebp)
+ * b -16(%ebp)
+ * a -20(%ebp)
+ * s -24(%ebp) -> %esi
+ * d -28(%ebp) -> %edi
+ * x -32(%ebp) -> %ebx
+ * y -36(%ebp)
+ * ca -40(%ebp)
+ *
+ * mm0 Fg
+ * mm1 FgA
+ * mm2 FgPre
+ * mm3
+ * mm4
+ * mm5
+ * mm6 128
+ * mm7 0
+ *
+*/
+
+nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+ subl $36, %esp
+ pushl %edi
+ pushl %esi
+
+/* Load %mm7 with [0 0 0 0] */
+ movl $0, %eax
+ movd %eax, %mm7
+
+/* Load %mm6 with [128 128 128 128] */
+ movl $0x80808080, %eax
+ movd %eax, %mm6
+ punpcklbw %mm7, %mm6
+
+/* FgC -> %mm0 */
+ movl 32(%ebp), %eax
+ movd (%eax), %mm0
+ punpcklbw %mm7, %mm0
+
+/* for (y = ...) */
+ movl 16(%ebp), %ecx
+.fory:
+
+/* d = px */
+/* s = spx */
+ movl 8(%ebp), %edi
+ movl 24(%ebp), %esi
+
+/* for (x = ...) */
+ movl 12(%ebp), %ebx
+.forx:
+
+/* [m m m m] -> %mm1 */
+ movzbl (%esi), %eax
+ testb $0xff, %al
+ jz .clip
+ movd %eax, %mm1
+ punpcklwd %mm1, %mm1
+ punpckldq %mm1, %mm1
+
+/* Fg -> mm2 */
+ movq %mm0, %mm2
+ pmullw %mm1, %mm2
+ paddw %mm6, %mm2
+ movq %mm2, %mm3
+ psrlw $8, %mm3
+ paddw %mm3, %mm2
+ psrlw $8, %mm2
+
+/* Store pixel */
+ packuswb %mm2, %mm2
+ movd %mm2, (%edi)
+
+.clip:
+ addl $4, %edi
+ incl %esi
+
+ decl %ebx
+ jnz .forx
+
+ movl 20(%ebp), %eax
+ addl %eax, 8(%ebp)
+ movl 28(%ebp), %eax
+ addl %eax, 24(%ebp)
+
+ decl %ecx
+ jnz .fory
+
+.exit:
+ emms
+ popl %esi
+ popl %edi
+ addl $36, %esp
+ popl %ebx
+ popl %ebp
+ ret
+
+.Lfe1:
+ .size nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP,.Lfe1-nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP
+ .ident "GCC: (GNU) 3.2"
diff --git a/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP.S b/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP.S
new file mode 100644
index 000000000..fe1d9be57
--- /dev/null
+++ b/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP.S
@@ -0,0 +1,231 @@
+ .file "nr-compose.c"
+
+# Ensure Inkscape is execshield protected
+ .section .note.GNU-stack
+ .previous
+
+ .text
+ .align 2
+.globl nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP
+ .type nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP,@function
+
+/*
+ * This code is in public domain
+ *
+ * c 32(%ebp)
+ * srs 28(%ebp)
+ * spx 24(%ebp)
+ * rs 20(%ebp)
+ * h 16(%ebp)
+ * w 12(%ebp)
+ * px 8(%ebp)
+ * r -8(%ebp)
+ * g -12(%ebp)
+ * b -16(%ebp)
+ * a -20(%ebp)
+ * s -24(%ebp) -> %esi
+ * d -28(%ebp) -> %edi
+ * x -32(%ebp) -> %ebx
+ * y -36(%ebp)
+ * ca -40(%ebp)
+ *
+ * mm0 Fg
+ * mm1 MMMM
+ * mm2 FgM
+ * mm3
+ * mm4
+ * mm5 255
+ * mm6 128
+ * mm7 0
+ *
+*/
+
+nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+ subl $36, %esp
+ pushl %edi
+ pushl %esi
+
+/* Load %mm7 with [0 0 0 0] */
+ movl $0, %eax
+ movd %eax, %mm7
+
+/* Load %mm6 with [128 128 128 128] */
+ movl $0x80808080, %eax
+ movd %eax, %mm6
+ punpcklbw %mm7, %mm6
+
+/* Load %mm5 with [255 255 255 255] */
+ movl $0xffffffff, %eax
+ movd %eax, %mm5
+ punpcklbw %mm7, %mm5
+
+/* FgC -> %mm0 */
+ movl 32(%ebp), %eax
+ movd (%eax), %mm0
+ punpcklbw %mm7, %mm0
+
+/* Check full opacity */
+ cmpb $0xff, %al
+ jz .opaque
+
+/* for (y = ...) */
+ movl 16(%ebp), %ecx
+.fory:
+
+/* d = px */
+/* s = spx */
+ movl 8(%ebp), %edi
+ movl 24(%ebp), %esi
+
+/* for (x = ...) */
+ movl 12(%ebp), %ebx
+.forx:
+
+/* [m m m m] -> %mm1 */
+ movzbl (%esi), %eax
+ testb $0xff, %al
+ jz .clip
+ movd %eax, %mm1
+ punpcklwd %mm1, %mm1
+ punpckldq %mm1, %mm1
+
+/* Fg -> mm2 */
+ movq %mm0, %mm2
+ pmullw %mm1, %mm2
+ paddw %mm6, %mm2
+ movq %mm2, %mm3
+ psrlw $8, %mm3
+ paddw %mm3, %mm2
+ psrlw $8, %mm2
+
+/* [255 - FgA] -> mm1 */
+ movq %mm2, %mm1
+ punpckhwd %mm1, %mm1
+ punpckhdq %mm1, %mm1
+ pxor %mm5, %mm1
+
+/* Bg -> mm3 */
+ movd (%edi), %mm3
+ punpcklbw %mm7, %mm3
+
+/* Fg + ((255 - FgA) * Bg) / 255 */
+ pmullw %mm1, %mm3
+ paddw %mm6, %mm3
+ movq %mm3, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm3
+ psrlw $8, %mm3
+ paddw %mm2, %mm3
+
+/* Store pixel */
+ packuswb %mm3, %mm3
+ movd %mm3, (%edi)
+
+.clip:
+ addl $4, %edi
+ incl %esi
+
+ decl %ebx
+ jnz .forx
+
+ movl 20(%ebp), %eax
+ addl %eax, 8(%ebp)
+ movl 28(%ebp), %eax
+ addl %eax, 24(%ebp)
+
+ decl %ecx
+ jnz .fory
+
+.exit:
+ emms
+ popl %esi
+ popl %edi
+ addl $36, %esp
+ popl %ebx
+ popl %ebp
+ ret
+
+.opaque:
+/* for (y = ...) */
+ movl 16(%ebp), %ecx
+.o_fory:
+
+/* d = px */
+/* s = spx */
+ movl 8(%ebp), %edi
+ movl 24(%ebp), %esi
+
+/* for (x = ...) */
+ movl 12(%ebp), %ebx
+.o_forx:
+
+/* [m m m m] -> %mm1 */
+ movzbl (%esi), %eax
+ testb $0xff, %al
+ jz .o_clip
+ cmpb $0xff, %al
+ jz .o_full
+ movd %eax, %mm1
+ punpcklwd %mm1, %mm1
+ punpckldq %mm1, %mm1
+
+/* Fg -> mm2 */
+ movq %mm0, %mm2
+ pmullw %mm1, %mm2
+ paddw %mm6, %mm2
+ movq %mm2, %mm3
+ psrlw $8, %mm3
+ paddw %mm3, %mm2
+ psrlw $8, %mm2
+
+/* [255 - FgA] -> mm1 */
+ movq %mm2, %mm1
+ punpckhwd %mm1, %mm1
+ punpckhdq %mm1, %mm1
+ pxor %mm5, %mm1
+
+/* Bg -> mm3 */
+ movd (%edi), %mm3
+ punpcklbw %mm7, %mm3
+
+/* Fg + ((255 - FgA) * Bg) / 255 */
+ pmullw %mm1, %mm3
+ paddw %mm6, %mm3
+ movq %mm3, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm3
+ psrlw $8, %mm3
+ paddw %mm2, %mm3
+
+ jmp .o_store
+
+.o_full:
+ movq %mm0, %mm3
+
+.o_store:
+/* Store pixel */
+ packuswb %mm3, %mm3
+ movd %mm3, (%edi)
+
+.o_clip:
+ addl $4, %edi
+ incl %esi
+
+ decl %ebx
+ jnz .o_forx
+
+ movl 20(%ebp), %eax
+ addl %eax, 8(%ebp)
+ movl 28(%ebp), %eax
+ addl %eax, 24(%ebp)
+
+ decl %ecx
+ jnz .o_fory
+ jmp .exit
+
+.Lfe1:
+ .size nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP,.Lfe1-nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP
+ .ident "GCC: (GNU) 3.2"
diff --git a/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S b/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S
new file mode 100644
index 000000000..e30056af2
--- /dev/null
+++ b/src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S
@@ -0,0 +1,414 @@
+ .file "nr-compose-transform.c"
+
+# Ensure Inkscape is execshield protected
+ .section .note.GNU-stack
+ .previous
+
+ .text
+ .align 2
+.globl nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0
+ .type nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0,@function
+
+/*
+ * This code is in public domain
+ *
+ */
+
+nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+ subl $48, %esp
+ pushl %edi
+ pushl %esi
+
+/* Load %mm7 with [0 0 0 0] */
+ movl $0, %eax
+ movd %eax, %mm7
+
+/* Load %mm6 with [128 128 128 128] */
+ movl $0x80808080, %eax
+ movd %eax, %mm6
+ punpcklbw %mm7, %mm6
+
+/* Load %mm5 with [255 255 255 255] */
+ movl $0xffffffff, %eax
+ movd %eax, %mm5
+ punpcklbw %mm7, %mm5
+
+/* Load %mm0 with [a a a a] */
+ movzbl 44(%ebp), %eax
+ movd %eax, %mm0
+ punpcklwd %mm0, %mm0
+ punpckldq %mm0, %mm0
+
+ movl 8(%ebp), %eax
+ movl %eax, -8(%ebp)
+ movl 40(%ebp), %eax
+ addl $16, %eax
+ movl (%eax), %eax
+ movl %eax, -12(%ebp)
+ movl 40(%ebp), %eax
+ addl $20, %eax
+ movl (%eax), %eax
+ movl %eax, -16(%ebp)
+ movl $0, -24(%ebp)
+.L29:
+ movl -24(%ebp), %eax
+ cmpl 16(%ebp), %eax
+ jl .L32
+ jmp .L28
+.L32:
+ movl -8(%ebp), %edi
+
+ movl -12(%ebp), %eax
+ movl %eax, %esi
+ movl -16(%ebp), %eax
+ movl %eax, -36(%ebp)
+
+ movl 12(%ebp), %ebx
+.for_x_0:
+
+ movl %esi, %ecx
+ cmpl $0, %ecx
+ js .clip_0
+ sarl $12, %ecx
+ cmpl 28(%ebp), %ecx
+ jge .clip_0
+ shll $2, %ecx
+
+ movl -36(%ebp), %eax
+ cmpl $0, %eax
+ js .clip_0
+ sarl $12, %eax
+ cmpl 32(%ebp), %eax
+ jge .clip_0
+ imull 36(%ebp), %eax
+
+ addl %ecx, %eax
+ addl 24(%ebp), %eax
+
+/* Fg -> %mm1 */
+ movl (%eax), %eax
+ testl $0xff000000, %eax
+ jz .clip_0
+ movd %eax, %mm1
+ punpcklbw %mm7, %mm1
+
+/* [a a a 255] -> %mm3 */
+ shrl $24, %eax
+ movl $0x10101, %edx
+ mull %edx
+ orl $0xff000000, %eax
+ movd %eax, %mm3
+ punpcklbw %mm7, %mm3
+
+/* [Fg * a] -> mm1 */
+ pmullw %mm3, %mm1
+ paddw %mm6, %mm1
+ movq %mm1, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm1
+ psrlw $8, %mm1
+
+/* Multiply by alpha */
+ pmullw %mm0, %mm1
+ paddw %mm6, %mm1
+ movq %mm1, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm1
+ psrlw $8, %mm1
+
+/* [255 - FgA] -> mm2 */
+ movq %mm1, %mm2
+ punpckhwd %mm2, %mm2
+ punpckhdq %mm2, %mm2
+ pxor %mm5, %mm2
+
+/* Bg -> mm3 */
+ movd (%edi), %mm3
+ punpcklbw %mm7, %mm3
+
+/* Fg + ((255 - FgA) * Bg) / 255 */
+
+ pmullw %mm2, %mm3
+ paddw %mm6, %mm3
+ movq %mm3, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm3
+ psrlw $8, %mm3
+ paddw %mm1, %mm3
+
+/* Store pixel */
+ packuswb %mm3, %mm3
+ movd %mm3, (%edi)
+
+.clip_0:
+.L37:
+ movl 40(%ebp), %ecx
+ movl (%ecx), %edx
+ addl %edx, %esi
+ movl 4(%ecx), %edx
+ addl %edx, -36(%ebp)
+
+ addl $4, %edi
+
+ decl %ebx
+ jnz .for_x_0
+
+.L34:
+ movl 8(%ecx), %edx
+ addl %edx, -12(%ebp)
+ movl 12(%ecx), %edx
+ addl %edx, -16(%ebp)
+
+ movl 20(%ebp), %edx
+ leal -8(%ebp), %eax
+ addl %edx, (%eax)
+ leal -24(%ebp), %eax
+ incl (%eax)
+ jmp .L29
+.L28:
+ emms
+ popl %esi
+ popl %edi
+ addl $48, %esp
+ popl %ebx
+ popl %ebp
+ ret
+.Lfe2:
+ .size nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0,.Lfe2-nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_0
+
+/*
+ *
+ * dbits 52(%ebp)
+ * alpha 48(%ebp)
+ * FF_S 44(%ebp)
+ *
+ * d -32(%ebp) -> %edi
+ * i -60(%ebp) -> %esi
+ * sx -64(%ebp) -> %ebx
+ * sy -68(%ebp)
+ * s -72(%ebp)
+ *
+ * %mm0 a a a a
+ * %mm1 FgA
+ * %mm2 SumFgA
+ * %mm3 a a a 255
+ * %mm4
+*/
+
+ .align 2
+.globl nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n
+ .type nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n,@function
+nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+ subl $72, %esp
+ pushl %edi
+ pushl %esi
+
+/* Load %mm7 with [0 0 0 0] */
+ movl $0, %eax
+ movd %eax, %mm7
+
+/* Load %mm6 with [128 128 128 128] */
+ movl $0x80808080, %eax
+ movd %eax, %mm6
+ punpcklbw %mm7, %mm6
+
+/* Load %mm5 with [255 255 255 255] */
+ movl $0xffffffff, %eax
+ movd %eax, %mm5
+ punpcklbw %mm7, %mm5
+
+/* Load %mm0 with [a a a a] */
+ movzbl 48(%ebp), %eax
+ movd %eax, %mm0
+ punpcklwd %mm0, %mm0
+ punpckldq %mm0, %mm0
+
+ movl $1, %eax
+ movzbl 52(%ebp), %ecx
+ sall %cl, %eax
+ movl %eax, -8(%ebp)
+ movl 8(%ebp), %eax
+ movl %eax, -12(%ebp)
+ movl 40(%ebp), %eax
+ addl $16, %eax
+ movl (%eax), %eax
+ movl %eax, -16(%ebp)
+ movl 40(%ebp), %eax
+ addl $20, %eax
+ movl (%eax), %eax
+ movl %eax, -20(%ebp)
+ movl $0, -28(%ebp)
+.L44:
+ movl -28(%ebp), %eax
+ cmpl 16(%ebp), %eax
+ jl .L47
+ jmp .exit_n
+.L47:
+ movl -12(%ebp), %eax
+ movl %eax, -32(%ebp)
+ movl -16(%ebp), %eax
+ movl %eax, -36(%ebp)
+ movl -20(%ebp), %eax
+ movl %eax, -40(%ebp)
+ movl $0, -24(%ebp)
+.L48:
+ movl -24(%ebp), %eax
+ cmpl 12(%ebp), %eax
+ jl .L51
+ jmp .L49
+.L51:
+
+/* Zero accumulator */
+ movq %mm7, %mm2
+
+/* Set i to dptr (size - 1) */
+ movl -8(%ebp), %esi
+ sub $1, %esi
+ shll $3, %esi
+
+ movl 44(%ebp), %edi
+ movl -36(%ebp), %ecx
+
+.for_i_n:
+ movl (%edi,%esi), %ebx
+ addl %ecx, %ebx
+/* Test negative before shift */
+ cmpl $0, %ebx
+ js .next_i_n
+ sarl $12, %ebx
+ cmpl 28(%ebp), %ebx
+ jge .next_i_n
+/* We multiply sx by 4 here */
+ shll $2, %ebx
+
+ movl 4(%edi,%esi), %eax
+ addl -40(%ebp), %eax
+/* Test negative before shift */
+ cmpl $0, %eax
+ js .next_i_n
+ sarl $12, %eax
+ cmpl 32(%ebp), %eax
+ jge .next_i_n
+/* We multiply sy by srs here */
+ imull 36(%ebp), %eax
+
+ addl %ebx, %eax
+ addl 24(%ebp), %eax
+
+/* Fg -> %mm1 */
+ movl (%eax), %eax
+ testl $0xff000000, %eax
+ jz .next_i_n
+ movd %eax, %mm1
+ punpcklbw %mm7, %mm1
+
+/* [a a a 255] -> %mm3 */
+ shrl $24, %eax
+ movl $0x10101, %edx
+ mull %edx
+ orl $0xff000000, %eax
+ movd %eax, %mm3
+ punpcklbw %mm7, %mm3
+
+/* [Fg * a] -> mm1 */
+ pmullw %mm3, %mm1
+ paddw %mm6, %mm1
+ movq %mm1, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm1
+ psrlw $8, %mm1
+
+/* Add to accumulator */
+ paddw %mm1, %mm2
+
+.next_i_n:
+ subl $8, %esi
+ jnb .for_i_n
+
+/* Divide components by sample size */
+ movd 52(%ebp), %mm3
+ psrlw %mm3, %mm2
+
+/* Multiply by alpha */
+ pmullw %mm0, %mm2
+ paddw %mm6, %mm2
+ movq %mm2, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm2
+ psrlw $8, %mm2
+
+/* [255 - FgA] -> mm1 */
+ movq %mm2, %mm1
+ punpckhwd %mm1, %mm1
+ punpckhdq %mm1, %mm1
+ pxor %mm5, %mm1
+
+ movl -32(%ebp), %edi
+/* Bg -> mm3 */
+ movd (%edi), %mm3
+ punpcklbw %mm7, %mm3
+
+/* Fg + ((255 - FgA) * Bg) / 255 */
+
+ pmullw %mm1, %mm3
+ paddw %mm6, %mm3
+ movq %mm3, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm3
+ psrlw $8, %mm3
+ paddw %mm2, %mm3
+
+/* Store pixel */
+ packuswb %mm3, %mm3
+ movd %mm3, (%edi)
+
+.L58:
+ movl 40(%ebp), %eax
+ movl (%eax), %edx
+ leal -36(%ebp), %eax
+ addl %edx, (%eax)
+ movl 40(%ebp), %eax
+ addl $4, %eax
+ movl (%eax), %edx
+ leal -40(%ebp), %eax
+ addl %edx, (%eax)
+ leal -32(%ebp), %eax
+ addl $4, (%eax)
+ leal -24(%ebp), %eax
+ incl (%eax)
+ jmp .L48
+.L49:
+ movl 40(%ebp), %eax
+ addl $8, %eax
+ movl (%eax), %edx
+ leal -16(%ebp), %eax
+ addl %edx, (%eax)
+ movl 40(%ebp), %eax
+ addl $12, %eax
+ movl (%eax), %edx
+ leal -20(%ebp), %eax
+ addl %edx, (%eax)
+ movl 20(%ebp), %edx
+ leal -12(%ebp), %eax
+ addl %edx, (%eax)
+ leal -28(%ebp), %eax
+ incl (%eax)
+ jmp .L44
+
+.exit_n:
+ emms
+ popl %esi
+ popl %edi
+ addl $72, %esp
+ popl %ebx
+ popl %ebp
+ ret
+.Lfe3:
+ .size nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n,.Lfe3-nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM_n
+ .ident "GCC: (GNU) 3.2"
diff --git a/src/libnr/nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P.S b/src/libnr/nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P.S
new file mode 100644
index 000000000..37261e572
--- /dev/null
+++ b/src/libnr/nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P.S
@@ -0,0 +1,227 @@
+ .file "nr-compose.c"
+
+# Ensure Inkscape is execshield protected
+ .section .note.GNU-stack
+ .previous
+
+ .text
+ .align 2
+.globl nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P
+ .type nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P,@function
+
+/*
+ * This code is in public domain
+ *
+ * alpha 32(%ebp)
+ * srs 28(%ebp)
+ * spx 24(%ebp)
+ * rs 20(%ebp)
+ * h 16(%ebp)
+ * w 12(%ebp)
+ * px 8(%ebp)
+ * r -8(%ebp)
+ * g -12(%ebp)
+ * b -16(%ebp)
+ * a -20(%ebp)
+ * s -24(%ebp) -> %esi
+ * d -28(%ebp) -> %edi
+ * x -32(%ebp) -> %ebx
+ * y -36(%ebp)
+ * ca -40(%ebp)
+ *
+ * mm0 A
+ * mm1 FgA
+ * mm2 FgPre
+ * mm3
+ * mm4
+ * mm5 255
+ * mm6 128
+ * mm7 0
+ *
+*/
+
+nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P:
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %ebx
+ subl $36, %esp
+ pushl %edi
+ pushl %esi
+
+/* Load %mm7 with [0 0 0 0] */
+ movl $0, %eax
+ movd %eax, %mm7
+
+/* Load %mm6 with [128 128 128 128] */
+ movl $0x80808080, %eax
+ movd %eax, %mm6
+ punpcklbw %mm7, %mm6
+
+/* Load %mm5 with [255 255 255 255] */
+ movl $0xffffffff, %eax
+ movd %eax, %mm5
+ punpcklbw %mm7, %mm5
+
+/* Load %mm0 with [a a a a] */
+/* Check full opacity */
+ movzbl 32(%ebp), %eax
+ cmpb $0xff, %al
+ jz .opaque
+ movd %eax, %mm0
+ punpcklwd %mm0, %mm0
+ punpckldq %mm0, %mm0
+
+/* for (y = ...) */
+ movl 16(%ebp), %ecx
+.fory:
+
+/* d = px */
+/* s = spx */
+ movl 8(%ebp), %edi
+ movl 24(%ebp), %esi
+
+/* for (x = ...) */
+ movl 12(%ebp), %ebx
+.forx:
+
+/* Fg -> %mm1 */
+/* fixme: Do we have to bother about alignment here? (Lauris) */
+ movl (%esi), %eax
+ testl $0xff000000, %eax
+ jz .clip
+ movd %eax, %mm1
+ punpcklbw %mm7, %mm1
+
+/* [Fg * a] -> mm1 */
+ pmullw %mm0, %mm1
+ paddw %mm6, %mm1
+ movq %mm1, %mm2
+ psrlw $8, %mm2
+ paddw %mm2, %mm1
+ psrlw $8, %mm1
+
+/* [255 - FgA] -> mm2 */
+ movq %mm1, %mm2
+ punpckhwd %mm2, %mm2
+ punpckhdq %mm2, %mm2
+ pxor %mm5, %mm2
+
+/* Bg -> mm3 */
+ movd (%edi), %mm3
+ punpcklbw %mm7, %mm3
+
+/* Fg + ((255 - FgA) * Bg) / 255 */
+ pmullw %mm2, %mm3
+ paddw %mm6, %mm3
+ movq %mm3, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm3
+ psrlw $8, %mm3
+ paddw %mm1, %mm3
+
+/* Store pixel */
+ packuswb %mm3, %mm3
+ movd %mm3, %eax
+ movb %al, 0(%edi)
+ shrl $8, %eax
+ movb %al, 1(%edi)
+ shrl $8, %eax
+ movb %al, 2(%edi)
+
+.clip:
+ addl $3, %edi
+ addl $4, %esi
+
+ decl %ebx
+ jnz .forx
+
+ movl 20(%ebp), %eax
+ addl %eax, 8(%ebp)
+ movl 28(%ebp), %eax
+ addl %eax, 24(%ebp)
+
+ decl %ecx
+ jnz .fory
+
+.exit:
+ emms
+ popl %esi
+ popl %edi
+ addl $36, %esp
+ popl %ebx
+ popl %ebp
+ ret
+
+.opaque:
+/* for (y = ...) */
+ movl 16(%ebp), %ecx
+.o_fory:
+
+/* d = px */
+/* s = spx */
+ movl 8(%ebp), %edi
+ movl 24(%ebp), %esi
+
+/* for (x = ...) */
+ movl 12(%ebp), %ebx
+.o_forx:
+
+/* Fg -> %mm1 */
+/* fixme: Do we have to bother about alignment here? (Lauris) */
+ movl (%esi), %eax
+ testl $0xff000000, %eax
+ jz .o_clip
+ cmpl $0xff000000, %eax
+ jnb .o_store
+ movd %eax, %mm1
+ punpcklbw %mm7, %mm1
+
+/* [255 - FgA] -> mm2 */
+ movq %mm1, %mm2
+ punpckhwd %mm2, %mm2
+ punpckhdq %mm2, %mm2
+ pxor %mm5, %mm2
+
+/* Bg -> mm3 */
+ movd (%edi), %mm3
+ punpcklbw %mm7, %mm3
+
+/* Fg + ((255 - FgA) * Bg) / 255 */
+ pmullw %mm2, %mm3
+ paddw %mm6, %mm3
+ movq %mm3, %mm4
+ psrlw $8, %mm4
+ paddw %mm4, %mm3
+ psrlw $8, %mm3
+ paddw %mm1, %mm3
+
+/* Store pixel */
+ packuswb %mm3, %mm3
+ movd %mm3, %eax
+.o_store:
+ movb %al, 0(%edi)
+ shrl $8, %eax
+ movb %al, 1(%edi)
+ shrl $8, %eax
+ movb %al, 2(%edi)
+
+.o_clip:
+ addl $3, %edi
+ addl $4, %esi
+
+ decl %ebx
+ jnz .o_forx
+
+ movl 20(%ebp), %eax
+ addl %eax, 8(%ebp)
+ movl 28(%ebp), %eax
+ addl %eax, 24(%ebp)
+
+ decl %ecx
+ jnz .o_fory
+
+ jmp .exit
+
+.Lfe1:
+ .size nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P,.Lfe1-nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P
+ .ident "GCC: (GNU) 3.2"
diff --git a/src/libnr/testnr.cpp b/src/libnr/testnr.cpp
new file mode 100644
index 000000000..12dce4c52
--- /dev/null
+++ b/src/libnr/testnr.cpp
@@ -0,0 +1,92 @@
+#define __TESTNR_C__
+
+/*
+ * Pixel buffer rendering library
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * This code is in public domain
+ */
+
+#if defined (_WIN32) || defined (__WIN32__)
+# include <windows.h>
+#include <glib.h>
+#endif
+
+
+#include "nr-blit.h"
+
+static double
+get_time (void)
+{
+ GTimeVal tv;
+ g_get_current_time (&tv);
+ return tv.tv_sec + 1e-6 * tv.tv_usec;
+}
+
+static unsigned int
+rand_byte (void)
+{
+ return (int) (256.0 * rand () / (RAND_MAX + 1.0));
+}
+
+int
+main (int argc, const char **argv)
+{
+ double start, end;
+ NRPixBlock d, m[16];
+ int count, i;
+
+ srand (time (NULL));
+
+ printf ("Initializing buffers\n");
+
+ /* Destination */
+ nr_pixblock_setup_fast (&d, NR_PIXBLOCK_MODE_R8G8B8A8P, 0, 0, 64, 64, 1);
+ d.empty = 0;
+
+ /* Masks */
+ for (i = 0; i < 16; i++) {
+ int r, b, c;
+ nr_pixblock_setup_fast (&m[i], NR_PIXBLOCK_MODE_A8, 0, 0, 64, 64, 0);
+ for (r = 0; r < 64; r++) {
+ unsigned int q;
+ unsigned char *p;
+ p = NR_PIXBLOCK_PX (&m[i]) + r * m[i].rs;
+ for (b = 0; b < 8; b++) {
+ q = rand_byte ();
+ if (q < 120) {
+ for (c = 0; c < 8; c++) *p++ = 0;
+ } else if (q < 240) {
+ for (c = 0; c < 8; c++) *p++ = 255;
+ } else {
+ for (c = 0; c < 8; c++) *p++ = rand_byte ();
+ }
+ }
+ }
+ m[i].empty = 0;
+ }
+
+ printf ("Random transparency\n");
+ count = 0;
+ start = end = get_time ();
+ while ((end - start) < 5.0) {
+ unsigned char r, g, b, a;
+ r = rand_byte ();
+ g = rand_byte ();
+ b = rand_byte ();
+ a = rand_byte ();
+
+ for (i = 0; i < 16; i++) {
+ nr_blit_pixblock_mask_rgba32 (&d, &m[i], (a << 24) | (g << 16) | (b << 8) | a);
+ count += 1;
+ }
+ end = get_time ();
+ }
+ printf ("Did %d [64x64] random buffers in %f sec\n", count, end - start); // localizing ok
+ printf ("%f buffers per second\n", count / (end - start)); // localizing ok
+ printf ("%f pixels per second\n", count * (64 * 64) / (end - start)); // localizing ok
+
+ return 0;
+}