[Android] 图片JNI(C++\Java)高斯模糊 多线程

在我的博客中,曾经发布了一篇高斯模糊(堆栈模糊)的文章;在其中使用了国外的一个堆栈模糊来实现对图片的模糊处理;同时弄了一个JNI C++ 的版本。

这篇文章依然是堆栈模糊;可以说最原始的地方还是堆栈模糊部分;只不过是支持多线程的。

纳尼??感情是之前那个不支持多线程?Sorry,我说错了;两个都是支持多线程调用的。不过新讲的这个是能在内部采用多线程进行分段模糊。

原来的:[Android]-图片JNI(C++\Java)高斯模糊的实现与比较

开工吧

说明:其中代码大部分来源于网络,不过都是开源的。

最原始的代码:

stackblur.cpp

// The Stack Blur Algorithm was invented by Mario Klingemann,
// [email protected] and described here:
// http://incubator.quasimondo.com/processing/fast_blur_deluxe.php

// This is C++ RGBA (32 bit color) multi-threaded version
// by Victor Laskin ([email protected])
// More details: http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp

// This code is using MVThread class from my cross-platform framework
// You can exchange it with any thread implementation you like

// -------------------------------------- stackblur ----------------------------------------->

static unsigned short const stackblur_mul[255] =
{
		512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
		454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
		482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
		437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
		497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
		320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
		446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
		329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
		505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
		399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
		324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
		268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
		451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
		385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
		332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
		289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
};

static unsigned char const stackblur_shr[255] =
{
		9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
		17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
		19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
		20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
		21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
		21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
		22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
		22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
		23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
		23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
		23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
		23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
		24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
		24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
		24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
		24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
};

/// Stackblur algorithm body
void stackblurJob(unsigned char* src,				///< input image data
	   			  unsigned int w,					///< image width
				  unsigned int h,					///< image height
				  unsigned int radius,				///< blur intensity (should be in 2..254 range)
				  int cores,						///< total number of working threads
				  int core,							///< current thread number
				  int step,							///< step of processing (1,2)
				  unsigned char* stack				///< stack buffer
				  )
{
	unsigned int x, y, xp, yp, i;
	unsigned int sp;
	unsigned int stack_start;
	unsigned char* stack_ptr;

	unsigned char* src_ptr;
	unsigned char* dst_ptr;

	unsigned long sum_r;
	unsigned long sum_g;
	unsigned long sum_b;
	unsigned long sum_a;
	unsigned long sum_in_r;
	unsigned long sum_in_g;
	unsigned long sum_in_b;
	unsigned long sum_in_a;
	unsigned long sum_out_r;
	unsigned long sum_out_g;
	unsigned long sum_out_b;
	unsigned long sum_out_a;

	unsigned int wm = w - 1;
	unsigned int hm = h - 1;
	unsigned int w4 = w * 4;
	unsigned int div = (radius * 2) + 1;
	unsigned int mul_sum = stackblur_mul[radius];
	unsigned char shr_sum = stackblur_shr[radius];

	if (step == 1)
	{
		int minY = core * h / cores;
		int maxY = (core + 1) * h / cores;

		for(y = minY; y < maxY; y++)
		{
			sum_r = sum_g = sum_b = sum_a =
			sum_in_r = sum_in_g = sum_in_b = sum_in_a =
			sum_out_r = sum_out_g = sum_out_b = sum_out_a = 0;

			src_ptr = src + w4 * y; // start of line (0,y)

			for(i = 0; i <= radius; i++)
			{
				stack_ptr    = &stack[ 4 * i ];
				stack_ptr[0] = src_ptr[0];
				stack_ptr[1] = src_ptr[1];
				stack_ptr[2] = src_ptr[2];
				stack_ptr[3] = src_ptr[3];
				sum_r += src_ptr[0] * (i + 1);
				sum_g += src_ptr[1] * (i + 1);
				sum_b += src_ptr[2] * (i + 1);
				sum_a += src_ptr[3] * (i + 1);
				sum_out_r += src_ptr[0];
				sum_out_g += src_ptr[1];
				sum_out_b += src_ptr[2];
				sum_out_a += src_ptr[3];
			}

			for(i = 1; i <= radius; i++)
			{
				if (i <= wm) src_ptr += 4;
				stack_ptr = &stack[ 4 * (i + radius) ];
				stack_ptr[0] = src_ptr[0];
				stack_ptr[1] = src_ptr[1];
				stack_ptr[2] = src_ptr[2];
				stack_ptr[3] = src_ptr[3];
				sum_r += src_ptr[0] * (radius + 1 - i);
				sum_g += src_ptr[1] * (radius + 1 - i);
				sum_b += src_ptr[2] * (radius + 1 - i);
				sum_a += src_ptr[3] * (radius + 1 - i);
				sum_in_r += src_ptr[0];
				sum_in_g += src_ptr[1];
				sum_in_b += src_ptr[2];
				sum_in_a += src_ptr[3];
			}

			sp = radius;
			xp = radius;
			if (xp > wm) xp = wm;
			src_ptr = src + 4 * (xp + y * w); //   img.pix_ptr(xp, y);
			dst_ptr = src + y * w4; // img.pix_ptr(0, y);
			for(x = 0; x < w; x++)
			{
				dst_ptr[0] = (sum_r * mul_sum) >> shr_sum;
				dst_ptr[1] = (sum_g * mul_sum) >> shr_sum;
				dst_ptr[2] = (sum_b * mul_sum) >> shr_sum;
				dst_ptr[3] = (sum_a * mul_sum) >> shr_sum;
				dst_ptr += 4;

				sum_r -= sum_out_r;
				sum_g -= sum_out_g;
				sum_b -= sum_out_b;
				sum_a -= sum_out_a;

				stack_start = sp + div - radius;
				if (stack_start >= div) stack_start -= div;
				stack_ptr = &stack[4 * stack_start];

				sum_out_r -= stack_ptr[0];
				sum_out_g -= stack_ptr[1];
				sum_out_b -= stack_ptr[2];
				sum_out_a -= stack_ptr[3];

				if(xp < wm)
				{
					src_ptr += 4;
					++xp;
				}

				stack_ptr[0] = src_ptr[0];
				stack_ptr[1] = src_ptr[1];
				stack_ptr[2] = src_ptr[2];
				stack_ptr[3] = src_ptr[3];

				sum_in_r += src_ptr[0];
				sum_in_g += src_ptr[1];
				sum_in_b += src_ptr[2];
				sum_in_a += src_ptr[3];
				sum_r    += sum_in_r;
				sum_g    += sum_in_g;
				sum_b    += sum_in_b;
				sum_a    += sum_in_a;

				++sp;
				if (sp >= div) sp = 0;
				stack_ptr = &stack[sp*4];

				sum_out_r += stack_ptr[0];
				sum_out_g += stack_ptr[1];
				sum_out_b += stack_ptr[2];
				sum_out_a += stack_ptr[3];
				sum_in_r  -= stack_ptr[0];
				sum_in_g  -= stack_ptr[1];
				sum_in_b  -= stack_ptr[2];
				sum_in_a  -= stack_ptr[3];

			}

		}
	}

	// step 2
	if (step == 2)
	{
		int minX = core * w / cores;
		int maxX = (core + 1) * w / cores;

		for(x = minX; x < maxX; x++)
		{
			sum_r =	sum_g =	sum_b =	sum_a =
			sum_in_r = sum_in_g = sum_in_b = sum_in_a =
			sum_out_r = sum_out_g = sum_out_b = sum_out_a = 0;

			src_ptr = src + 4 * x; // x,0
			for(i = 0; i <= radius; i++)
			{
				stack_ptr    = &stack[i * 4];
				stack_ptr[0] = src_ptr[0];
				stack_ptr[1] = src_ptr[1];
				stack_ptr[2] = src_ptr[2];
				stack_ptr[3] = src_ptr[3];
				sum_r           += src_ptr[0] * (i + 1);
				sum_g           += src_ptr[1] * (i + 1);
				sum_b           += src_ptr[2] * (i + 1);
				sum_a           += src_ptr[3] * (i + 1);
				sum_out_r       += src_ptr[0];
				sum_out_g       += src_ptr[1];
				sum_out_b       += src_ptr[2];
				sum_out_a       += src_ptr[3];
			}
			for(i = 1; i <= radius; i++)
			{
				if(i <= hm) src_ptr += w4; // +stride

				stack_ptr = &stack[4 * (i + radius)];
				stack_ptr[0] = src_ptr[0];
				stack_ptr[1] = src_ptr[1];
				stack_ptr[2] = src_ptr[2];
				stack_ptr[3] = src_ptr[3];
				sum_r += src_ptr[0] * (radius + 1 - i);
				sum_g += src_ptr[1] * (radius + 1 - i);
				sum_b += src_ptr[2] * (radius + 1 - i);
				sum_a += src_ptr[3] * (radius + 1 - i);
				sum_in_r += src_ptr[0];
				sum_in_g += src_ptr[1];
				sum_in_b += src_ptr[2];
				sum_in_a += src_ptr[3];
			}

			sp = radius;
			yp = radius;
			if (yp > hm) yp = hm;
			src_ptr = src + 4 * (x + yp * w); // img.pix_ptr(x, yp);
			dst_ptr = src + 4 * x; 			  // img.pix_ptr(x, 0);
			for(y = 0; y < h; y++)
			{
				dst_ptr[0] = (sum_r * mul_sum) >> shr_sum;
				dst_ptr[1] = (sum_g * mul_sum) >> shr_sum;
				dst_ptr[2] = (sum_b * mul_sum) >> shr_sum;
				dst_ptr[3] = (sum_a * mul_sum) >> shr_sum;
				dst_ptr += w4;

				sum_r -= sum_out_r;
				sum_g -= sum_out_g;
				sum_b -= sum_out_b;
				sum_a -= sum_out_a;

				stack_start = sp + div - radius;
				if(stack_start >= div) stack_start -= div;
				stack_ptr = &stack[4 * stack_start];

				sum_out_r -= stack_ptr[0];
				sum_out_g -= stack_ptr[1];
				sum_out_b -= stack_ptr[2];
				sum_out_a -= stack_ptr[3];

				if(yp < hm)
				{
					src_ptr += w4; // stride
					++yp;
				}

				stack_ptr[0] = src_ptr[0];
				stack_ptr[1] = src_ptr[1];
				stack_ptr[2] = src_ptr[2];
				stack_ptr[3] = src_ptr[3];

				sum_in_r += src_ptr[0];
				sum_in_g += src_ptr[1];
				sum_in_b += src_ptr[2];
				sum_in_a += src_ptr[3];
				sum_r    += sum_in_r;
				sum_g    += sum_in_g;
				sum_b    += sum_in_b;
				sum_a    += sum_in_a;

				++sp;
				if (sp >= div) sp = 0;
				stack_ptr = &stack[sp*4];

				sum_out_r += stack_ptr[0];
				sum_out_g += stack_ptr[1];
				sum_out_b += stack_ptr[2];
				sum_out_a += stack_ptr[3];
				sum_in_r  -= stack_ptr[0];
				sum_in_g  -= stack_ptr[1];
				sum_in_b  -= stack_ptr[2];
				sum_in_a  -= stack_ptr[3];
			}
		}
	}

}

class MVImageUtilsStackBlurTask : public MVThread
{
public:
	unsigned char* src;
	unsigned int w;
	unsigned int h;
	unsigned int radius;
	int cores;
	int core;
	int step;
	unsigned char* stack;

	inline MVImageUtilsStackBlurTask(unsigned char* src, unsigned int w, unsigned int h, unsigned int radius, int cores, int core, int step, unsigned char* stack)
	{
		this->src = src;
		this->w = w;
		this->h = h;
		this->radius = radius;
		this->cores = cores;
		this->core = core;
		this->step = step;
		this->stack = stack;
	}

	inline void run()
	{
		stackblurJob(src, w, h, radius, cores, core, step, stack);
	}

};

/// Stackblur algorithm by Mario Klingemann
/// Details here:
/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
/// C++ implemenation base from:
/// https://gist.github.com/benjamin9999/3809142
/// http://www.antigrain.com/__code/include/agg_blur.h.html
/// This version works only with RGBA color
void 			   stackblur(unsigned char* src,				///< input image data
	   					     unsigned int w,					///< image width
							 unsigned int h,					///< image height
							 unsigned int radius,				///< blur intensity (should be in 2..254 range)
							 int cores = 1						///< number of threads (1 - normal single thread)
							 )
{
	if (radius > 254) return;
	if (radius < 2) return;

	unsigned int div = (radius * 2) + 1;
	unsigned char* stack = new unsigned char [div * 4 * cores];

	if (cores == 1)
	{
		// no multithreading
		stackblurJob(src, w, h, radius, 1, 0, 1, stack);
		stackblurJob(src, w, h, radius, 1, 0, 2, stack);
	}
	else
	{
		MVImageUtilsStackBlurTask** workers = new MVImageUtilsStackBlurTask*[cores];
		for (int i = 0; i < cores; i++)
		{
			workers[i] = new MVImageUtilsStackBlurTask(src, w, h, radius, cores, i, 1, stack + div * 4 * i);
			workers[i]->start();
		}

		for (int i = 0; i < cores; i++)
			workers[i]->wait();

		for (int i = 0; i < cores; i++)
		{
			workers[i]->step = 2;
			workers[i]->start();
		}

		for (int i = 0; i < cores; i++)
		{
			workers[i]->wait();
			delete workers[i];
		}

		delete[] workers;
	}

	delete[] stack;
}

这个是C++的代码。

在网上发现了一个开源的 Android 模糊的例子,其中有该代码的 Java 版本。

JavaBlurProcess.java

import android.graphics.Bitmap;

import java.util.ArrayList;
import java.util.concurrent.Callable;

class JavaBlurProcess implements BlurProcess {

	private static final short[] stackblur_mul = {
			512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
			454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
			482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
			437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
			497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
			320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
			446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
			329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
			505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
			399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
			324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
			268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
			451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
			385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
			332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
			289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
	};

	private static final byte[] stackblur_shr = {
			9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
			17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
			19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
			20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
			21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
			21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
			22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
			22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
			23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
			23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
			23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
			23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
			24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
			24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
			24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
			24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
	};

	@Override
	public Bitmap blur(Bitmap original, float radius) {
		int w = original.getWidth();
		int h = original.getHeight();
		int[] currentPixels = new int[w * h];
		original.getPixels(currentPixels, 0, w, 0, 0, w, h);
		int cores = StackBlurManager.EXECUTOR_THREADS;

		ArrayList<BlurTask> horizontal = new ArrayList<BlurTask>(cores);
		ArrayList<BlurTask> vertical = new ArrayList<BlurTask>(cores);
		for (int i = 0; i < cores; i++) {
			horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1));
			vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2));
		}

		try {
			StackBlurManager.EXECUTOR.invokeAll(horizontal);
		} catch (InterruptedException e) {
			return null;
		}

		try {
			StackBlurManager.EXECUTOR.invokeAll(vertical);
		} catch (InterruptedException e) {
			return null;
		}

		return Bitmap.createBitmap(currentPixels, w, h, Bitmap.Config.ARGB_8888);
	}

	private static void blurIteration(int[] src, int w, int h, int radius, int cores, int core, int step) {
		int x, y, xp, yp, i;
		int sp;
		int stack_start;
		int stack_i;

		int src_i;
		int dst_i;

		long sum_r, sum_g, sum_b,
				sum_in_r, sum_in_g, sum_in_b,
				sum_out_r, sum_out_g, sum_out_b;

		int wm = w - 1;
		int hm = h - 1;
		int div = (radius * 2) + 1;
		int mul_sum = stackblur_mul[radius];
		byte shr_sum = stackblur_shr[radius];
		int[] stack = new int[div];

		if (step == 1)
		{
			int minY = core * h / cores;
			int maxY = (core + 1) * h / cores;

			for(y = minY; y < maxY; y++)
			{
				sum_r = sum_g = sum_b =
				sum_in_r = sum_in_g = sum_in_b =
				sum_out_r = sum_out_g = sum_out_b = 0;

				src_i = w * y; // start of line (0,y)

				for(i = 0; i <= radius; i++)
				{
					stack_i    = i;
					stack[stack_i] = src[src_i];
					sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1);
					sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1);
					sum_b += (src[src_i] & 0xff) * (i + 1);
					sum_out_r += ((src[src_i] >>> 16) & 0xff);
					sum_out_g += ((src[src_i] >>> 8) & 0xff);
					sum_out_b += (src[src_i] & 0xff);
				}

				for(i = 1; i <= radius; i++)
				{
					if (i <= wm) src_i += 1;
					stack_i = i + radius;
					stack[stack_i] = src[src_i];
					sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i);
					sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i);
					sum_b += (src[src_i] & 0xff) * (radius + 1 - i);
					sum_in_r += ((src[src_i] >>> 16) & 0xff);
					sum_in_g += ((src[src_i] >>> 8) & 0xff);
					sum_in_b += (src[src_i] & 0xff);
				}

				sp = radius;
				xp = radius;
				if (xp > wm) xp = wm;
				src_i = xp + y * w; //   img.pix_ptr(xp, y);
				dst_i = y * w; // img.pix_ptr(0, y);
				for(x = 0; x < w; x++)
				{
					src[dst_i] = (int)
								((src[dst_i] & 0xff000000) |
								((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) |
								((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) |
								((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
					dst_i += 1;

					sum_r -= sum_out_r;
					sum_g -= sum_out_g;
					sum_b -= sum_out_b;

					stack_start = sp + div - radius;
					if (stack_start >= div) stack_start -= div;
					stack_i = stack_start;

					sum_out_r -= ((stack[stack_i] >>> 16) & 0xff);
					sum_out_g -= ((stack[stack_i] >>> 8) & 0xff);
					sum_out_b -= (stack[stack_i] & 0xff);

					if(xp < wm)
					{
						src_i += 1;
						++xp;
					}

					stack[stack_i] = src[src_i];

					sum_in_r += ((src[src_i] >>> 16) & 0xff);
					sum_in_g += ((src[src_i] >>> 8) & 0xff);
					sum_in_b += (src[src_i] & 0xff);
					sum_r    += sum_in_r;
					sum_g    += sum_in_g;
					sum_b    += sum_in_b;

					++sp;
					if (sp >= div) sp = 0;
					stack_i = sp;

					sum_out_r += ((stack[stack_i] >>> 16) & 0xff);
					sum_out_g += ((stack[stack_i] >>> 8) & 0xff);
					sum_out_b += (stack[stack_i] & 0xff);
					sum_in_r  -= ((stack[stack_i] >>> 16) & 0xff);
					sum_in_g  -= ((stack[stack_i] >>> 8) & 0xff);
					sum_in_b  -= (stack[stack_i] & 0xff);
				}

			}
		}

		// step 2
		else if (step == 2)
		{
			int minX = core * w / cores;
			int maxX = (core + 1) * w / cores;

			for(x = minX; x < maxX; x++)
			{
				sum_r =    sum_g =    sum_b =
				sum_in_r = sum_in_g = sum_in_b =
				sum_out_r = sum_out_g = sum_out_b = 0;

				src_i = x; // x,0
				for(i = 0; i <= radius; i++)
				{
					stack_i    = i;
					stack[stack_i] = src[src_i];
					sum_r           += ((src[src_i] >>> 16) & 0xff) * (i + 1);
					sum_g           += ((src[src_i] >>> 8) & 0xff) * (i + 1);
					sum_b           += (src[src_i] & 0xff) * (i + 1);
					sum_out_r       += ((src[src_i] >>> 16) & 0xff);
					sum_out_g       += ((src[src_i] >>> 8) & 0xff);
					sum_out_b       += (src[src_i] & 0xff);
				}
				for(i = 1; i <= radius; i++)
				{
					if(i <= hm) src_i += w; // +stride

					stack_i = i + radius;
					stack[stack_i] = src[src_i];
					sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i);
					sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i);
					sum_b += (src[src_i] & 0xff) * (radius + 1 - i);
					sum_in_r += ((src[src_i] >>> 16) & 0xff);
					sum_in_g += ((src[src_i] >>> 8) & 0xff);
					sum_in_b += (src[src_i] & 0xff);
				}

				sp = radius;
				yp = radius;
				if (yp > hm) yp = hm;
				src_i = x + yp * w; // img.pix_ptr(x, yp);
				dst_i = x;               // img.pix_ptr(x, 0);
				for(y = 0; y < h; y++)
				{
					src[dst_i] = (int)
							((src[dst_i] & 0xff000000) |
							((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) |
							((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) |
							((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
					dst_i += w;

					sum_r -= sum_out_r;
					sum_g -= sum_out_g;
					sum_b -= sum_out_b;

					stack_start = sp + div - radius;
					if(stack_start >= div) stack_start -= div;
					stack_i = stack_start;

					sum_out_r -= ((stack[stack_i] >>> 16) & 0xff);
					sum_out_g -= ((stack[stack_i] >>> 8) & 0xff);
					sum_out_b -= (stack[stack_i] & 0xff);

					if(yp < hm)
					{
						src_i += w; // stride
						++yp;
					}

					stack[stack_i] = src[src_i];

					sum_in_r += ((src[src_i] >>> 16) & 0xff);
					sum_in_g += ((src[src_i] >>> 8) & 0xff);
					sum_in_b += (src[src_i] & 0xff);
					sum_r    += sum_in_r;
					sum_g    += sum_in_g;
					sum_b    += sum_in_b;

					++sp;
					if (sp >= div) sp = 0;
					stack_i = sp;

					sum_out_r += ((stack[stack_i] >>> 16) & 0xff);
					sum_out_g += ((stack[stack_i] >>> 8) & 0xff);
					sum_out_b += (stack[stack_i] & 0xff);
					sum_in_r  -= ((stack[stack_i] >>> 16) & 0xff);
					sum_in_g  -= ((stack[stack_i] >>> 8) & 0xff);
					sum_in_b  -= (stack[stack_i] & 0xff);
				}
			}
		}

	}

	private static class BlurTask implements Callable<Void> {
		private final int[] _src;
		private final int _w;
		private final int _h;
		private final int _radius;
		private final int _totalCores;
		private final int _coreIndex;
		private final int _round;

		public BlurTask(int[] src, int w, int h, int radius, int totalCores, int coreIndex, int round) {
			_src = src;
			_w = w;
			_h = h;
			_radius = radius;
			_totalCores = totalCores;
			_coreIndex = coreIndex;
			_round = round;
		}

		@Override public Void call() throws Exception {
			blurIteration(_src, _w, _h, _radius, _totalCores, _coreIndex, _round);
			return null;
		}

	}
}

该代码与C++的代码同出一源。下面来简要分析一下。

分析

入口:

	public Bitmap blur(Bitmap original, float radius) {
		int w = original.getWidth();
		int h = original.getHeight();
		int[] currentPixels = new int[w * h];
		original.getPixels(currentPixels, 0, w, 0, 0, w, h);
		int cores = StackBlurManager.EXECUTOR_THREADS;

		ArrayList<BlurTask> horizontal = new ArrayList<BlurTask>(cores);
		ArrayList<BlurTask> vertical = new ArrayList<BlurTask>(cores);
		for (int i = 0; i < cores; i++) {
			horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1));
			vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2));
		}

		try {
			StackBlurManager.EXECUTOR.invokeAll(horizontal);
		} catch (InterruptedException e) {
			return null;
		}

		try {
			StackBlurManager.EXECUTOR.invokeAll(vertical);
		} catch (InterruptedException e) {
			return null;
		}

		return Bitmap.createBitmap(currentPixels, w, h, Bitmap.Config.ARGB_8888);
	}

方法进入后,首先得到图片的像素点集合;而后 New 两个 List
,分别存储 BlurTask

看看 BlurTask :

	private static class BlurTask implements Callable<Void> {
		private final int[] _src;
		private final int _w;
		private final int _h;
		private final int _radius;
		private final int _totalCores;
		private final int _coreIndex;
		private final int _round;

		public BlurTask(int[] src, int w, int h, int radius, int totalCores, int coreIndex, int round) {
			_src = src;
			_w = w;
			_h = h;
			_radius = radius;
			_totalCores = totalCores;
			_coreIndex = coreIndex;
			_round = round;
		}

		@Override public Void call() throws Exception {
			blurIteration(_src, _w, _h, _radius, _totalCores, _coreIndex, _round);
			return null;
		}

	}

可以看出 BlurTask 是用于进行线程池的回调类;其主要调用了方法 blurIteration
进行模糊。

同时给方法,传入的参数有:

  • 像素点集合
  • 模糊半径
  • 最大内核数
  • 当前内核
步骤

而后回到第一步处:

int cores = StackBlurManager.EXECUTOR_THREADS;

其实是调用的:

Runtime.getRuntime().availableProcessors();

该方法用于返回当前虚拟机的核数;用此方法可以最大效率的进行多线程的分配。

而后:

		for (int i = 0; i < cores; i++) {
			horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1));
			vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2));
		}

可以看出往列表中加上,对应的线程回调操作;分别是步骤1,和步骤2

而后:

		try {
			StackBlurManager.EXECUTOR.invokeAll(horizontal);
		} catch (InterruptedException e) {
			return null;
		}

		try {
			StackBlurManager.EXECUTOR.invokeAll(vertical);
		} catch (InterruptedException e) {
			return null;
		}

该方法中:StackBlurManager.EXECUTOR 其实是:

Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

可以看出其实是建立了一个线程池,线程池的运行线程数为当前虚拟机的核数。

然后把步骤1的集合丢进线程池并等待执行完成;而后丢进步骤2.

其实从其命名就能看出:

  • 步骤1其实是模糊水平方向
  • 步骤2其实是模糊垂直方向

具体是不是可以看看 blurIteration
方法。

由于方法太长就简化了一下:

	private static void blurIteration(int[] src, int w, int h, int radius, int cores, int core, int step) {
        ...

		if (step == 1)
		{
			int minY = core * h / cores;
			int maxY = (core + 1) * h / cores;

			for(y = minY; y < maxY; y++)
			{
				...
			}
		}

		// step 2
		else if (step == 2)
		{
			int minX = core * w / cores;
			int maxX = (core + 1) * w / cores;

			for(x = minX; x < maxX; x++)
                <span style="white-space:pre">	</span>{
                <span style="white-space:pre">		</span>...
                <span style="white-space:pre">	</span>}
		}

	}

可以看出分别进行了模糊水平与垂直。

同时,在模糊时:分为了许多部分,具体取决于虚拟核数。

在这里有必要说明一点就是;在模糊时只能先模糊第一步,而后才能模糊第二部。

意思就是说第一步与第二步是不能同时进行的;如果不赞同;可以啊把上面的两个列表改成一个同时进行多线程你就知道模糊的效果了。

测试

这里截图就不进行截图了,不过根据我的测试;该模糊方法在多线程下优势较为明显;也就是说手机核数越高其模糊速度越快。

如果该方法顺序执行,不使用多线程;那么其模糊所耗费时间约比我上一章中介绍的方法多1/4的时间。

下面其实,该开源项目做的很完善,同时包含了 Java,JNI C,RenderScript  三种方式进行模糊:

由于代码较多就不一一贴出来了。我打包上传了;需要的朋友可以自行下载。

源码

[Android] 图片JNI(C++\Java)高斯模糊 多线程 源码

时间: 2024-11-03 20:22:08

[Android] 图片JNI(C++\Java)高斯模糊 多线程的相关文章

Android与JNI(二) ---- Java调用C 动态调用(转载)

目录: 1. 简介 2. JNI 组件的入口函数 3. 使用 registerNativeMethods 方法 4. 测试 5. JNI 帮助方法 6. 参考资料 1. 简介 目录: 1. 简介 2. JNI 组件的入口函数 3. 使用 registerNativeMethods 方法 4. 测试 5. JNI 帮助方法 6. 参考资料 1. 简介 Android与JNI(一)已经简单介绍了如何在 android  环境下使用 JNI 了.但是遵循 JNI 开发的基本步骤似乎有点死板,而且得到的

【我的Android进阶之旅】Android调用JNI出错 java.lang.UnsatisfiedLinkError: No implementation found for的解决方法

错误描述 今天使用第三方的so库时候,调用JNI方法时出现了错误.报错如下所示: 11-01 16:39:20.979 4669-4669/com.netease.xtc.cloudmusic E/art: No implementation found for void com.netease.xtc.cloudmusic.utils.NeteaseMusicUtils.nativeInit(android.content.Context) (tried Java_com_netease_xt

[Android]-图片JNI(C++\Java)高斯模糊的实现与比較

前几天一直在弄android上的图片模糊效果的实现! 一直找不到方法,看别人说都是调用JNI,可是JNI这个东西我还真不熟悉啊! 仅仅好从零開始了!这里不讲JNI的平台搭建,仅仅讲JNI的关键代码,详细的项目我会共享出来给大家! 对于JNI下使用C++来模糊图片这个我真的没找到,仅仅好自己写C++的来实现了. 在国外的一个项目中找到了一个"堆栈模糊效果",原型例如以下: // Stack Blur v1.0 // // Author: Mario Klingemann <[ema

Android使用JNI实现Java与C之间传递数据(转)

介绍Java如何将数据传递给C和C回调Java的方法.  java传递数据给C,在C代码中进行处理数据,处理完数据后返回给java.C的回调是Java传递数据给C,C需要用到Java中的某个方法,就需要调用java的方法. Android中使用JNI七个步骤: 1.创建一个android工程 2.JAVA代码中写声明native 方法 public native String helloFromJNI(); 3.用javah工具生成头文件 4. 创建jni目录,引入头文件,根据头文件实现c代码

coco2dx jni 调用 java 相机返回 图片数据

新建 一个项目 名字:testJin  包名:com.TanSon.org  python命令:python create_project.py -project testJin -package com.TanSon.org -language cpp eclipse 导入项目配置 ... 略去,(可以google) 1 c++ 调用 andriod 1.1 包含头文件 #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include <jni.

Android使用JNI(从java调用本地函数)

当编写一个混合有本地C代码和Java的应用程序时,需要使用Java本地接口(JNI)作为连接桥梁.JNI作为一个软件层和API,允许使用本地代码调用Java对象的方法,同时也允许在Java方法中调用本地函数. 在Java端,开发者所需要做的仅仅是在连接本地函数的方法之前加上native关键字.这样VM就会去寻找这个本地函数. 1.从Java调用本地函数 从Java调用本地函数时,需要在类中定义一个带有native关键字的特有方法,作为连接本地代码的桥梁.通过这个定义,尝试调用本地方法时JVM会找

Android JNI之JAVA与C++对象建立对称关联(JNI优化设计,确保JNI调用的稳定性)

转载请声明:原文转自:http://www.cnblogs.com/xiezie/p/5930503.html Android JNI之JAVA与C++对象建立对称关联 1.JAVA对象持有C++对象的指针 在JAVA类中创建一个int类型的变量(如int mObj),用于储存C++对象的指针 在创建C++对象(如MyCPlusObj)的本地方法中,将C++对象的指针存入JAVA变量中(mObj) JNIEXPORT void JNICALL Java_nativeMethod (JNIEnv

Android NDK开发之Jni调用Java对象

https://my.oschina.net/zhiweiofli/blog/114064 通过使用合适的JNI函数,你可以创建Java对象,get.set 静态(static)和 实例(instance)的域,调用静态(static)和实例(instance)函数.JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数.下表列出了用以得到静态(static)和实例(instance)的域与方法的JNI函数.每个函数接受(作为参数)域或方法的类,它们的名称,符号和它们对

Android开发实践:Java层与Jni层的数组传递

Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni层也需要把从Socket接收到的数据流返回给Java层.我简单地总结了一下,从Java层到Jni层,从Jni层到JAVA层,各有3种传递方式,下面用代码示例简单地介绍一下. 示例代码的主要文件有两个,一个是Native.java,是Java层的类:另一个是Native.c,是JNI层的文件,关键的地