Code

Tests for nr-compose, as well as updated tests for svg-affine and svg-length.
authorjaspervdg <jaspervdg@users.sourceforge.net>
Sat, 5 Jul 2008 14:56:14 +0000 (14:56 +0000)
committerjaspervdg <jaspervdg@users.sourceforge.net>
Sat, 5 Jul 2008 14:56:14 +0000 (14:56 +0000)
build.xml
src/libnr/nr-compose-reference.cpp [new file with mode: 0644]
src/libnr/nr-compose-reference.h [new file with mode: 0644]
src/libnr/nr-compose-test.h [new file with mode: 0644]
src/svg/svg-affine-test.h
src/svg/svg-length-test.h

index 2ab7104e24a6e9a46397b262e13e303f0ff12203..ac34d71f475c8a7a918f0d96375da28c5541b491 100644 (file)
--- a/build.xml
+++ b/build.xml
     <cxxtestpart command="python ${cxxtest}/cxxtestgen.py" 
                  out="${src}/libnr/test-nr.cpp">
         <fileset dir="${src}/libnr">
+            <include name="nr-compose-test.h"/>
             <include name="nr-types-test.h"/>
             <include name="nr-translate-test.h"/>
             <include name="nr-rotate-test.h"/>
             <exclude name="libnr/test-nr-main.cpp"/>
             <exclude name="libnr/testnr.cpp"/>
             <exclude name="libnr/in-svg-plane-test.cpp"/>
-            <exclude name="libnr/nr-compose-reference.cpp"/>
-            <exclude name="libnr/nr-compose-test.cpp"/>
             <exclude name="libnr/nr-matrix-test.cpp"/>
             <exclude name="libnr/nr-point-fns-test.cpp"/>
             <exclude name="libnr/nr-rotate-fns-test.cpp"/>
            <exclude name="obj/test-src.o"/>
            <exclude name="obj/display/test-display.o"/>
            <exclude name="obj/helper/test-helper.o"/>
+           <exclude name="obj/libnr/nr-compose-reference.o"/>
            <exclude name="obj/libnr/test-nr.o"/>
            <exclude name="obj/svg/test-svg.o"/>
            <exclude name="obj/util/test-util.o"/>
            <include name="obj/test-src.o"/>
            <include name="obj/display/test-display.o"/>
            <include name="obj/helper/test-helper.o"/>
+           <include name="obj/libnr/nr-compose-reference.o"/>
            <include name="obj/libnr/test-nr.o"/>
            <include name="obj/svg/test-svg.o"/>
            <include name="obj/util/test-util.o"/>
diff --git a/src/libnr/nr-compose-reference.cpp b/src/libnr/nr-compose-reference.cpp
new file mode 100644 (file)
index 0000000..b952939
--- /dev/null
@@ -0,0 +1,266 @@
+
+// This is a reference implementation of the compositing functions in nr-compose.cpp.
+
+#include "nr-compose-reference.h"
+
+#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)
+
+static inline unsigned int DIV_ROUND(unsigned int v, unsigned int divisor) { return (v+divisor/2)/divisor; }
+
+static unsigned int pixelSize[] = { 1, 3, 4, 4 };
+
+// Computes :
+//   dc' = (1 - alpha*sa) * dc + alpha*sc
+//   da' = 1 - (1 - alpha*sa) * (1 - da)
+// Assuming premultiplied color values
+template<PIXEL_FORMAT resultFormat, PIXEL_FORMAT backgroundFormat, PIXEL_FORMAT foregroundFormat>
+static void composePixel(unsigned char *d, const unsigned char *s, unsigned int alpha);
+
+template<> static void composePixel<R8G8B8, R8G8B8, R8G8B8A8N>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       d[0] = DIV_ROUND((255*255 - alpha*s[3]) * d[0] + alpha*s[3]*s[0], 255*255);
+       d[1] = DIV_ROUND((255*255 - alpha*s[3]) * d[1] + alpha*s[3]*s[1], 255*255);
+       d[2] = DIV_ROUND((255*255 - alpha*s[3]) * d[2] + alpha*s[3]*s[2], 255*255);
+}
+
+template<> static void composePixel<R8G8B8, R8G8B8, R8G8B8A8P>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       d[0] = DIV_ROUND((255*255 - alpha*s[3]) * d[0] + 255*alpha*s[0], 255*255);
+       d[1] = DIV_ROUND((255*255 - alpha*s[3]) * d[1] + 255*alpha*s[1], 255*255);
+       d[2] = DIV_ROUND((255*255 - alpha*s[3]) * d[2] + 255*alpha*s[2], 255*255);
+}
+
+template<> static void composePixel<R8G8B8A8N, EMPTY, R8G8B8A8N>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       unsigned int newa = 255*255 - (255*255 - alpha*s[3]);
+       d[0] = s[0];//newa == 0 ? 0 : DIV_ROUND(alpha*s[3]*s[0], newa);
+       d[1] = s[1];//newa == 0 ? 0 : DIV_ROUND(alpha*s[3]*s[1], newa);
+       d[2] = s[2];//newa == 0 ? 0 : DIV_ROUND(alpha*s[3]*s[2], newa);
+       d[3] = DIV_ROUND(newa, 255);
+}
+
+template<> static void composePixel<R8G8B8A8N, EMPTY, R8G8B8A8P>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       unsigned int newa = 255*255 - (255*255 - alpha*s[3]);
+       d[0] = s[3] == 0 ? 0 : DIV_ROUND(255*s[0], s[3]);//newa == 0 ? 0 : DIV_ROUND(255*alpha*s[0], newa);
+       d[1] = s[3] == 0 ? 0 : DIV_ROUND(255*s[1], s[3]);//newa == 0 ? 0 : DIV_ROUND(255*alpha*s[1], newa);
+       d[2] = s[3] == 0 ? 0 : DIV_ROUND(255*s[2], s[3]);//newa == 0 ? 0 : DIV_ROUND(255*alpha*s[2], newa);
+       d[3] = DIV_ROUND(newa, 255);
+}
+
+template<> static void composePixel<R8G8B8A8N, R8G8B8A8N, R8G8B8A8N>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       if ( d[3] == 0 ) {
+               composePixel<R8G8B8A8N, EMPTY, R8G8B8A8N>(d, s, alpha);
+       } else if ( alpha*s[3] == 0 ) {
+               /* NOP */
+       } else {
+               unsigned int newa = 255*255*255 - (255*255 - alpha*s[3]) * (255 - d[3]);
+               d[0] = DIV_ROUND((255*255 - alpha*s[3]) * d[3]*d[0] + 255 * alpha*s[3]*s[0], newa);
+               d[1] = DIV_ROUND((255*255 - alpha*s[3]) * d[3]*d[1] + 255 * alpha*s[3]*s[1], newa);
+               d[2] = DIV_ROUND((255*255 - alpha*s[3]) * d[3]*d[2] + 255 * alpha*s[3]*s[2], newa);
+               d[3] = DIV_ROUND(newa, 255*255);
+       }
+}
+
+template<> static void composePixel<R8G8B8A8N, R8G8B8A8N, R8G8B8A8P>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       if ( d[3] == 0 ) {
+               composePixel<R8G8B8A8N, EMPTY, R8G8B8A8P>(d, s, alpha);
+       } else if ( alpha*s[3] == 0 ) {
+               /* NOP */
+       } else {
+               unsigned int newa = 255*255*255 - (255*255 - alpha*s[3]) * (255 - d[3]);
+               d[0] = DIV_ROUND((255*255 - alpha*s[3]) * d[3]*d[0] + 255*255 * alpha*s[0], newa);
+               d[1] = DIV_ROUND((255*255 - alpha*s[3]) * d[3]*d[1] + 255*255 * alpha*s[1], newa);
+               d[2] = DIV_ROUND((255*255 - alpha*s[3]) * d[3]*d[2] + 255*255 * alpha*s[2], newa);
+               d[3] = DIV_ROUND(newa, 255*255);
+       }
+}
+
+template<> static void composePixel<R8G8B8A8P, EMPTY, R8G8B8A8N>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       d[0] = DIV_ROUND(alpha*s[3]*s[0], 255*255);
+       d[1] = DIV_ROUND(alpha*s[3]*s[1], 255*255);
+       d[2] = DIV_ROUND(alpha*s[3]*s[2], 255*255);
+       d[3] = DIV_ROUND(255*255 - (255*255 - alpha*s[3]), 255);
+}
+
+template<> static void composePixel<R8G8B8A8P, EMPTY, R8G8B8A8P>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       d[0] = DIV_ROUND(alpha*s[0], 255);
+       d[1] = DIV_ROUND(alpha*s[1], 255);
+       d[2] = DIV_ROUND(alpha*s[2], 255);
+       d[3] = DIV_ROUND(255*255 - (255*255 - alpha*s[3]), 255);
+}
+
+template<> static void composePixel<R8G8B8A8P, R8G8B8A8P, R8G8B8A8N>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       d[0] = DIV_ROUND((255*255 - alpha*s[3]) * d[0] + alpha*s[3]*s[0], 255*255);
+       d[1] = DIV_ROUND((255*255 - alpha*s[3]) * d[1] + alpha*s[3]*s[1], 255*255);
+       d[2] = DIV_ROUND((255*255 - alpha*s[3]) * d[2] + alpha*s[3]*s[2], 255*255);
+       d[3] = DIV_ROUND(255*255*255 - (255*255 - alpha*s[3]) * (255 - d[3]), 255*255);
+}
+
+template<> static void composePixel<R8G8B8A8P, R8G8B8A8P, R8G8B8A8P>(unsigned char *d, const unsigned char *s, unsigned int alpha) {
+       d[0] = DIV_ROUND((255*255 - alpha*s[3]) * d[0] + 255 * alpha*s[0], 255*255);
+       d[1] = DIV_ROUND((255*255 - alpha*s[3]) * d[1] + 255 * alpha*s[1], 255*255);
+       d[2] = DIV_ROUND((255*255 - alpha*s[3]) * d[2] + 255 * alpha*s[2], 255*255);
+       d[3] = DIV_ROUND(255*255*255 - (255*255 - alpha*s[3]) * (255 - d[3]), 255*255);
+}
+
+
+// composeAlpha, iterates over all pixels and applies composePixel to each of them
+template<PIXEL_FORMAT resultFormat, PIXEL_FORMAT backgroundFormat, PIXEL_FORMAT foregroundFormat>
+static void composeAlpha(unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       for(int y=0; y<h; y++) {
+               unsigned char* d = px;
+               const unsigned char* s = spx;
+               for(int x=0; x<w; x++) {
+                       composePixel<resultFormat, backgroundFormat, foregroundFormat>(d, s, alpha);
+                       d += pixelSize[resultFormat];
+                       s += pixelSize[foregroundFormat];
+               }
+               px += rs;
+               spx += srs;
+       }
+}
+
+template<PIXEL_FORMAT resultFormat, PIXEL_FORMAT backgroundFormat, PIXEL_FORMAT foregroundFormat>
+static void composeMask(unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs) {
+       for(int y=0; y<h; y++) {
+               unsigned char* d = px;
+               const unsigned char* s = spx;
+               const unsigned char* m = mpx;
+               for(int x=0; x<w; x++) {
+                       composePixel<resultFormat, backgroundFormat, foregroundFormat>(d, s, *m);
+                       d += pixelSize[resultFormat];
+                       s += pixelSize[foregroundFormat];
+                       m += 1;
+               }
+               px += rs;
+               spx += srs;
+               mpx += mrs;
+       }
+}
+
+template<PIXEL_FORMAT resultFormat, PIXEL_FORMAT backgroundFormat>
+static void composeColor(unsigned char *px, int w, int h, int rs, const unsigned char *mpx, int mrs, unsigned long rgba) {
+       const unsigned char rgba_array[4] = {NR_RGBA32_R(rgba), NR_RGBA32_G(rgba), NR_RGBA32_B(rgba), NR_RGBA32_A(rgba)};
+       for(int y=0; y<h; y++) {
+               unsigned char* d = px;
+               const unsigned char* m = mpx;
+               for(int x=0; x<w; x++) {
+                       composePixel<resultFormat, backgroundFormat, R8G8B8A8N>(d, rgba_array, *m);
+                       d += pixelSize[resultFormat];
+                       m += 1;
+               }
+               px += rs;
+               mpx += mrs;
+       }
+}
+
+/* FINAL DST SRC */
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8A8N, EMPTY, R8G8B8A8N>(px, w, h, rs, spx, srs, alpha);
+}
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8A8N, EMPTY, R8G8B8A8P>(px, w, h, rs, spx, srs, alpha);
+}
+
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8A8P, EMPTY, R8G8B8A8N>(px, w, h, rs, spx, srs, alpha);
+}
+
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8A8P, EMPTY, R8G8B8A8P>(px, w, h, rs, spx, srs, alpha);
+}
+
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8A8N, R8G8B8A8N, R8G8B8A8N>(px, w, h, rs, spx, srs, alpha);
+}
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8A8N, R8G8B8A8N, R8G8B8A8P>(px, w, h, rs, spx, srs, alpha);
+}
+
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8A8P, R8G8B8A8P, R8G8B8A8N>(px, w, h, rs, spx, srs, alpha);
+}
+
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8A8P, R8G8B8A8P, R8G8B8A8P>(px, w, h, rs, spx, srs, alpha);
+}
+
+/* FINAL DST SRC MASK */
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_A8_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs) {
+       composeMask<R8G8B8A8N, EMPTY, R8G8B8A8N>(px, w, h, rs, spx, srs, mpx, mrs);
+}
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_A8_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs) {
+       composeMask<R8G8B8A8N, EMPTY, R8G8B8A8P>(px, w, h, rs, spx, srs, mpx, mrs);
+}
+
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_A8_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs) {
+       composeMask<R8G8B8A8P, EMPTY, R8G8B8A8N>(px, w, h, rs, spx, srs, mpx, mrs);
+}
+
+void nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_A8_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs) {
+       composeMask<R8G8B8A8P, EMPTY, R8G8B8A8P>(px, w, h, rs, spx, srs, mpx, mrs);
+}
+
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_A8_ref (unsigned char *p, int w, int h, int rs, const unsigned char *s, int srs, const unsigned char *m, int mrs) {
+       composeMask<R8G8B8A8N, R8G8B8A8N, R8G8B8A8N>(p, w, h, rs, s, srs, m, mrs);
+}
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_A8_ref (unsigned char *p, int w, int h, int rs, const unsigned char *s, int srs, const unsigned char *m, int mrs) {
+       composeMask<R8G8B8A8N, R8G8B8A8N, R8G8B8A8P>(p, w, h, rs, s, srs, m, mrs);
+}
+
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_A8_ref (unsigned char *p, int w, int h, int rs, const unsigned char *s, int srs, const unsigned char *m, int mrs) {
+       composeMask<R8G8B8A8P, R8G8B8A8P, R8G8B8A8N>(p, w, h, rs, s, srs, m, mrs);
+}
+
+void nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_A8_ref (unsigned char *p, int w, int h, int rs, const unsigned char *s, int srs, const unsigned char *m, int mrs) {
+       composeMask<R8G8B8A8P, R8G8B8A8P, R8G8B8A8P>(p, w, h, rs, s, srs, m, mrs);
+}
+
+/* FINAL DST MASK COLOR */
+
+void nr_R8G8B8A8_N_EMPTY_A8_RGBA32_ref (unsigned char *px, int w, int h, int rs, const unsigned char *mpx, int mrs, unsigned long rgba) {
+       composeColor<R8G8B8A8N, EMPTY>(px, w, h, rs, mpx, mrs, rgba);
+}
+
+void nr_R8G8B8A8_P_EMPTY_A8_RGBA32_ref (unsigned char *px, int w, int h, int rs, const unsigned char *mpx, int mrs, unsigned long rgba) {
+       composeColor<R8G8B8A8P, EMPTY>(px, w, h, rs, mpx, mrs, rgba);
+}
+
+
+void nr_R8G8B8_R8G8B8_A8_RGBA32_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba) {
+       composeColor<R8G8B8, R8G8B8>(px, w, h, rs, spx, srs, rgba);
+}
+
+void nr_R8G8B8A8_N_R8G8B8A8_N_A8_RGBA32_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba) {
+       composeColor<R8G8B8A8N, R8G8B8A8N>(px, w, h, rs, spx, srs, rgba);
+}
+
+void nr_R8G8B8A8_P_R8G8B8A8_P_A8_RGBA32_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned long rgba) {
+       composeColor<R8G8B8A8P, R8G8B8A8P>(px, w, h, rs, spx, srs, rgba);
+}
+
+/* RGB */
+
+void nr_R8G8B8_R8G8B8_R8G8B8A8_P_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8, R8G8B8, R8G8B8A8P>(px, w, h, rs, spx, srs, alpha);
+}
+
+void nr_R8G8B8_R8G8B8_R8G8B8A8_N_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha) {
+       composeAlpha<R8G8B8, R8G8B8, R8G8B8A8N>(px, w, h, rs, spx, srs, alpha);
+}
+
+void nr_R8G8B8_R8G8B8_R8G8B8A8_P_A8_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs) {
+       composeMask<R8G8B8, R8G8B8, R8G8B8A8P>(px, w, h, rs, spx, srs, mpx, mrs);
+}
+
+void nr_R8G8B8_R8G8B8_R8G8B8A8_N_A8_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs) {
+       composeMask<R8G8B8, R8G8B8, R8G8B8A8N>(px, w, h, rs, spx, srs, mpx, mrs);
+}
diff --git a/src/libnr/nr-compose-reference.h b/src/libnr/nr-compose-reference.h
new file mode 100644 (file)
index 0000000..8d004a1
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __NR_COMPOSE_REFERENCE_H__
+#define __NR_COMPOSE_REFERENCE_H__
+
+// Based on nr-pixblock.h
+typedef enum {
+       A8 = 0,
+       R8G8B8,
+       R8G8B8A8N,
+       R8G8B8A8P,
+       EMPTY = -1
+} PIXEL_FORMAT;
+
+/* FINAL DST SRC */
+
+void nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (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_ref (unsigned char *px, int w, int h, int rs, const unsigned char *mpx, int mrs, unsigned long rgba);
+
+void nr_R8G8B8_R8G8B8_A8_RGBA32_ref (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_ref (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_ref (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_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, unsigned int alpha);
+void nr_R8G8B8_R8G8B8_R8G8B8A8_N_ref (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_ref (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_ref (unsigned char *px, int w, int h, int rs, const unsigned char *spx, int srs, const unsigned char *mpx, int mrs);
+
+#endif//__NR_COMPOSE_REFERENCE_H__
diff --git a/src/libnr/nr-compose-test.h b/src/libnr/nr-compose-test.h
new file mode 100644 (file)
index 0000000..5b0d129
--- /dev/null
@@ -0,0 +1,451 @@
+
+#include <cxxtest/TestSuite.h>
+
+#include "nr-compose.h"
+#include "nr-compose-reference.h"
+#include <glib.h>
+#include <memory.h>
+#include <stdlib.h>
+
+static inline unsigned int DIV_ROUND(unsigned int v, unsigned int divisor) { return (v+divisor/2)/divisor; }
+static inline unsigned char NR_PREMUL_111(unsigned int c, unsigned int a) { return static_cast<unsigned char>(DIV_ROUND(c*a, 255)); }
+
+template<PIXEL_FORMAT format>
+static int IMGCMP(const unsigned char* a, const unsigned char* b, size_t n) { return memcmp(a, b, n); }
+
+template<>
+static int IMGCMP<R8G8B8A8N>(const unsigned char* a, const unsigned char* b, size_t n)
+{
+    // If two pixels each have their alpha channel set to zero they're equivalent
+    //   Note that this doesn't work for premultiplied values, as their color values should
+    //   be zero when alpha is zero.
+    int cr = 0;
+    while(n && cr == 0) {
+        if ( a[3] != 0 || b[3] != 0 ) {
+            cr = memcmp(a, b, 4);
+        }
+        a+=4;
+        b+=4;
+        n-=4;
+    }
+    return cr;
+}
+
+class NrComposeTest : public CxxTest::TestSuite {
+private:
+    int const w, h;
+
+    unsigned char* const dst_rgba_n_org;
+    unsigned char* const dst_rgba_p_org;
+    unsigned char* const dst_rgb_org;
+
+    unsigned char* const dst1_rgba;
+    unsigned char* const dst2_rgba;
+    unsigned char* const src_rgba_n;
+    unsigned char* const src_rgba_p;
+    unsigned char* const dst1_rgb;
+    unsigned char* const dst2_rgb;
+    unsigned char* const src_rgb;
+    unsigned char* const mask;
+
+    static unsigned int const alpha_vals[7];
+    static unsigned int const rgb_vals[3];
+
+public:
+    NrComposeTest() :
+        w(13),
+        h(5),
+
+        dst_rgba_n_org(new unsigned char[w*h*4]),
+        dst_rgba_p_org(new unsigned char[w*h*4]),
+        dst_rgb_org(new unsigned char[w*h*3]),
+
+        dst1_rgba(new unsigned char[w*h*4]),
+        dst2_rgba(new unsigned char[w*h*4]),
+        src_rgba_n(new unsigned char[w*h*4]),
+        src_rgba_p(new unsigned char[w*h*4]),
+        dst1_rgb(new unsigned char[w*h*3]),
+        dst2_rgb(new unsigned char[w*h*3]),
+        src_rgb(new unsigned char[w*h*3]),
+        mask(new unsigned char[w*h])
+    {
+        srand(23874683); // It shouldn't really matter what this is, as long as it's always the same (to be reproducible)
+
+        for(int y=0; y<h; y++) {
+            for(int x=0; x<w; x++) {
+                dst_rgba_n_org[(x+y*w)*4+3] = 255*rand()/RAND_MAX;
+                dst_rgba_p_org[(x+y*w)*4+3] = 255*rand()/RAND_MAX;
+                src_rgba_n[(x+y*w)*4+3] = 255*rand()/RAND_MAX;
+                src_rgba_p[(x+y*w)*4+3] = 255*rand()/RAND_MAX;
+                for(int i=0; i<3; i++) {
+                    dst_rgba_n_org[(x+y*w)*4+i] = 255*rand()/RAND_MAX;
+                    dst_rgba_p_org[(x+y*w)*4+i] = NR_PREMUL_111(255*rand()/RAND_MAX, dst_rgba_p_org[(x+y*w)*4+3]);
+                    src_rgba_n[(x+y*w)*4+i] = 255*rand()/RAND_MAX;
+                    src_rgba_p[(x+y*w)*4+i] = NR_PREMUL_111(255*rand()/RAND_MAX, src_rgba_p[(x+y*w)*4+3]);
+                    dst_rgb_org[(x+y*w)*3+i] = 255*rand()/RAND_MAX;
+                }
+                mask[x+y*w] = 255*rand()/RAND_MAX;
+            }
+        }
+    }
+    virtual ~NrComposeTest()
+    {
+        delete[] dst_rgba_n_org;
+        delete[] dst_rgba_p_org;
+        delete[] dst_rgb_org;
+
+        delete[] dst1_rgba;
+        delete[] dst2_rgba;
+        delete[] src_rgba_n;
+        delete[] src_rgba_p;
+        delete[] dst1_rgb;
+        delete[] dst2_rgb;
+        delete[] src_rgb;
+        delete[] mask;
+    }
+
+    // FINAL DST SRC
+
+    void testnr_R8G8B8A8_N_EMPTY_R8G8B8A8_N()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+            memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+            nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N(dst1_rgba, w, h, w*4, src_rgba_n, w*4, alpha);
+            nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_ref(dst2_rgba, w, h, w*4, src_rgba_n, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8A8_N_EMPTY_R8G8B8A8_P()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+            memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+            nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P(dst1_rgba, w, h, w*4, src_rgba_p, w*4, alpha);
+            nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_ref(dst2_rgba, w, h, w*4, src_rgba_p, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8A8_P_EMPTY_R8G8B8A8_N()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+            memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+            nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N(dst1_rgba, w, h, w*4, src_rgba_n, w*4, alpha);
+            nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_ref(dst2_rgba, w, h, w*4, src_rgba_n, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8A8_P_EMPTY_R8G8B8A8_P()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+            memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+            nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P(dst1_rgba, w, h, w*4, src_rgba_p, w*4, alpha);
+            nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_ref(dst2_rgba, w, h, w*4, src_rgba_p, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+            memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+            nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N(dst1_rgba, w, h, w*4, src_rgba_n, w*4, alpha);
+            nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_ref(dst2_rgba, w, h, w*4, src_rgba_n, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+            memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+            nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P(dst1_rgba, w, h, w*4, src_rgba_p, w*4, alpha);
+            nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_ref(dst2_rgba, w, h, w*4, src_rgba_p, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+            memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+            nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N(dst1_rgba, w, h, w*4, src_rgba_n, w*4, alpha);
+            nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_ref(dst2_rgba, w, h, w*4, src_rgba_n, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+            memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+            nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P(dst1_rgba, w, h, w*4, src_rgba_p, w*4, alpha);
+            nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_ref(dst2_rgba, w, h, w*4, src_rgba_p, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+        }
+    }
+
+    // FINAL DST SRC MASK
+
+    void testnr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_A8()
+    {
+        memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+        memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+        nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_A8(dst1_rgba, w, h, w*4, src_rgba_n, w*4, mask, w);
+        nr_R8G8B8A8_N_EMPTY_R8G8B8A8_N_A8_ref(dst2_rgba, w, h, w*4, src_rgba_n, w*4, mask, w);
+        TS_ASSERT( IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+    }
+
+    void testnr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_A8()
+    {
+        memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+        memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+        nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_A8(dst1_rgba, w, h, w*4, src_rgba_p, w*4, mask, w);
+        nr_R8G8B8A8_N_EMPTY_R8G8B8A8_P_A8_ref(dst2_rgba, w, h, w*4, src_rgba_p, w*4, mask, w);
+        TS_ASSERT( IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+    }
+
+    void testnr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_A8()
+    {
+        memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+        memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+        nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_A8(dst1_rgba, w, h, w*4, src_rgba_n, w*4, mask, w);
+        nr_R8G8B8A8_P_EMPTY_R8G8B8A8_N_A8_ref(dst2_rgba, w, h, w*4, src_rgba_n, w*4, mask, w);
+        TS_ASSERT( IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+    }
+
+    void testnr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_A8()
+    {
+        memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+        memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+        nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_A8(dst1_rgba, w, h, w*4, src_rgba_p, w*4, mask, w);
+        nr_R8G8B8A8_P_EMPTY_R8G8B8A8_P_A8_ref(dst2_rgba, w, h, w*4, src_rgba_p, w*4, mask, w);
+        TS_ASSERT( IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+    }
+
+    void testnr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_A8()
+    {
+        memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+        memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+        nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_A8(dst1_rgba, w, h, w*4, src_rgba_n, w*4, mask, w);
+        nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_N_A8_ref(dst2_rgba, w, h, w*4, src_rgba_n, w*4, mask, w);
+        TS_ASSERT( IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+    }
+
+    void testnr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_A8()
+    {
+        memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+        memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+        nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_A8(dst1_rgba, w, h, w*4, src_rgba_p, w*4, mask, w);
+        nr_R8G8B8A8_N_R8G8B8A8_N_R8G8B8A8_P_A8_ref(dst2_rgba, w, h, w*4, src_rgba_p, w*4, mask, w);
+        TS_ASSERT( IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+    }
+
+    void testnr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_A8()
+    {
+        memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+        memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+        nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_A8(dst1_rgba, w, h, w*4, src_rgba_n, w*4, mask, w);
+        nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_N_A8_ref(dst2_rgba, w, h, w*4, src_rgba_n, w*4, mask, w);
+        TS_ASSERT( IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+    }
+
+    void testnr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_A8()
+    {
+        memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+        memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+        nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_A8(dst1_rgba, w, h, w*4, src_rgba_p, w*4, mask, w);
+        nr_R8G8B8A8_P_R8G8B8A8_P_R8G8B8A8_P_A8_ref(dst2_rgba, w, h, w*4, src_rgba_p, w*4, mask, w);
+        TS_ASSERT( IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+    }
+
+    // FINAL DST MASK COLOR
+
+    void testnr_R8G8B8A8_N_EMPTY_A8_RGBA32()
+    {
+        for(size_t j=0; j<G_N_ELEMENTS(rgb_vals); j++) {
+            for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+                unsigned int rgba = rgb_vals[j]+alpha_vals[i];
+                char msg[100];
+                sprintf(msg, "color = (%u,%u,%u,%u)", (rgba>>24u)&0xff, (rgba>>16u)&0xff, (rgba>>8u)&0xff, rgba&0xff);
+                memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+                memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+                nr_R8G8B8A8_N_EMPTY_A8_RGBA32(dst1_rgba, w, h, w*4, mask, w, rgba);
+                nr_R8G8B8A8_N_EMPTY_A8_RGBA32_ref(dst2_rgba, w, h, w*4, mask, w, rgba);
+                TSM_ASSERT(msg, IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+            }
+        }
+    }
+
+    void testnr_R8G8B8A8_P_EMPTY_A8_RGBA32()
+    {
+        for(size_t j=0; j<G_N_ELEMENTS(rgb_vals); j++) {
+            for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+                unsigned int rgba = rgb_vals[j]+alpha_vals[i];
+                char msg[100];
+                sprintf(msg, "color = (%u,%u,%u,%u)", (rgba>>24u)&0xff, (rgba>>16u)&0xff, (rgba>>8u)&0xff, rgba&0xff);
+                memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+                memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+                nr_R8G8B8A8_P_EMPTY_A8_RGBA32(dst1_rgba, w, h, w*4, mask, w, rgba);
+                nr_R8G8B8A8_P_EMPTY_A8_RGBA32_ref(dst2_rgba, w, h, w*4, mask, w, rgba);
+                TSM_ASSERT(msg, IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+            }
+        }
+    }
+
+    void testnr_R8G8B8_R8G8B8_A8_RGBA32()
+    {
+        for(size_t j=0; j<G_N_ELEMENTS(rgb_vals); j++) {
+            for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+                unsigned int rgba = rgb_vals[j]+alpha_vals[i];
+                char msg[100];
+                sprintf(msg, "color = (%u,%u,%u,%u)", (rgba>>24u)&0xff, (rgba>>16u)&0xff, (rgba>>8u)&0xff, rgba&0xff);
+                memcpy(dst1_rgb, dst_rgb_org, w*h*3);
+                memcpy(dst2_rgb, dst_rgb_org, w*h*3);
+                nr_R8G8B8_R8G8B8_A8_RGBA32(dst1_rgb, w, h, w*3, mask, w, rgba);
+                nr_R8G8B8_R8G8B8_A8_RGBA32_ref(dst2_rgb, w, h, w*3, mask, w, rgba);
+                TSM_ASSERT(msg, IMGCMP<R8G8B8>(dst1_rgb, dst2_rgb, w*h*3) == 0 );
+            }
+        }
+    }
+
+    void testnr_R8G8B8A8_N_R8G8B8A8_N_A8_RGBA32()
+    {
+        for(size_t j=0; j<G_N_ELEMENTS(rgb_vals); j++) {
+            for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+                unsigned int rgba = rgb_vals[j]+alpha_vals[i];
+                char msg[100];
+                sprintf(msg, "color = (%u,%u,%u,%u)", (rgba>>24u)&0xff, (rgba>>16u)&0xff, (rgba>>8u)&0xff, rgba&0xff);
+                memcpy(dst1_rgba, dst_rgba_n_org, w*h*4);
+                memcpy(dst2_rgba, dst_rgba_n_org, w*h*4);
+                nr_R8G8B8A8_N_R8G8B8A8_N_A8_RGBA32(dst1_rgba, w, h, w*4, mask, w, rgba);
+                nr_R8G8B8A8_N_R8G8B8A8_N_A8_RGBA32_ref(dst2_rgba, w, h, w*4, mask, w, rgba);
+                TSM_ASSERT(msg, IMGCMP<R8G8B8A8N>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+            }
+        }
+    }
+
+    void testnr_R8G8B8A8_P_R8G8B8A8_P_A8_RGBA32()
+    {
+        for(size_t j=0; j<G_N_ELEMENTS(rgb_vals); j++) {
+            for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+                unsigned int rgba = rgb_vals[j]+alpha_vals[i];
+                char msg[100];
+                sprintf(msg, "color = (%u,%u,%u,%u)", (rgba>>24u)&0xff, (rgba>>16u)&0xff, (rgba>>8u)&0xff, rgba&0xff);
+                memcpy(dst1_rgba, dst_rgba_p_org, w*h*4);
+                memcpy(dst2_rgba, dst_rgba_p_org, w*h*4);
+                nr_R8G8B8A8_P_R8G8B8A8_P_A8_RGBA32(dst1_rgba, w, h, w*4, mask, w, rgba);
+                nr_R8G8B8A8_P_R8G8B8A8_P_A8_RGBA32_ref(dst2_rgba, w, h, w*4, mask, w, rgba);
+                TSM_ASSERT(msg, IMGCMP<R8G8B8A8P>(dst1_rgba, dst2_rgba, w*h*4) == 0 );
+            }
+        }
+    }
+
+    // RGB
+
+    void testnr_R8G8B8_R8G8B8_R8G8B8A8_N()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgb, dst_rgb_org, w*h*3);
+            memcpy(dst2_rgb, dst_rgb_org, w*h*3);
+            nr_R8G8B8_R8G8B8_R8G8B8A8_N(dst1_rgb, w, h, w*3, src_rgba_n, w*4, alpha);
+            nr_R8G8B8_R8G8B8_R8G8B8A8_N_ref(dst2_rgb, w, h, w*3, src_rgba_n, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8>(dst1_rgb, dst2_rgb, w*h*3) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8_R8G8B8_R8G8B8A8_P()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgb, dst_rgb_org, w*h*3);
+            memcpy(dst2_rgb, dst_rgb_org, w*h*3);
+            nr_R8G8B8_R8G8B8_R8G8B8A8_P(dst1_rgb, w, h, w*3, src_rgba_p, w*4, alpha);
+            nr_R8G8B8_R8G8B8_R8G8B8A8_P_ref(dst2_rgb, w, h, w*3, src_rgba_p, w*4, alpha);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8>(dst1_rgb, dst2_rgb, w*h*3) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8_R8G8B8_R8G8B8A8_N_A8()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgb, dst_rgb_org, w*h*3);
+            memcpy(dst2_rgb, dst_rgb_org, w*h*3);
+            nr_R8G8B8_R8G8B8_R8G8B8A8_N_A8(dst1_rgb, w, h, w*3, src_rgba_n, w*4, mask, w);
+            nr_R8G8B8_R8G8B8_R8G8B8A8_N_A8_ref(dst2_rgb, w, h, w*3, src_rgba_n, w*4, mask, w);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8>(dst1_rgb, dst2_rgb, w*h*3) == 0 );
+        }
+    }
+
+    void testnr_R8G8B8_R8G8B8_R8G8B8A8_P_A8()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(alpha_vals); i++) {
+            unsigned int alpha = alpha_vals[i];
+            char msg[40];
+            sprintf(msg, "alpha = %u", alpha);
+            memcpy(dst1_rgb, dst_rgb_org, w*h*3);
+            memcpy(dst2_rgb, dst_rgb_org, w*h*3);
+            nr_R8G8B8_R8G8B8_R8G8B8A8_P_A8(dst1_rgb, w, h, w*3, src_rgba_p, w*4, mask, w);
+            nr_R8G8B8_R8G8B8_R8G8B8A8_P_A8_ref(dst2_rgb, w, h, w*3, src_rgba_p, w*4, mask, w);
+            TSM_ASSERT(msg, IMGCMP<R8G8B8>(dst1_rgb, dst2_rgb, w*h*3) == 0 );
+        }
+    }
+};
+
+unsigned int const NrComposeTest::alpha_vals[7] = {0, 1, 127, 128, 129, 254, 255};
+unsigned int const NrComposeTest::rgb_vals[3] = {
+    (  0u<<24u)+(  1u<<16u)+( 92u<<8u),
+    (127u<<24u)+(128u<<16u)+(129u<<8u),
+    (163u<<24u)+(254u<<16u)+(255u<<8u)};
+
+/*
+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 :
index 658968bce88b916d17b8f68fb95a51a0c8a18a6f..0284402edcd6dba0c5a7ac0b5785468a920b6995 100644 (file)
@@ -40,6 +40,7 @@ private:
     static test_t const read_scale_tests[3];
     static test_t const read_rotate_tests[4];
     static test_t const read_skew_tests[3];
+    static char const * read_fail_tests[25];
     static test_t const write_matrix_tests[2];
     static test_t const write_translate_tests[3];
     static test_t const write_scale_tests[2];
@@ -155,6 +156,9 @@ public:
 
     void testReadConcatenation()
     {
+        // NOTE: According to the SVG specification (see the syntax at http://www.w3.org/TR/SVG/coords.html#TransformAttribute
+        //       there should be 1 or more comma-wsp sequences between transforms... This doesn't make sense and it seems
+        //       likely that instead of a + they meant a ? (zero or one comma-wsp sequences).
         char const * str = "skewY(17)skewX(9)translate(7,13)scale(2)rotate(13)translate(3,5)";
         Geom::Matrix ref(2.0199976232558053, 1.0674773585906016, -0.14125199392774669, 1.9055550612095459, 14.412730624347654, 28.499820929377454); // Precomputed using Mathematica
         Geom::Matrix cm;
@@ -162,15 +166,21 @@ public:
         TS_ASSERT_RELATION(approx_equal , ref , cm);
     }
 
-    // TODO: Perhaps check faulty transforms (like "translate(1,2,3)", or "matrix(1,2,3,4,5)", or ...)
+    void testReadFailures()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(read_fail_tests); i++) {
+            Geom::Matrix cm;
+            TSM_ASSERT(read_fail_tests[i] , !sp_svg_transform_read(read_fail_tests[i], &cm));
+        }
+    }
 };
 
 static double const DEGREE = M_PI/180.;
 
 SvgAffineTest::test_t const SvgAffineTest::read_matrix_tests[3] = {
     {"matrix(0,0,0,0,0,0)",Geom::Matrix(0,0,0,0,0,0)},
-    {"matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
-    {"matrix(1 2 -3,-4,5e6,-6e-7)",Geom::Matrix(1,2,-3,-4,5e6,-6e-7)}};
+    {" matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
+    {"matrix (1 2 -3,-4,5e6,-6e-7)",Geom::Matrix(1,2,-3,-4,5e6,-6e-7)}};
 SvgAffineTest::test_t const SvgAffineTest::read_translate_tests[3] = {
     {"translate(1)",Geom::Matrix(1,0,0,1,1,0)},
     {"translate(1,1)",Geom::Matrix(1,0,0,1,1,1)},
@@ -188,6 +198,32 @@ SvgAffineTest::test_t const SvgAffineTest::read_skew_tests[3] = {
     {"skewX( 30)",Geom::Matrix(1,0,tan(30.*DEGREE),1,0,0)},
     {"skewX(-30)",Geom::Matrix(1,0,tan(-30.*DEGREE),1,0,0)},
     {"skewY(390)",Geom::Matrix(1,tan(30.*DEGREE),0,1,0,0)}};
+char const * SvgAffineTest::read_fail_tests[25] = {
+    "matrix((1,2,3,4,5,6)",
+    "matrix((1,2,3,4,5,6))",
+    "matrix(1,2,3,4,5,6))",
+    "matrix(,1,2,3,4,5,6)",
+    "matrix(1,2,3,4,5,6,)",
+    "matrix(1,2,3,4,5,)",
+    "matrix(1,2,3,4,5)",
+    "matrix(1,2,3,4,5e6-3)", // Here numbers HAVE to be separated by a comma-wsp sequence
+    "matrix(1,2,3,4,5e6.3)", // Here numbers HAVE to be separated by a comma-wsp sequence
+    "translate()",
+    "translate(,)",
+    "translate(1,)",
+    "translate(1,6,)",
+    "translate(1,6,0)",
+    "scale()",
+    "scale(1,6,2)",
+    "rotate()",
+    "rotate(1,6)",
+    "rotate(1,6,)",
+    "rotate(1,6,3,4)",
+    "skewX()",
+    "skewX(-)",
+    "skewX(.)",
+    "skewY(,)",
+    "skewY(1,2)"};
 
 SvgAffineTest::test_t const SvgAffineTest::write_matrix_tests[2] = {
     {"matrix(1,2,3,4,5,6)",Geom::Matrix(1,2,3,4,5,6)},
index f6c6cb4f696b9ab4938bfc83fa0f7a13b9f54e87..2345803d86a68d8915a50d91a8deade81e9f557b 100644 (file)
 class SvgLengthTest : public CxxTest::TestSuite
 {
 private:
+    struct test_t {
+        char const* str; SVGLength::Unit unit; float value; float computed;
+    };
+    static test_t const absolute_tests[12];
+    static test_t const relative_tests[3];
+    static char const * fail_tests[8];
 public:
     SvgLengthTest() {
     }
 
     void testRead()
     {
-        struct test_t {
-            char const* str; float computed;
-            test_t(char const* str, float computed) : str(str), computed(computed) {}
-        };
-        test_t tests[] = {
-            test_t("0",0),
-            test_t("1",1),
-            test_t("1.00001",1.00001),
-            test_t("1px",1),
-            test_t(".1px",0.1)};
-        size_t n = G_N_ELEMENTS(tests);
-        for(size_t i=0; i<n; i++) {
-            SVGLength l;
-            TSM_ASSERT(tests[i].str , l.read(tests[i].str));
-            TSM_ASSERT_EQUALS(tests[i].str , l.computed , tests[i].computed);
+        for(size_t i=0; i<G_N_ELEMENTS(absolute_tests); i++) {
+            SVGLength l;
+            TSM_ASSERT(absolute_tests[i].str , l.read(absolute_tests[i].str));
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.unit , absolute_tests[i].unit);
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.value , absolute_tests[i].value);
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.computed , absolute_tests[i].computed);
+        }
+        for(size_t i=0; i<G_N_ELEMENTS(relative_tests); i++) {
+            SVGLength l;
+            TSM_ASSERT(relative_tests[i].str , l.read(relative_tests[i].str));
+            l.update(7,13,19);
+            TSM_ASSERT_EQUALS(relative_tests[i].str , l.unit , relative_tests[i].unit);
+            TSM_ASSERT_EQUALS(relative_tests[i].str , l.value , relative_tests[i].value);
+            TSM_ASSERT_EQUALS(relative_tests[i].str , l.computed , relative_tests[i].computed);
+        }
+        for(size_t i=0; i<G_N_ELEMENTS(fail_tests); i++) {
+            SVGLength l;
+            TSM_ASSERT(fail_tests[i] , !l.read(fail_tests[i]));
+        }
+    }
+
+    void testReadOrUnset()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(absolute_tests); i++) {
+            SVGLength l;
+            l.readOrUnset(absolute_tests[i].str);
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.unit , absolute_tests[i].unit);
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.value , absolute_tests[i].value);
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.computed , absolute_tests[i].computed);
+        }
+        for(size_t i=0; i<G_N_ELEMENTS(relative_tests); i++) {
+            SVGLength l;
+            l.readOrUnset(relative_tests[i].str);
+            l.update(7,13,19);
+            TSM_ASSERT_EQUALS(relative_tests[i].str , l.unit , relative_tests[i].unit);
+            TSM_ASSERT_EQUALS(relative_tests[i].str , l.value , relative_tests[i].value);
+            TSM_ASSERT_EQUALS(relative_tests[i].str , l.computed , relative_tests[i].computed);
+        }
+        for(size_t i=0; i<G_N_ELEMENTS(fail_tests); i++) {
+            SVGLength l;
+            l.readOrUnset(fail_tests[i], SVGLength::INCH, 123, 456);
+            TSM_ASSERT_EQUALS(fail_tests[i] , l.unit , SVGLength::INCH);
+            TSM_ASSERT_EQUALS(fail_tests[i] , l.value , 123);
+            TSM_ASSERT_EQUALS(fail_tests[i] , l.computed , 456);
+        }
+    }
+
+    void testReadAbsolute()
+    {
+        for(size_t i=0; i<G_N_ELEMENTS(absolute_tests); i++) {
+            SVGLength l;
+            TSM_ASSERT(absolute_tests[i].str , l.readAbsolute(absolute_tests[i].str));
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.unit , absolute_tests[i].unit);
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.value , absolute_tests[i].value);
+            TSM_ASSERT_EQUALS(absolute_tests[i].str , l.computed , absolute_tests[i].computed);
+        }
+        for(size_t i=0; i<G_N_ELEMENTS(relative_tests); i++) {
+            SVGLength l;
+            TSM_ASSERT(relative_tests[i].str , !l.readAbsolute(relative_tests[i].str));
+        }
+        for(size_t i=0; i<G_N_ELEMENTS(fail_tests); i++) {
+            SVGLength l;
+            TSM_ASSERT(fail_tests[i] , !l.readAbsolute(fail_tests[i]));
         }
     }
 
     // TODO: More tests
 };
 
+SvgLengthTest::test_t const SvgLengthTest::absolute_tests[12] = {
+    {"0",            SVGLength::NONE,   0        ,   0},
+    {"1",            SVGLength::NONE,   1        ,   1},
+    {"1.00001",      SVGLength::NONE,   1.00001  ,   1.00001},
+    {"1px",          SVGLength::PX  ,   1        ,   1},
+    {".1px",         SVGLength::PX  ,   0.1      ,   0.1},
+    {"100pt",        SVGLength::PT  , 100        , 125},
+    {"1e2pt",        SVGLength::PT  , 100        , 125},
+    {"3pc",          SVGLength::PC  ,   3        ,  45},
+    {"-3.5pc",       SVGLength::PC  ,  -3.5      ,  -3.5*15.},
+    {"1.2345678mm",  SVGLength::MM  ,   1.2345678,   1.2345678*3.543307}, // TODO: More precise constants? (a 7 digit constant when the default precision is 8 digits?)
+    {"123.45678cm", SVGLength::CM   , 123.45678  , 123.45678*35.43307},
+    {"73.162987in",  SVGLength::INCH,  73.162987 ,  73.162987*90}};
+SvgLengthTest::test_t const SvgLengthTest::relative_tests[3] = {
+    {"123em", SVGLength::EM,      123, 123. *  7.},
+    {"123ex", SVGLength::EX,      123, 123. * 13.},
+    {"123%",  SVGLength::PERCENT, 1.23, 1.23 * 19.}};
+char const * SvgLengthTest::fail_tests[8] = {
+    "123 px",
+    "123e",
+    "123e+m",
+    "123ec",
+    "123pxt",
+    "--123",
+    "",
+    "px"};
+
 /*
   Local Variables:
   mode:c++