summaryrefslogtreecommitdiffstats
path: root/tests/RenderScriptTests/ImageProcessing/src
diff options
context:
space:
mode:
Diffstat (limited to 'tests/RenderScriptTests/ImageProcessing/src')
-rw-r--r--tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java441
-rw-r--r--tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs30
-rw-r--r--tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ip.rsh15
-rw-r--r--tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.rs93
-rw-r--r--tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs93
5 files changed, 672 insertions, 0 deletions
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
new file mode 100644
index 0000000..4f2f52ab
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.image;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.graphics.BitmapFactory;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.renderscript.ScriptC;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Script;
+import android.view.SurfaceView;
+import android.view.SurfaceHolder;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.view.View;
+import java.lang.Math;
+
+public class ImageProcessingActivity extends Activity
+ implements SurfaceHolder.Callback,
+ SeekBar.OnSeekBarChangeListener {
+ private Bitmap mBitmapIn;
+ private Bitmap mBitmapOut;
+ private Bitmap mBitmapScratch;
+ private ScriptC_threshold mScript;
+ private ScriptC_vertical_blur mScriptVBlur;
+ private ScriptC_horizontal_blur mScriptHBlur;
+ private int mRadius = 0;
+ private SeekBar mRadiusSeekBar;
+
+ private float mInBlack = 0.0f;
+ private SeekBar mInBlackSeekBar;
+ private float mOutBlack = 0.0f;
+ private SeekBar mOutBlackSeekBar;
+ private float mInWhite = 255.0f;
+ private SeekBar mInWhiteSeekBar;
+ private float mOutWhite = 255.0f;
+ private SeekBar mOutWhiteSeekBar;
+ private float mGamma = 1.0f;
+ private SeekBar mGammaSeekBar;
+
+ private float mSaturation = 1.0f;
+ private SeekBar mSaturationSeekBar;
+
+ private TextView mBenchmarkResult;
+
+ @SuppressWarnings({"FieldCanBeLocal"})
+ private RenderScript mRS;
+ @SuppressWarnings({"FieldCanBeLocal"})
+ private Type mPixelType;
+ @SuppressWarnings({"FieldCanBeLocal"})
+ private Allocation mInPixelsAllocation;
+ @SuppressWarnings({"FieldCanBeLocal"})
+ private Allocation mOutPixelsAllocation;
+ @SuppressWarnings({"FieldCanBeLocal"})
+ private Allocation mScratchPixelsAllocation1;
+ private Allocation mScratchPixelsAllocation2;
+
+ private SurfaceView mSurfaceView;
+ private ImageView mDisplayView;
+
+ class FilterCallback extends RenderScript.RSMessageHandler {
+ private Runnable mAction = new Runnable() {
+ public void run() {
+ mDisplayView.invalidate();
+ }
+ };
+
+ @Override
+ public void run() {
+ mSurfaceView.removeCallbacks(mAction);
+ mSurfaceView.post(mAction);
+ }
+ }
+
+ int in[];
+ int interm[];
+ int out[];
+ int MAX_RADIUS = 25;
+ // Store our coefficients here
+ float gaussian[];
+
+ private long javaFilter() {
+ final int width = mBitmapIn.getWidth();
+ final int height = mBitmapIn.getHeight();
+ final int count = width * height;
+
+ if (in == null) {
+ in = new int[count];
+ interm = new int[count];
+ out = new int[count];
+ gaussian = new float[MAX_RADIUS * 2 + 1];
+ mBitmapIn.getPixels(in, 0, width, 0, 0, width, height);
+ }
+
+ long t = java.lang.System.currentTimeMillis();
+
+ int w, h, r;
+
+ float fRadius = (float)mRadius;
+ int radius = (int)mRadius;
+
+ // Compute gaussian weights for the blur
+ // e is the euler's number
+ float e = 2.718281828459045f;
+ float pi = 3.1415926535897932f;
+ // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+ // x is of the form [-radius .. 0 .. radius]
+ // and sigma varies with radius.
+ // Based on some experimental radius values and sigma's
+ // we approximately fit sigma = f(radius) as
+ // sigma = radius * 0.4 + 0.6
+ // The larger the radius gets, the more our gaussian blur
+ // will resemble a box blur since with large sigma
+ // the gaussian curve begins to lose its shape
+ float sigma = 0.4f * fRadius + 0.6f;
+ // Now compute the coefficints
+ // We will store some redundant values to save some math during
+ // the blur calculations
+ // precompute some values
+ float coeff1 = 1.0f / (float)(Math.sqrt( 2.0f * pi ) * sigma);
+ float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+ float normalizeFactor = 0.0f;
+ float floatR = 0.0f;
+ for (r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] = coeff1 * (float)Math.pow(e, floatR * floatR * coeff2);
+ normalizeFactor += gaussian[r + radius];
+ }
+
+ //Now we need to normalize the weights because all our coefficients need to add up to one
+ normalizeFactor = 1.0f / normalizeFactor;
+ for (r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] *= normalizeFactor;
+ }
+
+ float blurredPixelR = 0.0f;
+ float blurredPixelG = 0.0f;
+ float blurredPixelB = 0.0f;
+ float blurredPixelA = 0.0f;
+
+ for (h = 0; h < height; h ++) {
+ for (w = 0; w < width; w ++) {
+
+ blurredPixelR = 0.0f;
+ blurredPixelG = 0.0f;
+ blurredPixelB = 0.0f;
+ blurredPixelA = 0.0f;
+
+ for (r = -radius; r <= radius; r ++) {
+ // Stepping left and right away from the pixel
+ int validW = w + r;
+ // Clamp to zero and width max() isn't exposed for ints yet
+ if (validW < 0) {
+ validW = 0;
+ }
+ if (validW > width - 1) {
+ validW = width - 1;
+ }
+
+ int input = in[h*width + validW];
+
+ int R = ((input >> 24) & 0xff);
+ int G = ((input >> 16) & 0xff);
+ int B = ((input >> 8) & 0xff);
+ int A = (input & 0xff);
+
+ float weight = gaussian[r + radius];
+
+ blurredPixelR += (float)(R)*weight;
+ blurredPixelG += (float)(G)*weight;
+ blurredPixelB += (float)(B)*weight;
+ blurredPixelA += (float)(A)*weight;
+ }
+
+ int R = (int)blurredPixelR;
+ int G = (int)blurredPixelG;
+ int B = (int)blurredPixelB;
+ int A = (int)blurredPixelA;
+
+ interm[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A);
+ }
+ }
+
+ for (h = 0; h < height; h ++) {
+ for (w = 0; w < width; w ++) {
+
+ blurredPixelR = 0.0f;
+ blurredPixelG = 0.0f;
+ blurredPixelB = 0.0f;
+ blurredPixelA = 0.0f;
+ for (r = -radius; r <= radius; r ++) {
+ int validH = h + r;
+ // Clamp to zero and width
+ if (validH < 0) {
+ validH = 0;
+ }
+ if (validH > height - 1) {
+ validH = height - 1;
+ }
+
+ int input = interm[validH*width + w];
+
+ int R = ((input >> 24) & 0xff);
+ int G = ((input >> 16) & 0xff);
+ int B = ((input >> 8) & 0xff);
+ int A = (input & 0xff);
+
+ float weight = gaussian[r + radius];
+
+ blurredPixelR += (float)(R)*weight;
+ blurredPixelG += (float)(G)*weight;
+ blurredPixelB += (float)(B)*weight;
+ blurredPixelA += (float)(A)*weight;
+ }
+
+ int R = (int)blurredPixelR;
+ int G = (int)blurredPixelG;
+ int B = (int)blurredPixelB;
+ int A = (int)blurredPixelA;
+
+ out[h*width + w] = (R << 24) | (G << 16) | (B << 8) | (A);
+ }
+ }
+
+ t = java.lang.System.currentTimeMillis() - t;
+ android.util.Log.v("Img", "Java frame time ms " + t);
+ mBitmapOut.setPixels(out, 0, width, 0, 0, width, height);
+ return t;
+ }
+
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+
+ if (seekBar == mRadiusSeekBar) {
+ float fRadius = progress / 100.0f;
+ fRadius *= (float)(MAX_RADIUS);
+ mRadius = (int)fRadius;
+
+ mScript.set_radius(mRadius);
+ } else if (seekBar == mInBlackSeekBar) {
+ mInBlack = (float)progress;
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ } else if (seekBar == mOutBlackSeekBar) {
+ mOutBlack = (float)progress;
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ } else if (seekBar == mInWhiteSeekBar) {
+ mInWhite = (float)progress + 127.0f;
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ } else if (seekBar == mOutWhiteSeekBar) {
+ mOutWhite = (float)progress + 127.0f;
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ } else if (seekBar == mGammaSeekBar) {
+ mGamma = (float)progress/100.0f;
+ mGamma = Math.max(mGamma, 0.1f);
+ mGamma = 1.0f / mGamma;
+ mScriptVBlur.invoke_setGamma(mGamma);
+ } else if (seekBar == mSaturationSeekBar) {
+ mSaturation = (float)progress / 50.0f;
+ mScriptVBlur.invoke_setSaturation(mSaturation);
+ }
+
+ long t = java.lang.System.currentTimeMillis();
+ if (true) {
+ mScript.invoke_filter();
+ mOutPixelsAllocation.copyTo(mBitmapOut);
+ } else {
+ javaFilter();
+ mDisplayView.invalidate();
+ }
+
+ t = java.lang.System.currentTimeMillis() - t;
+ android.util.Log.v("Img", "Renderscript frame time core ms " + t);
+ }
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mBitmapIn = loadBitmap(R.drawable.data);
+ mBitmapOut = loadBitmap(R.drawable.data);
+ mBitmapScratch = loadBitmap(R.drawable.data);
+
+ mSurfaceView = (SurfaceView) findViewById(R.id.surface);
+ mSurfaceView.getHolder().addCallback(this);
+
+ mDisplayView = (ImageView) findViewById(R.id.display);
+ mDisplayView.setImageBitmap(mBitmapOut);
+
+ mRadiusSeekBar = (SeekBar) findViewById(R.id.radius);
+ mRadiusSeekBar.setOnSeekBarChangeListener(this);
+
+ mInBlackSeekBar = (SeekBar)findViewById(R.id.inBlack);
+ mInBlackSeekBar.setOnSeekBarChangeListener(this);
+ mInBlackSeekBar.setMax(128);
+ mInBlackSeekBar.setProgress(0);
+ mOutBlackSeekBar = (SeekBar)findViewById(R.id.outBlack);
+ mOutBlackSeekBar.setOnSeekBarChangeListener(this);
+ mOutBlackSeekBar.setMax(128);
+ mOutBlackSeekBar.setProgress(0);
+
+ mInWhiteSeekBar = (SeekBar)findViewById(R.id.inWhite);
+ mInWhiteSeekBar.setOnSeekBarChangeListener(this);
+ mInWhiteSeekBar.setMax(128);
+ mInWhiteSeekBar.setProgress(128);
+ mOutWhiteSeekBar = (SeekBar)findViewById(R.id.outWhite);
+ mOutWhiteSeekBar.setOnSeekBarChangeListener(this);
+ mOutWhiteSeekBar.setMax(128);
+ mOutWhiteSeekBar.setProgress(128);
+
+ mGammaSeekBar = (SeekBar)findViewById(R.id.inGamma);
+ mGammaSeekBar.setOnSeekBarChangeListener(this);
+ mGammaSeekBar.setMax(150);
+ mGammaSeekBar.setProgress(100);
+
+ mSaturationSeekBar = (SeekBar)findViewById(R.id.inSaturation);
+ mSaturationSeekBar.setOnSeekBarChangeListener(this);
+ mSaturationSeekBar.setProgress(50);
+
+ mBenchmarkResult = (TextView) findViewById(R.id.benchmarkText);
+ mBenchmarkResult.setText("Result: not run");
+ }
+
+ public void surfaceCreated(SurfaceHolder holder) {
+ createScript();
+ mScript.invoke_filter();
+ mOutPixelsAllocation.copyTo(mBitmapOut);
+ }
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ private void createScript() {
+ mRS = RenderScript.create(this);
+ mRS.setMessageHandler(new FilterCallback());
+
+ mInPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapIn,
+ Allocation.MipmapControl.MIPMAP_NONE,
+ Allocation.USAGE_SCRIPT);
+ mOutPixelsAllocation = Allocation.createFromBitmap(mRS, mBitmapOut,
+ Allocation.MipmapControl.MIPMAP_NONE,
+ Allocation.USAGE_SCRIPT);
+
+ Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
+ tb.setX(mBitmapIn.getWidth());
+ tb.setY(mBitmapIn.getHeight());
+ mScratchPixelsAllocation1 = Allocation.createTyped(mRS, tb.create());
+ mScratchPixelsAllocation2 = Allocation.createTyped(mRS, tb.create());
+
+ mScriptVBlur = new ScriptC_vertical_blur(mRS, getResources(), R.raw.vertical_blur);
+ mScriptHBlur = new ScriptC_horizontal_blur(mRS, getResources(), R.raw.horizontal_blur);
+
+ mScript = new ScriptC_threshold(mRS, getResources(), R.raw.threshold);
+ mScript.set_width(mBitmapIn.getWidth());
+ mScript.set_height(mBitmapIn.getHeight());
+ mScript.set_radius(mRadius);
+
+ mScriptVBlur.invoke_setLevels(mInBlack, mOutBlack, mInWhite, mOutWhite);
+ mScriptVBlur.invoke_setGamma(mGamma);
+ mScriptVBlur.invoke_setSaturation(mSaturation);
+
+ mScript.bind_InPixel(mInPixelsAllocation);
+ mScript.bind_OutPixel(mOutPixelsAllocation);
+ mScript.bind_ScratchPixel1(mScratchPixelsAllocation1);
+ mScript.bind_ScratchPixel2(mScratchPixelsAllocation2);
+
+ mScript.set_vBlurScript(mScriptVBlur);
+ mScript.set_hBlurScript(mScriptHBlur);
+ }
+
+ private Bitmap loadBitmap(int resource) {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ return copyBitmap(BitmapFactory.decodeResource(getResources(), resource, options));
+ }
+
+ private static Bitmap copyBitmap(Bitmap source) {
+ Bitmap b = Bitmap.createBitmap(source.getWidth(), source.getHeight(), source.getConfig());
+ Canvas c = new Canvas(b);
+ c.drawBitmap(source, 0, 0, null);
+ source.recycle();
+ return b;
+ }
+
+ // button hook
+ public void benchmark(View v) {
+ android.util.Log.v("Img", "Benchmarking");
+ int oldRadius = mRadius;
+ mRadius = MAX_RADIUS;
+ mScript.set_radius(mRadius);
+
+ long t = java.lang.System.currentTimeMillis();
+
+ mScript.invoke_filter();
+ mOutPixelsAllocation.copyTo(mBitmapOut);
+
+ t = java.lang.System.currentTimeMillis() - t;
+ android.util.Log.v("Img", "Renderscript frame time core ms " + t);
+
+ //long javaTime = javaFilter();
+ //mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
+ mBenchmarkResult.setText("Result: " + t + " ms");
+
+ mRadius = oldRadius;
+ mScript.set_radius(mRadius);
+
+ mScript.invoke_filter();
+ mOutPixelsAllocation.copyTo(mBitmapOut);
+ }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
new file mode 100644
index 0000000..652ffd7
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs
@@ -0,0 +1,30 @@
+#pragma version(1)
+
+#include "ip.rsh"
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+ float4 *output = (float4 *)v_out;
+ const FilterStruct *fs = (const FilterStruct *)usrData;
+ const float4 *input = (const float4 *)rsGetElementAt(fs->ain, 0, y);
+
+ float3 blurredPixel = 0;
+ const float *gPtr = fs->gaussian;
+ if ((x > fs->radius) && (x < (fs->width - fs->radius))) {
+ const float4 *i = input + (x - fs->radius);
+ for (int r = -fs->radius; r <= fs->radius; r ++) {
+ blurredPixel += i->xyz * gPtr[0];
+ gPtr++;
+ i++;
+ }
+ } else {
+ for (int r = -fs->radius; r <= fs->radius; r ++) {
+ // Stepping left and right away from the pixel
+ int validW = rsClamp(x + r, (uint)0, (uint)(fs->width - 1));
+ blurredPixel += input[validW].xyz * gPtr[0];
+ gPtr++;
+ }
+ }
+
+ output->xyz = blurredPixel;
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ip.rsh b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ip.rsh
new file mode 100644
index 0000000..1d7a719
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ip.rsh
@@ -0,0 +1,15 @@
+#pragma rs java_package_name(com.android.rs.image)
+
+#define MAX_RADIUS 25
+
+typedef struct FilterStruct_s {
+ rs_allocation ain;
+
+ float *gaussian; //[MAX_RADIUS * 2 + 1];
+ int height;
+ int width;
+ int radius;
+
+} FilterStruct;
+
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.rs
new file mode 100644
index 0000000..f2f9a36
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/threshold.rs
@@ -0,0 +1,93 @@
+#pragma version(1)
+
+#include "ip.rsh"
+
+int height;
+int width;
+int radius;
+
+uchar4 * InPixel;
+uchar4 * OutPixel;
+float4 * ScratchPixel1;
+float4 * ScratchPixel2;
+
+rs_script vBlurScript;
+rs_script hBlurScript;
+
+const int CMD_FINISHED = 1;
+
+// Store our coefficients here
+static float gaussian[MAX_RADIUS * 2 + 1];
+
+
+static void computeGaussianWeights() {
+ // Compute gaussian weights for the blur
+ // e is the euler's number
+ float e = 2.718281828459045f;
+ float pi = 3.1415926535897932f;
+ // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 )
+ // x is of the form [-radius .. 0 .. radius]
+ // and sigma varies with radius.
+ // Based on some experimental radius values and sigma's
+ // we approximately fit sigma = f(radius) as
+ // sigma = radius * 0.4 + 0.6
+ // The larger the radius gets, the more our gaussian blur
+ // will resemble a box blur since with large sigma
+ // the gaussian curve begins to lose its shape
+ float sigma = 0.4f * (float)radius + 0.6f;
+
+ // Now compute the coefficints
+ // We will store some redundant values to save some math during
+ // the blur calculations
+ // precompute some values
+ float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma);
+ float coeff2 = - 1.0f / (2.0f * sigma * sigma);
+
+ float normalizeFactor = 0.0f;
+ float floatR = 0.0f;
+ int r;
+ for (r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2);
+ normalizeFactor += gaussian[r + radius];
+ }
+
+ //Now we need to normalize the weights because all our coefficients need to add up to one
+ normalizeFactor = 1.0f / normalizeFactor;
+ for (r = -radius; r <= radius; r ++) {
+ floatR = (float)r;
+ gaussian[r + radius] *= normalizeFactor;
+ }
+}
+
+
+static void copyInput() {
+ rs_allocation ain;
+ rsSetObject(&ain,rsGetAllocation(InPixel));
+ uint32_t dimx = rsAllocationGetDimX(ain);
+ uint32_t dimy = rsAllocationGetDimY(ain);
+ for (uint32_t y = 0; y < dimy; y++) {
+ for (uint32_t x = 0; x < dimx; x++) {
+ ScratchPixel1[x + y * dimx] = convert_float4(InPixel[x + y * dimx]);
+ }
+ }
+}
+
+void filter() {
+ copyInput();
+ computeGaussianWeights();
+
+ FilterStruct fs;
+ fs.gaussian = gaussian;
+ fs.width = width;
+ fs.height = height;
+ fs.radius = radius;
+
+ fs.ain = rsGetAllocation(ScratchPixel1);
+ rsForEach(hBlurScript, fs.ain, rsGetAllocation(ScratchPixel2), &fs);
+
+ fs.ain = rsGetAllocation(ScratchPixel2);
+ rsForEach(vBlurScript, fs.ain, rsGetAllocation(OutPixel), &fs);
+ rsSendToClientBlocking(CMD_FINISHED);
+}
+
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
new file mode 100644
index 0000000..bd4ae4e
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/vertical_blur.rs
@@ -0,0 +1,93 @@
+#pragma version(1)
+
+#include "ip.rsh"
+
+static float inBlack;
+static float outBlack;
+static float inWhite;
+static float outWhite;
+static float3 gamma;
+static float saturation;
+
+static float inWMinInB;
+static float outWMinOutB;
+static float overInWMinInB;
+static rs_matrix3x3 colorMat;
+
+void setLevels(float iBlk, float oBlk, float iWht, float oWht) {
+ inBlack = iBlk;
+ outBlack = oBlk;
+ inWhite = iWht;
+ outWhite = oWht;
+
+ inWMinInB = inWhite - inBlack;
+ outWMinOutB = outWhite - outBlack;
+ overInWMinInB = 1.f / inWMinInB;
+}
+
+void setSaturation(float sat) {
+ saturation = sat;
+
+ // Saturation
+ // Linear weights
+ //float rWeight = 0.3086f;
+ //float gWeight = 0.6094f;
+ //float bWeight = 0.0820f;
+
+ // Gamma 2.2 weights (we haven't converted our image to linear space yet for perf reasons)
+ float rWeight = 0.299f;
+ float gWeight = 0.587f;
+ float bWeight = 0.114f;
+
+ float oneMinusS = 1.0f - saturation;
+ rsMatrixSet(&colorMat, 0, 0, oneMinusS * rWeight + saturation);
+ rsMatrixSet(&colorMat, 0, 1, oneMinusS * rWeight);
+ rsMatrixSet(&colorMat, 0, 2, oneMinusS * rWeight);
+ rsMatrixSet(&colorMat, 1, 0, oneMinusS * gWeight);
+ rsMatrixSet(&colorMat, 1, 1, oneMinusS * gWeight + saturation);
+ rsMatrixSet(&colorMat, 1, 2, oneMinusS * gWeight);
+ rsMatrixSet(&colorMat, 2, 0, oneMinusS * bWeight);
+ rsMatrixSet(&colorMat, 2, 1, oneMinusS * bWeight);
+ rsMatrixSet(&colorMat, 2, 2, oneMinusS * bWeight + saturation);
+}
+
+void setGamma(float g) {
+ gamma = (float3)g;
+}
+
+//sliao
+extern uchar3 __attribute__((overloadable)) convert2uchar3(float3 xyz);
+
+void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) {
+ uchar4 *output = (uchar4 *)v_out;
+ const FilterStruct *fs = (const FilterStruct *)usrData;
+ const float4 *input = (const float4 *)rsGetElementAt(fs->ain, x, 0);
+
+ float3 blurredPixel = 0;
+ const float *gPtr = fs->gaussian;
+ if ((y > fs->radius) && (y < (fs->height - fs->radius))) {
+ const float4 *i = input + ((y - fs->radius) * fs->width);
+ for (int r = -fs->radius; r <= fs->radius; r ++) {
+ blurredPixel += i->xyz * gPtr[0];
+ gPtr++;
+ i += fs->width;
+ }
+ } else {
+ for (int r = -fs->radius; r <= fs->radius; r ++) {
+ int validH = rsClamp(y + r, (uint)0, (uint)(fs->height - 1));
+ const float4 *i = input + validH * fs->width;
+ blurredPixel += i->xyz * gPtr[0];
+ gPtr++;
+ }
+ }
+
+ float3 temp = rsMatrixMultiply(&colorMat, blurredPixel);
+ temp = (clamp(temp, 0.f, 255.f) - inBlack) * overInWMinInB;
+ if (gamma.x != 1.0f)
+ temp = pow(temp, (float3)gamma);
+ temp = clamp(temp * outWMinOutB + outBlack, 0.f, 255.f);
+
+ output->xyz = convert_uchar3(temp);
+ //output->w = input->w;
+}
+