Merge "add validation of blend functions"
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
index f38bdb0..df8a6fe 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ImageProcessingTest.java
@@ -47,6 +47,7 @@
import android.renderscript.Short4;
import android.renderscript.Matrix4f;
+import android.renderscript.Script;
import android.renderscript.Type;
@@ -58,6 +59,7 @@
import android.renderscript.ScriptIntrinsicConvolve3x3;
import android.renderscript.ScriptIntrinsicConvolve5x5;
import android.renderscript.ScriptIntrinsicLUT;
+import android.util.Log;
public class ImageProcessingTest extends RSBaseCompute {
private Allocation a1, a2;
@@ -100,67 +102,551 @@
public void testBlend() {
ScriptIntrinsicBlend mBlend;
mBlend = ScriptIntrinsicBlend.create(mRS, Element.U8_4(mRS));
-
- Allocation a1_copy, a2_copy;
- a1_copy = Allocation.createTyped(mRS, a1.getType());
- a2_copy = Allocation.createTyped(mRS, a2.getType());
-
- for (int i = 0; i < 14; i++) {
-
- a1_copy.copy2DRangeFrom(0, 0, a1.getType().getX(), a1.getType().getY(), a1, 0, 0);
- a2_copy.copy2DRangeFrom(0, 0, a2.getType().getX(), a2.getType().getY(), a2, 0, 0);
+ int w = 256;
+ int h = 256;
+ Allocation src = creatAllocation(w, h);
+ Allocation dst = creatAllocation(w, h);
+ byte[] srcData = new byte[w * h * 4];
+ byte[] dstData = new byte[w * h * 4];
+ byte[] resultData = new byte[w * h * 4];
+ Script.LaunchOptions opt = new Script.LaunchOptions();
+ // unclipped but with options
+ for (int i = 0; i < 28; i++) {
+ buildSrc(srcData, w, h);
+ buildDst(dstData, w, h);
+ src.copyFromUnchecked(srcData);
+ dst.copyFromUnchecked(dstData);
switch (i) {
- case 0:
- mBlend.forEachSrc(a1_copy, a2_copy);
- break;
- case 1:
- mBlend.forEachDst(a1_copy, a2_copy);
- break;
- case 2:
- mBlend.forEachSrcOver(a1_copy, a2_copy);
- break;
- case 3:
- mBlend.forEachDstOver(a1_copy, a2_copy);
- break;
- case 4:
- mBlend.forEachSrcIn(a1_copy, a2_copy);
- break;
- case 5:
- mBlend.forEachDstIn(a1_copy, a2_copy);
- break;
- case 6:
- mBlend.forEachSrcOut(a1_copy, a2_copy);
- break;
- case 7:
- mBlend.forEachDstOut(a1_copy, a2_copy);
- break;
- case 8:
- mBlend.forEachSrcAtop(a1_copy, a2_copy);
- break;
- case 9:
- mBlend.forEachDstAtop(a1_copy, a2_copy);
- break;
- case 10:
- mBlend.forEachXor(a1_copy, a2_copy);
- break;
- case 11:
- mBlend.forEachAdd(a1_copy, a2_copy);
- break;
- case 12:
- mBlend.forEachSubtract(a1_copy, a2_copy);
- break;
- case 13:
- mBlend.forEachMultiply(a1_copy, a2_copy);
- break;
+ case 0:
+ mBlend.forEachSrc(src, dst);
+ break;
+ case 1:
+ mBlend.forEachDst(src, dst);
+ break;
+ case 2:
+ mBlend.forEachSrcOver(src, dst);
+ break;
+ case 3:
+ mBlend.forEachDstOver(src, dst);
+ break;
+ case 4:
+ mBlend.forEachSrcIn(src, dst);
+ break;
+ case 5:
+ mBlend.forEachDstIn(src, dst);
+ break;
+ case 6:
+ mBlend.forEachSrcOut(src, dst);
+ break;
+ case 7:
+ mBlend.forEachDstOut(src, dst);
+ break;
+ case 8:
+ mBlend.forEachSrcAtop(src, dst);
+ break;
+ case 9:
+ mBlend.forEachDstAtop(src, dst);
+ break;
+ case 10:
+ mBlend.forEachXor(src, dst);
+ break;
+ case 11:
+ mBlend.forEachAdd(src, dst);
+ break;
+ case 12:
+ mBlend.forEachSubtract(src, dst);
+ break;
+ case 13:
+ mBlend.forEachMultiply(src, dst);
+ break;
+ case 14:
+ mBlend.forEachSrc(src, dst, opt);
+ break;
+ case 15:
+ mBlend.forEachDst(src, dst, opt);
+ break;
+ case 16:
+ mBlend.forEachSrcOver(src, dst, opt);
+ break;
+ case 17:
+ mBlend.forEachDstOver(src, dst, opt);
+ break;
+ case 18:
+ mBlend.forEachSrcIn(src, dst, opt);
+ break;
+ case 19:
+ mBlend.forEachDstIn(src, dst, opt);
+ break;
+ case 20:
+ mBlend.forEachSrcOut(src, dst, opt);
+ break;
+ case 21:
+ mBlend.forEachDstOut(src, dst, opt);
+ break;
+ case 22:
+ mBlend.forEachSrcAtop(src, dst, opt);
+ break;
+ case 23:
+ mBlend.forEachDstAtop(src, dst, opt);
+ break;
+ case 24:
+ mBlend.forEachXor(src, dst, opt);
+ break;
+ case 25:
+ mBlend.forEachAdd(src, dst, opt);
+ break;
+ case 26:
+ mBlend.forEachSubtract(src, dst, opt);
+ break;
+ case 27:
+ mBlend.forEachMultiply(src, dst, opt);
+ break;
}
+ dst.copyTo(resultData);
+ String name = javaBlend(i%14, srcData, dstData);
+ assertTrue(name, similar(resultData,dstData));
+ Log.v("BlendUnit", name + " " + similar(resultData, dstData));
- // validate
+ }
+ }
+ // utility to create and allocation of a given dimension
+ protected Allocation creatAllocation(int w, int h) {
+ Type.Builder b = new Type.Builder(mRS, Element.RGBA_8888(mRS));
+ b.setX(w);
+ b.setY(h);
+ return Allocation.createTyped(mRS, b.create(), Allocation.USAGE_SCRIPT);
+ }
+
+ // Compare two images ensuring returning false if error is greater than 2
+ // so that it can support integer and float non identical versions
+ public boolean similar(byte[] a, byte[] b) {
+ for (int i = 0; i < a.length; i++) {
+ int v1 = 0xFF & a[i];
+ int v2 = 0xFF & b[i];
+ int error = Math.abs(v1 - v2);
+ if (error > 2) {
+ return false;
+ }
+ }
+ return true;
+ }
+ // Build a test pattern to be the source pattern designed to provide a wide range of values
+ public void buildSrc(byte[] srcData, int width, int height) {
+ for (int i = 0; i < srcData.length / 4; i++) {
+ int x = i % width;
+ int y = i / width;
+ int d = (x - width / 2) * (x - width / 2) + (y - height / 2) * (y - height / 2);
+ d = (255 * d) / ((width / 2) * (width / 2));
+ d = (d > 255) ? 0 : d;
+
+ srcData[i * 4 + 0] = (byte) d; // red
+ srcData[i * 4 + 1] = (byte) d; // green
+ srcData[i * 4 + 2] = (byte) 0; // blue
+ srcData[i * 4 + 3] = (byte) y; // alpha
+ }
+ }
+
+ // Build a test pattern to be the destination pattern designed to provide a wide range of values
+ public void buildDst(byte[] dstData, int width, int height) {
+ for (int i = 0; i < dstData.length / 4; i++) {
+ int x = i % width;
+ int y = i / width;
+
+ dstData[i * 4 + 0] = (byte) 0; // red
+ dstData[i * 4 + 1] = (byte) 0; // green
+ dstData[i * 4 + 2] = (byte) y; // blue
+ dstData[i * 4 + 3] = (byte) x; // alpha
}
}
+ public String javaBlend(int type, byte[] src, byte[] dst) {
+
+ for (int i = 0; i < dst.length; i += 4) {
+ byte[] rgba = func[type].filter(src[i], src[i + 1], src[i + 2], src[i + 3],
+ dst[i], dst[i + 1], dst[i + 2], dst[i + 3]);
+ dst[i] = rgba[0];
+ dst[i + 1] = rgba[1];
+ dst[i + 2] = rgba[2];
+ dst[i + 3] = rgba[3];
+ }
+ return func[type].name;
+ }
+
+ // Base class for Java blend implementation supporting float and int implementations
+
+ abstract class BlendFunc {
+ float srcR, srcG, srcB, srcA;
+ float dstR, dstG, dstB, dstA;
+ int s_srcR, s_srcG, s_srcB, s_srcA;
+ int s_dstR, s_dstG, s_dstB, s_dstA;
+ byte[] rgba = new byte[4];
+ String name;
+
+ final int clamp(int c) {
+ final int N = 255;
+ c &= ~(c >> 31);
+ c -= N;
+ c &= (c >> 31);
+ c += N;
+ return c;
+ }
+
+ int pack(float a, float r, float g, float b) {
+ int ia = clamp((int) (255 * a));
+ int ir = clamp((int) (255 * r));
+ int ig = clamp((int) (255 * g));
+ int ib = clamp((int) (255 * b));
+ rgba[0] = (byte) ir;
+ rgba[1] = (byte) ig;
+ rgba[2] = (byte) ib;
+ rgba[3] = (byte) ia;
+ return (ia << 24) | (ir << 16) | (ig << 8) | ib;
+ }
+
+ int pack(int a, int r, int g, int b) {
+
+ rgba[0] = (byte) clamp(r);
+ rgba[1] = (byte) clamp(g);
+ rgba[2] = (byte) clamp(b);
+ rgba[3] = (byte) clamp(a);
+ return 0;
+ }
+
+ void unpackSrc(int src) {
+ s_srcR = (0xFF & (src >> 16));
+ s_srcG = (0xFF & (src >> 8));
+ s_srcB = (0xFF & (src >> 0));
+ s_srcA = (0xFF & (src >> 24));
+ float scale = 1 / 255f;
+
+ srcR = (0xFF & (src >> 16)) * scale;
+ srcG = (0xFF & (src >> 8)) * scale;
+ srcB = (0xFF & (src >> 0)) * scale;
+ srcA = (0xFF & (src >> 24)) * scale;
+ }
+
+ void unpackDst(int dst) {
+ float scale = 1 / 255f;
+
+ s_dstR = (0xFF & (dst >> 16));
+ s_dstG = (0xFF & (dst >> 8));
+ s_dstB = (0xFF & (dst >> 0));
+ s_dstA = (0xFF & (dst >> 24));
+
+ dstR = (0xFF & (dst >> 16)) * scale;
+ dstG = (0xFF & (dst >> 8)) * scale;
+ dstB = (0xFF & (dst >> 0)) * scale;
+ dstA = (0xFF & (dst >> 24)) * scale;
+ }
+
+ int filter(int scr, int dst) {
+ unpackSrc(scr);
+ unpackDst(dst);
+ return blend();
+ }
+
+ byte[] filter(byte srcR, byte srcG, byte srcB, byte srcA,
+ byte dstR, byte dstG, byte dstB, byte dstA) {
+ float scale = 1 / 255f;
+ this.srcR = (0xFF & (srcR)) * scale;
+ this.srcG = (0xFF & (srcG)) * scale;
+ this.srcB = (0xFF & (srcB)) * scale;
+ this.srcA = (0xFF & (srcA)) * scale;
+
+ this.dstR = (0xFF & (dstR)) * scale;
+ this.dstG = (0xFF & (dstG)) * scale;
+ this.dstB = (0xFF & (dstB)) * scale;
+ this.dstA = (0xFF & (dstA)) * scale;
+ s_dstR = (0xFF & (dstR));
+ s_dstG = (0xFF & (dstG));
+ s_dstB = (0xFF & (dstB));
+ s_dstA = (0xFF & (dstA));
+
+ s_srcR = (0xFF & (srcR));
+ s_srcG = (0xFF & (srcG));
+ s_srcB = (0xFF & (srcB));
+ s_srcA = (0xFF & (srcA));
+
+ blend();
+ return rgba;
+ }
+
+ abstract int blend();
+ }
+
+ BlendFunc blend_dstAtop = new BlendFunc() {
+ // dst = dst.rgb * src.a + (1.0 - dst.a) * src.rgb
+ // dst.a = src.a
+ {
+ name = "blend_dstAtop";
+ }
+
+ @Override
+ int blend() {
+ float r = (dstR * srcA + (1 - dstA) * srcR);
+ float g = (dstG * srcA + (1 - dstA) * srcG);
+ float b = (dstB * srcA + (1 - dstA) * srcB);
+ float a = srcA;
+
+ return pack(a, r, g, b);
+ }
+ };
+ BlendFunc blend_dstIn = new BlendFunc() {
+ // Sets dst = dst * src.a
+ {
+ name = "blend_dstIn";
+ }
+
+ @Override
+ int blend() {
+ float r = (dstR * srcA);
+ float g = (dstG * srcA);
+ float b = (dstB * srcA);
+ float a = (dstA * srcA);
+ return pack(a, r, g, b);
+ }
+ };
+
+ BlendFunc blend_add = new BlendFunc() {
+ // dst = dst + src
+ {
+ name = "blend_add";
+ }
+
+ @Override
+ int blend() {
+
+ int r = Math.min(s_dstR + s_srcR, 255);
+ int g = Math.min(s_dstG + s_srcG, 255);
+ int b = Math.min(s_dstB + s_srcB, 255);
+ int a = Math.min(s_dstA + s_srcA, 255);
+ return pack(a, r, g, b);
+
+ }
+ };
+
+ BlendFunc blend_clear = new BlendFunc() {
+ // Sets dst = {0, 0, 0, 0}
+ {
+ name = "blend_clear";
+ }
+
+ @Override
+ int blend() {
+ return pack(0, 0, 0, 0);
+ }
+ };
+
+ BlendFunc blend_dst = new BlendFunc() {
+ // Sets dst = dst
+ {
+ name = "blend_dst";
+ }
+
+ @Override
+ int blend() {
+ return pack(dstA, dstR, dstG, dstB);
+ }
+ };
+
+ BlendFunc blend_dstOut = new BlendFunc() {
+ // Sets dst = dst * (1.0 - src.a)
+ {
+ name = "blend_dstOut";
+ }
+
+ @Override
+ int blend() {
+ float r = (dstR * (1 - srcA));
+ float g = (dstG * (1 - srcA));
+ float b = (dstB * (1 - srcA));
+ float a = (dstA * (1 - srcA));
+ return pack(a, r, g, b);
+ }
+ };
+ BlendFunc blend_dstOver = new BlendFunc() {
+ // Sets dst = dst + src * (1.0 - dst.a)
+ {
+ name = "blend_dstOver";
+ }
+
+ @Override
+ int blend() {
+ float r = dstR + (srcR * (1 - dstA));
+ float g = dstG + (srcG * (1 - dstA));
+ float b = dstB + (srcB * (1 - dstA));
+ float a = dstA + (srcA * (1 - dstA));
+ return pack(a, r, g, b);
+ }
+ };
+
+ BlendFunc blend_multiply = new BlendFunc() {
+ // dst = dst * src
+ {
+ name = "blend_multiply";
+ }
+
+ @Override
+ int blend() {
+ float r = (srcR * dstR);
+ float g = (srcG * dstG);
+ float b = (srcB * dstB);
+ float a = (srcA * dstA);
+ return pack(a, r, g, b);
+ }
+ };
+
+ BlendFunc blend_src = new BlendFunc() {
+ // Sets dst = src
+ {
+ name = "blend_src";
+ }
+
+ int blend() {
+ return pack(srcA, srcR, srcG, srcB);
+
+ }
+ };
+
+ BlendFunc blend_srcAtop = new BlendFunc() {
+ // dst.rgb = src.rgb * dst.a + (1.0 - src.a) * dst.rgb
+ // dst.a = dst.a
+ {
+ name = "blend_srcAtop";
+ }
+
+ @Override
+ int blend() {
+ float r = (srcR * dstA + (1 - srcA) * dstR);
+ float g = (srcG * dstA + (1 - srcA) * dstG);
+ float b = (srcB * dstA + (1 - srcA) * dstB);
+ float a = (srcA * dstA + (1 - srcA) * dstA);
+ return pack(a, r, g, b);
+ }
+ };
+
+ BlendFunc blend_srcIn = new BlendFunc() {
+ // dst = src * dst.a
+ {
+ name = "blend_srcIn";
+ }
+
+ @Override
+ int blend() {
+ float r = (srcR * dstA);
+ float g = (srcG * dstA);
+ float b = (srcB * dstA);
+ float a = (srcA * dstA);
+ ;
+ return pack(a, r, g, b);
+ }
+ };
+
+ BlendFunc blend_srcOut = new BlendFunc() {
+ // Sets dst = src * (1.0 - dst.a)
+ {
+ name = "blend_srcOut";
+ }
+
+ @Override
+ int blend() {
+ float r = (srcR * (1 - dstA));
+ float g = (srcG * (1 - dstA));
+ float b = (srcB * (1 - dstA));
+ float a = (srcA * (1 - dstA));
+ ;
+ return pack(a, r, g, b);
+ }
+ };
+
+ BlendFunc blend_srcOver = new BlendFunc() {
+ // Sets dst = src + dst * (1.0 - src.a)
+ {
+ name = "blend_srcOver";
+ }
+
+ @Override
+ int blend() {
+ float r = srcR + (dstR * (1 - srcA));
+ float g = srcG + (dstG * (1 - srcA));
+ float b = srcB + (dstB * (1 - srcA));
+ float a = srcA + (dstA * (1 - srcA));
+ return pack(a, r, g, b);
+ }
+ };
+
+ BlendFunc blend_subtract = new BlendFunc() {
+ // Sets dst = dst - src
+ {
+ name = "blend_subtract";
+ }
+
+ @Override
+ int blend() {
+ float r = Math.max(dstR - srcR, 0);
+ float g = Math.max(dstG - srcG, 0);
+ float b = Math.max(dstB - srcB, 0);
+ float a = Math.max(dstA - srcA, 0);
+ return pack(a, r, g, b);
+ }
+ };
+
+ // Porter/Duff xor compositing
+ BlendFunc blend_pdxor = new BlendFunc() {
+ // dst.rgb = src.rgb*(1-dst.a)+(1-src.a)*dst.rgb;
+ // dst.a = src.a+dst.a - 2*src.a*dst.a
+ {
+ name = "blend_pdxor";
+ }
+
+ @Override
+ int blend() {
+ float r = srcR * (1 - dstA) + (dstR * (1 - srcA));
+ float g = srcG * (1 - dstA) + (dstG * (1 - srcA));
+ float b = srcB * (1 - dstA) + (dstB * (1 - srcA));
+ float a = srcA + dstA - (2 * srcA * dstA);
+ return pack(a, r, g, b);
+ }
+ };
+
+ // NOT Porter/Duff xor compositing simple XOR
+ BlendFunc blend_xor = new BlendFunc() {
+ // Sets dst = {src.r ^ dst.r, src.g ^ dst.g, src.b ^ dst.b, src.a ^ dst.a}
+ {
+ name = "blend_xor";
+ }
+
+ @Override
+ int blend() {
+ float scale = 1 / 255f;
+ float r = (((int) (dstR * 255)) ^ ((int) (srcR * 255))) * scale;
+ float g = (((int) (dstG * 255)) ^ ((int) (srcG * 255))) * scale;
+ float b = (((int) (dstB * 255)) ^ ((int) (srcB * 255))) * scale;
+ float a = (((int) (dstA * 255)) ^ ((int) (srcA * 255))) * scale;
+ return pack(a, r, g, b);
+ }
+ };
+
+ BlendFunc[] func = {
+ blend_src,
+ blend_dst,
+ blend_srcOver,
+ blend_dstOver,
+ blend_srcIn,
+ blend_dstIn,
+ blend_srcOut,
+ blend_dstOut,
+ blend_srcAtop,
+ blend_dstAtop,
+ blend_xor,
+ blend_add,
+ blend_subtract,
+ blend_multiply,
+ };
+
public void testColorMatrix() {
ScriptIntrinsicColorMatrix mColorMatrix;
mColorMatrix = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
@@ -198,7 +684,6 @@
}
-
public void testConvolve3x3() {
ScriptIntrinsicConvolve3x3 mConvolve3x3;
mConvolve3x3 = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));