From 179fa413b047bede6e32109e2ce82437c5fb8d34 Mon Sep 17 00:00:00 2001 From: MenTaLguY Date: Mon, 16 Jan 2006 02:36:01 +0000 Subject: moving trunk for module inkscape (bzr r1) --- src/libnr/.cvsignore | 13 + src/libnr/Makefile_insert | 167 ++++ src/libnr/have_mmx.S | 47 + src/libnr/in-svg-plane-test.cpp | 58 ++ src/libnr/in-svg-plane-test.h | 81 ++ src/libnr/in-svg-plane.h | 33 + src/libnr/libnr.def | 89 ++ src/libnr/makefile.in | 17 + src/libnr/n-art-bpath.h | 61 ++ src/libnr/nr-blit.cpp | 300 +++++++ src/libnr/nr-blit.h | 32 + src/libnr/nr-compose-transform.cpp | 360 ++++++++ src/libnr/nr-compose-transform.h | 43 + src/libnr/nr-compose.cpp | 986 +++++++++++++++++++++ src/libnr/nr-compose.h | 69 ++ src/libnr/nr-convex-hull-ops.h | 29 + src/libnr/nr-convex-hull.h | 49 + src/libnr/nr-coord.h | 29 + src/libnr/nr-dim2.h | 22 + src/libnr/nr-forward.h | 42 + src/libnr/nr-gradient.cpp | 317 +++++++ src/libnr/nr-gradient.h | 47 + src/libnr/nr-i-coord.h | 25 + src/libnr/nr-macros.h | 71 ++ src/libnr/nr-matrix-div.cpp | 22 + src/libnr/nr-matrix-div.h | 21 + src/libnr/nr-matrix-fns.cpp | 54 ++ src/libnr/nr-matrix-fns.h | 50 ++ src/libnr/nr-matrix-ops.h | 45 + src/libnr/nr-matrix-rotate-ops.cpp | 18 + src/libnr/nr-matrix-rotate-ops.h | 20 + src/libnr/nr-matrix-scale-ops.cpp | 37 + src/libnr/nr-matrix-scale-ops.h | 14 + src/libnr/nr-matrix-test.cpp | 197 ++++ src/libnr/nr-matrix-test.h | 221 +++++ src/libnr/nr-matrix-translate-ops.cpp | 26 + src/libnr/nr-matrix-translate-ops.h | 36 + src/libnr/nr-matrix.cpp | 607 +++++++++++++ src/libnr/nr-matrix.h | 453 ++++++++++ src/libnr/nr-maybe.h | 140 +++ src/libnr/nr-object.cpp | 295 ++++++ src/libnr/nr-object.h | 157 ++++ src/libnr/nr-path-code.h | 28 + src/libnr/nr-path.cpp | 461 ++++++++++ src/libnr/nr-path.h | 62 ++ src/libnr/nr-pixblock-line.cpp | 92 ++ src/libnr/nr-pixblock-line.h | 28 + src/libnr/nr-pixblock-pattern.cpp | 126 +++ src/libnr/nr-pixblock-pattern.h | 28 + src/libnr/nr-pixblock-pixel.cpp | 230 +++++ src/libnr/nr-pixblock-pixel.h | 28 + src/libnr/nr-pixblock.cpp | 411 +++++++++ src/libnr/nr-pixblock.h | 95 ++ src/libnr/nr-pixops.h | 46 + src/libnr/nr-point-fns-test.cpp | 107 +++ src/libnr/nr-point-fns-test.h | 141 +++ src/libnr/nr-point-fns.cpp | 74 ++ src/libnr/nr-point-fns.h | 104 +++ src/libnr/nr-point-l.h | 95 ++ src/libnr/nr-point-matrix-ops.h | 47 + src/libnr/nr-point-ops.h | 88 ++ src/libnr/nr-point.h | 159 ++++ src/libnr/nr-rect-l.cpp | 22 + src/libnr/nr-rect-l.h | 131 +++ src/libnr/nr-rect-ops.h | 51 ++ src/libnr/nr-rect.cpp | 233 +++++ src/libnr/nr-rect.h | 255 ++++++ src/libnr/nr-render.h | 25 + src/libnr/nr-rotate-fns-test.cpp | 43 + src/libnr/nr-rotate-fns-test.h | 54 ++ src/libnr/nr-rotate-fns.cpp | 66 ++ src/libnr/nr-rotate-fns.h | 29 + src/libnr/nr-rotate-matrix-ops.cpp | 19 + src/libnr/nr-rotate-matrix-ops.h | 21 + src/libnr/nr-rotate-ops.h | 43 + src/libnr/nr-rotate-test.cpp | 89 ++ src/libnr/nr-rotate-test.h | 112 +++ src/libnr/nr-rotate.h | 66 ++ src/libnr/nr-scale-matrix-ops.cpp | 26 + src/libnr/nr-scale-matrix-ops.h | 13 + src/libnr/nr-scale-ops.h | 40 + src/libnr/nr-scale-test.cpp | 71 ++ src/libnr/nr-scale-test.h | 92 ++ src/libnr/nr-scale-translate-ops.cpp | 19 + src/libnr/nr-scale-translate-ops.h | 20 + src/libnr/nr-scale.h | 50 ++ src/libnr/nr-svp-private.h | 30 + src/libnr/nr-svp-render.cpp | 615 +++++++++++++ src/libnr/nr-svp-render.h | 30 + src/libnr/nr-svp.cpp | 180 ++++ src/libnr/nr-svp.h | 65 ++ src/libnr/nr-translate-matrix-ops.cpp | 27 + src/libnr/nr-translate-matrix-ops.h | 22 + src/libnr/nr-translate-ops.h | 43 + src/libnr/nr-translate-rotate-ops.cpp | 20 + src/libnr/nr-translate-rotate-ops.h | 21 + src/libnr/nr-translate-scale-ops.cpp | 25 + src/libnr/nr-translate-scale-ops.h | 20 + src/libnr/nr-translate-test.cpp | 65 ++ src/libnr/nr-translate-test.h | 87 ++ src/libnr/nr-translate.h | 34 + src/libnr/nr-types-test.cpp | 105 +++ src/libnr/nr-types-test.h | 145 +++ src/libnr/nr-types.cpp | 68 ++ src/libnr/nr-types.h | 47 + src/libnr/nr-values.cpp | 23 + src/libnr/nr-values.h | 45 + src/libnr/nr_config.h.mingw | 12 + src/libnr/nr_config.h.win32 | 14 + src/libnr/nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP.S | 125 +++ src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP.S | 231 +++++ ...mx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S | 414 +++++++++ src/libnr/nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P.S | 227 +++++ src/libnr/testnr.cpp | 92 ++ 114 files changed, 12272 insertions(+) create mode 100644 src/libnr/.cvsignore create mode 100644 src/libnr/Makefile_insert create mode 100644 src/libnr/have_mmx.S create mode 100644 src/libnr/in-svg-plane-test.cpp create mode 100644 src/libnr/in-svg-plane-test.h create mode 100644 src/libnr/in-svg-plane.h create mode 100644 src/libnr/libnr.def create mode 100644 src/libnr/makefile.in create mode 100644 src/libnr/n-art-bpath.h create mode 100644 src/libnr/nr-blit.cpp create mode 100644 src/libnr/nr-blit.h create mode 100644 src/libnr/nr-compose-transform.cpp create mode 100644 src/libnr/nr-compose-transform.h create mode 100644 src/libnr/nr-compose.cpp create mode 100644 src/libnr/nr-compose.h create mode 100644 src/libnr/nr-convex-hull-ops.h create mode 100644 src/libnr/nr-convex-hull.h create mode 100644 src/libnr/nr-coord.h create mode 100644 src/libnr/nr-dim2.h create mode 100644 src/libnr/nr-forward.h create mode 100644 src/libnr/nr-gradient.cpp create mode 100644 src/libnr/nr-gradient.h create mode 100644 src/libnr/nr-i-coord.h create mode 100644 src/libnr/nr-macros.h create mode 100644 src/libnr/nr-matrix-div.cpp create mode 100644 src/libnr/nr-matrix-div.h create mode 100644 src/libnr/nr-matrix-fns.cpp create mode 100644 src/libnr/nr-matrix-fns.h create mode 100644 src/libnr/nr-matrix-ops.h create mode 100644 src/libnr/nr-matrix-rotate-ops.cpp create mode 100644 src/libnr/nr-matrix-rotate-ops.h create mode 100644 src/libnr/nr-matrix-scale-ops.cpp create mode 100644 src/libnr/nr-matrix-scale-ops.h create mode 100644 src/libnr/nr-matrix-test.cpp create mode 100644 src/libnr/nr-matrix-test.h create mode 100644 src/libnr/nr-matrix-translate-ops.cpp create mode 100644 src/libnr/nr-matrix-translate-ops.h create mode 100644 src/libnr/nr-matrix.cpp create mode 100644 src/libnr/nr-matrix.h create mode 100644 src/libnr/nr-maybe.h create mode 100644 src/libnr/nr-object.cpp create mode 100644 src/libnr/nr-object.h create mode 100644 src/libnr/nr-path-code.h create mode 100644 src/libnr/nr-path.cpp create mode 100644 src/libnr/nr-path.h create mode 100644 src/libnr/nr-pixblock-line.cpp create mode 100644 src/libnr/nr-pixblock-line.h create mode 100644 src/libnr/nr-pixblock-pattern.cpp create mode 100644 src/libnr/nr-pixblock-pattern.h create mode 100644 src/libnr/nr-pixblock-pixel.cpp create mode 100644 src/libnr/nr-pixblock-pixel.h create mode 100644 src/libnr/nr-pixblock.cpp create mode 100644 src/libnr/nr-pixblock.h create mode 100644 src/libnr/nr-pixops.h create mode 100644 src/libnr/nr-point-fns-test.cpp create mode 100644 src/libnr/nr-point-fns-test.h create mode 100644 src/libnr/nr-point-fns.cpp create mode 100644 src/libnr/nr-point-fns.h create mode 100644 src/libnr/nr-point-l.h create mode 100644 src/libnr/nr-point-matrix-ops.h create mode 100644 src/libnr/nr-point-ops.h create mode 100644 src/libnr/nr-point.h create mode 100644 src/libnr/nr-rect-l.cpp create mode 100644 src/libnr/nr-rect-l.h create mode 100644 src/libnr/nr-rect-ops.h create mode 100644 src/libnr/nr-rect.cpp create mode 100644 src/libnr/nr-rect.h create mode 100644 src/libnr/nr-render.h create mode 100644 src/libnr/nr-rotate-fns-test.cpp create mode 100644 src/libnr/nr-rotate-fns-test.h create mode 100644 src/libnr/nr-rotate-fns.cpp create mode 100644 src/libnr/nr-rotate-fns.h create mode 100644 src/libnr/nr-rotate-matrix-ops.cpp create mode 100644 src/libnr/nr-rotate-matrix-ops.h create mode 100644 src/libnr/nr-rotate-ops.h create mode 100644 src/libnr/nr-rotate-test.cpp create mode 100644 src/libnr/nr-rotate-test.h create mode 100644 src/libnr/nr-rotate.h create mode 100644 src/libnr/nr-scale-matrix-ops.cpp create mode 100644 src/libnr/nr-scale-matrix-ops.h create mode 100644 src/libnr/nr-scale-ops.h create mode 100644 src/libnr/nr-scale-test.cpp create mode 100644 src/libnr/nr-scale-test.h create mode 100644 src/libnr/nr-scale-translate-ops.cpp create mode 100644 src/libnr/nr-scale-translate-ops.h create mode 100644 src/libnr/nr-scale.h create mode 100644 src/libnr/nr-svp-private.h create mode 100644 src/libnr/nr-svp-render.cpp create mode 100644 src/libnr/nr-svp-render.h create mode 100644 src/libnr/nr-svp.cpp create mode 100644 src/libnr/nr-svp.h create mode 100644 src/libnr/nr-translate-matrix-ops.cpp create mode 100644 src/libnr/nr-translate-matrix-ops.h create mode 100644 src/libnr/nr-translate-ops.h create mode 100644 src/libnr/nr-translate-rotate-ops.cpp create mode 100644 src/libnr/nr-translate-rotate-ops.h create mode 100644 src/libnr/nr-translate-scale-ops.cpp create mode 100644 src/libnr/nr-translate-scale-ops.h create mode 100644 src/libnr/nr-translate-test.cpp create mode 100644 src/libnr/nr-translate-test.h create mode 100644 src/libnr/nr-translate.h create mode 100644 src/libnr/nr-types-test.cpp create mode 100644 src/libnr/nr-types-test.h create mode 100644 src/libnr/nr-types.cpp create mode 100644 src/libnr/nr-types.h create mode 100644 src/libnr/nr-values.cpp create mode 100644 src/libnr/nr-values.h create mode 100644 src/libnr/nr_config.h.mingw create mode 100644 src/libnr/nr_config.h.win32 create mode 100644 src/libnr/nr_mmx_R8G8B8A8_P_EMPTY_A8_RGBAP.S create mode 100644 src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_A8_RGBAP.S create mode 100644 src/libnr/nr_mmx_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_TRANSFORM.S create mode 100644 src/libnr/nr_mmx_R8G8B8_R8G8B8_R8G8B8A8_P.S create mode 100644 src/libnr/testnr.cpp (limited to 'src/libnr') 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 +#include + +#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 + +#include +#include + +#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 + * + * 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 + * + * This code is in public domain + */ + +#include + +#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 + * + * 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 + * + * This code is in public domain + */ + +#include + +/* 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 + * + * This code is in public domain + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#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 + * + * 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 + * + * This code is licensed under the GNU GPL; see COPYING for more information. + */ + +#include +#include + +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 + * + * This code is licensed under the GNU GPL; see COPYING for more information. + */ + +#include + +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 + * + * 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 + * + * This code is in public domain + */ + +#include +#include +#include +#include + +#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 + * + * This code is in public domain + */ + +#include +#include + +#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 + +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 + * + * This code is in public domain + */ + +#include + +#if HAVE_STDLIB_H +#include +#endif +#include + +#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 + +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 + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +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 + * + * 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 : + * Original NRMatrix definition and related macros. + * + * Nathan Hurst : + * NR::Matrix class version of the above. + * + * This code is in public domain. + */ + +#include + +#include "libnr/nr-coord.h" +#include "libnr/nr-values.h" +#include +#include +#include + +/// 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(_c); + } + + + + /** + * + */ + operator NRMatrix const&() const { + g_assert(sizeof(_c) == sizeof(NRMatrix)); + return *reinterpret_cast(_c); + } + + + + /** + * + */ + operator NRMatrix*() { + g_assert(sizeof(_c) == sizeof(NRMatrix)); + return reinterpret_cast(_c); + } + + + /** + * + */ + operator NRMatrix const*() const { + g_assert(sizeof(_c) == sizeof(NRMatrix)); + return reinterpret_cast(_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 + * + * This code is licensed under the GNU GPL; see COPYING for more information. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +namespace NR { + +/** An exception class for run-time type errors */ +template +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 + bool operator==(T t) { return t == *this; } + template + bool operator!=(T t) { return t != *this; } +}; + +template +bool operator==(T t, Nothing n) { return false; } +template +bool operator!=(T t, Nothing n) { return !( t == n ); } + +template +struct MaybeTraits; + +template +class Maybe { +public: + typedef MaybeTraits traits; + typedef typename traits::storage storage; + typedef typename traits::reference reference; + + Maybe(Nothing n) : _is_nothing(true), _t() {} + + Maybe(const Maybe &m) : _is_nothing(m._is_nothing), _t(m._t) {} + + template + Maybe(const Maybe &m) + : _is_nothing(m._is_nothing), + _t(traits::to_storage(MaybeTraits::from_storage(m._t))) {} + + template + Maybe(T2 t) : _is_nothing(false), _t(traits::to_storage(t)) {} + + reference assume() const throw(IsNot) { + if (_is_nothing) { + throw IsNot(); + } else { + return traits::from_storage(_t); + } + } + + operator reference() const throw(IsNot) { + if (_is_nothing) { + throw IsNot(); + } else { + return traits::from_storage(_t); + } + } + operator Nothing() const throw(IsNot) { + if (!_is_nothing) { + throw IsNot(); + } 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 +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 +struct MaybeTraits { + 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 + * MenTaLguY + * + * This code is in public domain + */ + +#include +#include + +#include + +#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; +} + +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(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( + ::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; +} + +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 + * MenTaLguY + * + * This code is in public domain + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#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 + 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 + * + * 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 +#include + +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 + * + * This code is in public domain + */ + +#include +#include + +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 + * + * This code is in public domain + */ + +#include + +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 + * + * 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 + * + * This code is in public domain + */ + +#include + +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 + * + * 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 + * + * This code is in public domain + */ + +#include + +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 + * + * 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 + * (C) 2005 Ralf Stephan (some cleanup) + * + * This code is in the Public Domain. + */ + +#include +#include + +/// 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 + * bulia byak + * + * 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 +#include +#include +#include + +#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 + +#include +#include +#include +#include + +#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 +#include + +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 +#include +#include + +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 +#include +#include + +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 + +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 +//#include +#include +//#include + +#include +#include + +//#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 + +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 + +struct NRRectL { + NR::ICoord x0, y0, x1, y1; +}; + +#include +#include + +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() && isEmpty(); + } + + bool intersects(const IRect &r) const { + return intersects(r) && intersects(r); + } + bool contains(const IRect &r) const { + return contains(r) && contains(r); + } + bool contains(const IPoint &p) const { + return contains(p) && contains(p); + } + + ICoord maxExtent() const { + return MAX(extent(), extent()); + } + + ICoord extent(Dim2 axis) const { + switch (axis) { + case X: return extent(); + case Y: return extent(); + }; + } + + ICoord extent(unsigned i) const throw(std::out_of_range) { + switch (i) { + case 0: return extent(); + case 1: return extent(); + 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 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 + ICoord extent() const { + return _max[axis] - _min[axis]; + } + + template + bool isEmpty() const { + return !( _min[axis] < _max[axis] ); + } + + template + bool intersects(const IRect &r) const { + return _max[axis] >= r._min[axis] && _min[axis] <= r._max[axis]; + } + + template + bool contains(const IRect &r) const { + return contains(r._min) && contains(r._max); + } + + template + 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 , + * bulia byak + * + * This code is licensed under the GNU GPL; see COPYING for more information. + */ + +#include + +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 + * + * 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::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 + * Nathan Hurst + * MenTaLguY + * + * This code is in public domain + */ + + +#include + +#include "libnr/nr-values.h" +#include +#include +#include +#include +#include +#include + +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() || isEmpty(); + } + + bool intersects(Rect const &r) const { + return intersects(r) && intersects(r); + } + bool contains(Rect const &r) const { + return contains(r) && contains(r); + } + bool contains(Point const &p) const { + return contains(p) && contains(p); + } + + double area() const { + return extent() * extent(); + } + + double maxExtent() const { + return MAX(extent(), extent()); + } + + double extent(Dim2 const axis) const { + switch (axis) { + case X: return extent(); + case Y: return extent(); + 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(); + case 1: return extent(); + 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 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 + double extent() const { + return _max[axis] - _min[axis]; + } + + template + bool isEmpty() const { + return !( _min[axis] < _max[axis] ); + } + + template + bool intersects(Rect const &r) const { + return _max[axis] >= r._min[axis] && _min[axis] <= r._max[axis]; + } + + template + bool contains(Rect const &r) const { + return contains(r._min) && contains(r._max); + } + + template + bool contains(Point const &p) const { + return p[axis] >= _min[axis] && p[axis] <= _max[axis]; + } + + Point _min, _max; + + /* evil, but temporary */ + friend class Maybe; +}; + +/** 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 + * + * This code is in public domain + */ + +#include + +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 +#include + +#include +#include + +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 + +#include +#include + +#include + +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 +#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 +#include + +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 + +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 + + +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 + +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 +#include +#include +#include /* identity, matrix_equalp */ +#include +#include +#include +#include +#include +#include +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 + +#include +#include +#include +#include /* identity, matrix_equalp */ +#include +#include +#include +#include +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 +#include +#include + +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 + +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 +#include +#include +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 + +#include +#include +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 +#include + +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 + * + * This code is in public domain + */ + +#include + +#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 + * + * 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 + * + * This code is in public domain + */ + +#include +#include + +/* 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 + * + * This code is in public domain + */ + +#define noNR_VERBOSE + +#define NR_SVP_LENGTH_MAX 128 + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_IEEEFP_H +# include +#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 + * + * This code is in public domain + */ + +/* Sorted vector paths */ + +#include +#include + +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 +#include + +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 +#include + +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 + + +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 +#include +#include +#include +#include +#include +#include +#include +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 + +#include +#include +#include +#include +#include +#include +#include +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 + +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 +#include +#include +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 + +#include "libnr/nr-types.h" +#include "libnr/nr-point-fns.h" +#include +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 + +#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 + * Class-ifying NRPoint, Nathan Hurst + * + * This code is in public domain + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + + +/* +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 + * + * This code is in public domain + */ + +#include + +#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 + * + * This code is in public domain + */ + +#if defined (_WIN32) || defined (__WIN32__) +# include +#include +#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; +} -- cgit v1.2.3