学习Graphics中遇到位图(Bitmap)中getPixels()方法,对该方法的用法大体理解,但对其中的stride参数却不明白具体的用法以及用意,现记述过程如下:
Android英文SDK中有关getPixels()方法的介绍如下:
public void getPixels (int[] pixels, int offset, int stride, int x, int y, int width, int height)
Returns in pixels[] a copy of the data in the bitmap. Each value is a packed int representing a Color
. The stride parameter allows the caller to allow for gaps in the returned pixels array between rows. For normal packed results, just pass width for the stride value.
Parameters
pixels | The array to receive the bitmap's colors |
---|---|
offset | The first index to write into pixels[] |
stride | The number of entries in pixels[] to skip between rows (must be >= bitmap's width). Can be negative. |
x | The x coordinate of the first pixel to read from the bitmap |
y | The y coordinate of the first pixel to read from the bitmap |
width | The number of pixels to read from each row |
height | The number of rows to read |
Throws
IllegalArgumentException | if x, y, width, height exceed the bounds of the bitmap, or if abs(stride) < width. |
---|---|
ArrayIndexOutOfBoundsException | if the pixels array is too small to receive the specified number of pixels. |
看完英文文档仍然不甚明白,于是去搜了下中文Android文档相应内容, getPixels()
public void getPixels (int[] pixels, int offset, int stride, int x, int y, int width, int height)
把位图的数据拷贝到pixels[]中。每一个都由一个表示颜色值的int值来表示。幅度参数(stride)表明调用者允许的像素数组行间距。对通常的填充结果,只要传递宽度值给幅度参数。
参数
pixels 接收位图颜色值的数组
offset 写入到pixels[]中的第一个像素索引值
stride pixels[]中的行间距个数值(必须大于等于位图宽度)。可以为负数
x 从位图中读取的第一个像素的x坐标值。
y 从位图中读取的第一个像素的y坐标值
width 从每一行中读取的像素宽度
height 读取的行数
异常
IllegalArgumentExcepiton 如果x,y,width,height越界或stride的绝对值小于位图宽度时将被抛出。
ArrayIndexOutOfBoundsException 如果像素数组太小而无法接收指定书目的像素值时将被抛出。
看完后仍然对Stride解释中的"行间距"不太明白,去查了下Stride在英语中的原义,Stride在柯林斯中的英英释义如下:
1 If you stride somewhere, you walk there with quick, long steps.
stride意为"大踏步快速前进"
2 A stride is a long step which you take when you are walking or running.
stride在此做名词,意为"大步"
3 Someone's stride is their way of walking with long steps.
指代某人具体迈大步的方式.
于是可以把stride理解为人行走过程中所迈大步的一段距离,而在此方法中可以理解为每行的像素数,至于用处是什么,还要继续寻找答案.
然后去StackOverFlow去搜了搜"getPixels() stride"关键字,查找到如下信息:
1 In most cases the stride is the same as the width. The stride is useful if you are trying to copy/draw a sub-region of a Bitmap. For instance, if you have a 100x100 bitmap and you want to draw the 50x50 top-right corner, you can use a width of 50px and a stride of 100px.(注:stride绝对值要大于等于位图的宽度)
2 Stride is number of bytes used for storing one image row.
Most of the images are 4 byte aligned.
So you will see stride as 154, width 50 and image alignment as 4 byte.
上面内容表示stride参数有两种用处
第一种:
可以截取图片中部分区域或者图片拼接.
截图:假设读取像素值的原图片宽为w,高为h,此时设置参数pixels[w*h], 参数stride为 w ,参数offset为0,参数x ,y为截图的起点位置,参数width和height为截图的宽度和高度,则此方法运行后,返回的pixels[]数组中从pixels[0]至pixels[width*height-1]里存储的是从图片( x , y )处起读取的截图大小为width * height的像素值.
示例:修改Android SDK自带的AipDemo程序中BitmapDecode示例,更换图像为自制四角四色图:
图像大小为100*100,想截取图片右上1/4图像(图上黄色部分)修改程序部分代码为:
- int[] pixels = new int[w*h];
- mBitmap2.getPixels(pixels, 0, w, 50, 0, w/2, h/2);
- mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_8888);
- mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_4444);
- String txt = String.valueOf(pixels[10]);
- Log.i("myBitmapDecode", "w = " + w + "; h = " + h);
- Log.i("myBitmapDecode", "pixels[0] = " + pixels[0] + "; pixels[1] = " + pixels[1] + "; pixels[10] = " + pixels[10]);
- Log.i("myBitmapDecode", "pixels[w] = " + pixels[w] + "; pixels[h] = " + pixels[h] + "; pixels[w*h-1] = " + pixels[w*h-1]);
运行结果:
I/myBitmapDecode( 660): w = 100; h = 100
I/myBitmapDecode( 660): pixels[0]-16777216; pixels[1] = -16777216; pixels[10] = -4352
I/myBitmapDecode( 660): pixels[w]-16777216; pixels[h] = -16777216; pixels[w*h-1] = 0
我们看到右边两副ARGB_8888,ARGB_4444图像隐约只在左上角显示原图右上的1/4黄色部分,其余部分为背景色白色,那么问题又来了,此时ARGB_8888,ARGB_4444图像大小为多少?还是原图的大小(100*100)吗,或者是(50*50)了,不然背景色为何是画布的背景色呢(白色)?那么把 pixels[100*100]数组设初始值看下情况(通过Log.i()我查到了pixels中存储的像素值为百万左右的负整数(-16777216),所以这里胡乱取个数-2578654做为初始值,颜色不太好,请见谅),修改后代码如下:
- int[] pixels = new int[w*h];
- for(int i=0; i<w*h; i++){
- pixels[i] = -2578654;
- }
- mBitmap2.getPixels(pixels, 0, w, 50, 0, w/2, h/2);
- mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_8888);
- mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_4444);
- String txt = String.valueOf(pixels[10]);
- Log.i("myBitmapDecode", "w = " + w + "; h = " + h);
- Log.i("myBitmapDecode", "pixels[0] = " + pixels[0] + "; pixels[1] = " + pixels[1] + "; pixels[10] = " + pixels[10]);
- Log.i("myBitmapDecode", "pixels[w] = " + pixels[w] + "; pixels[h] = " + pixels[h] + "; pixels[w*h-1] = " + pixels[w*h-1]);
运行结果:
I/myBitmapDecode( 727): pixels[0] = -16777216; pixels[1] = -16777216; pixels[10] = -4352
I/myBitmapDecode( 727): pixels[w] = -16777216; pixels[h] = -16777216; pixels[w*h-1] = -2578654
我们可以看到结果了,如果pixels[]中的数值为int默认值(0)的话,图片相应的部分就为背景色,如果设置为别的初始值而在运行中没有被修改的话,背景色就是修改值对应的RGB颜色.
原图位置(offset)
下面设置下getPixels[]方法中offset,使得黄色部分截图出现在它在原图中的位置,
offset = x + y*w ,本例代码如下:
- int[] pixels = new int[w*h];
- for(int i=0; i<w*h; i++){
- pixels[i] = -2578654;
- }
- mBitmap2.getPixels(pixels, 50, w, 50, 0, w/2, h/2;
- mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_8888);
- mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_4444);
- String txt = String.valueOf(pixels[10]);
- Log.i("myBitmapDecode", "w = " + w + "; h = " + h);
- Log.i("myBitmapDecode", "pixels[0] = " + pixels[0] + "; pixels[1] = " + pixels[1] + "; pixels[10] = " + pixels[10]);
- Log.i("myBitmapDecode", "pixels[w] = " + pixels[w] + "; pixels[h] = " + pixels[h] + "; pixels[w*h-1] = " + pixels[w*h-1]);
运行结果:
I/myBitmapDecode( 761): w = 100; h = 100
I/myBitmapDecode( 761): pixels[0] = -2578654; pixels[1] = -2578654; pixels[10] = -2578654
I/myBitmapDecode( 761): pixels[w] = -2578654; pixels[h] = -2578654; pixels[w*h-1] = -2578654
当然可以用这个方法进行更复杂的运算,诸如截取素材图片修改目标图片(已存储至pixels数组中)的指定区域!!
背景色设置(pixels[])
背景颜色与pixels[]初始值一致,如红色RED(-65536 0xffff0000),黄色YELLOW(-256 0xffffff00),具体详见下面附注
- int[] pixels = new int[w*h];
- for(int i=0; i<w*h; i++){
- pixels[i] = -65536; // Color.RED : -65536 (0xffff0000)
- }
- mBitmap2.getPixels(pixels, 50, w, 50, 0, w/2, h/2);
- mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_8888);
- Log.i("myBitmapDecode", "w = " + w + "; h = " + h);
- Log.i("myBitmapDecode", "pixels[0] = " + pixels[0] + "; pixels[1] = " + pixels[1] + "; pixels[10] = " + pixels[10] + "; pixels[50] = " + pixels[50]);
- Log.i("myBitmapDecode", "pixels[w] = " + pixels[w] + "; pixels[h] = " + pixels[h] + "; pixels[w*h-1] = " + pixels[w*h-1]);
- for(int i=0; i<w*h; i++){
- pixels[i] = -256; // Color.YELLOW : -256 (0xffffff00)
- }
- mBitmap2.getPixels(pixels, 50*100 + 50, w, 50, 50, w/2, h/2);
- mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h, Bitmap.Config.ARGB_4444);
- Log.i("myBitmapDecode", "w = " + w + "; h = " + h);
- Log.i("myBitmapDecode", "pixels[0] = " + pixels[0] + "; pixels[1] = " + pixels[1] + "; pixels[10] = " + pixels[10] + "; pixels[50] = " + pixels[50]);
- Log.i("myBitmapDecode", "pixels[w] = " + pixels[w] + "; pixels[h] = " + pixels[h] + "; pixels[w*h-1] = " + pixels[w*h-1]);
运行结果:
I/myBitmapDecode( 1671): w = 100; h = 100
I/myBitmapDecode( 1671): pixels[0] = -65536; pixels[1] = -65536; pixels[10] = -65536; pixels[50] = -16777216
I/myBitmapDecode( 1671): pixels[w] = -65536; pixels[h] = -65536; pixels[w*h-1] = -65536
I/myBitmapDecode( 1671): w = 100; h = 100
I/myBitmapDecode( 1671): pixels[0] = -256; pixels[1] = -256; pixels[10] = -256; pixels[50] = -256
I/myBitmapDecode( 1671): pixels[w] = -256; pixels[h] = -256; pixels[w*h-1] = -16735513
图片拼接:
假设两张图片大小都为 w * h ,getPixels()方法中设置参数pixels[2*w*h],参数offset = 0,stride = 2*w读取第一张图片,再次运行getPixels()方法,设置参数offset = w,stride = 2*w,读取第二张图片,再将pixels[]绘制到画布上就可以看到两张图片已经拼接起来了.
示例如下:
- int w = mBitmap2.getWidth();
- int h = mBitmap2.getHeight();
- int[] pixels = new int[2*w*h];
- for(int i=0; i<2*w*h; i++){
- pixels[i] = -2578654;
- }
- mBitmap2.getPixels(pixels, 0, 2*w, 0, 0, w, h);
- mBitmap2.getPixels(pixels, w, 2*w, 0, 0, w, h);
- mBitmap3 = Bitmap.createBitmap(pixels, 0, 2*w, 2*w, h, Bitmap.Config.ARGB_8888);
- String txt = String.valueOf(pixels[10]);
- Log.i("myBitmapDecode", "w = " + w + "; h = " + h);
- Log.i("myBitmapDecode", "pixels[0] = " + pixels[0] + "; pixels[1] = " + pixels[1] + "; pixels[10] = " + pixels[10]);
- Log.i("myBitmapDecode", "pixels[w] = " + pixels[w] + "; pixels[h] = " + pixels[h] + "; pixels[w*h-1] = " + pixels[w*h-1]);
- Log.i("myBitmapDecode", "pixels[2*w-1] = " + pixels[2*w-1] + "; pixels[2*w] = " + pixels[2*w] + "; pixels[2*w*h-1] = " + pixels[2*w*h-1]);
运行结果:
I/myBitmapDecode( 989): w = 100; h = 100
I/myBitmapDecode( 989): pixels[0] = -16777216; pixels[1] = -16777216; pixels[10] = -16777216
I/myBitmapDecode( 989): pixels[w] = -16777216; pixels[h] = -16777216; pixels[w*h-1] = -16777216
I/myBitmapDecode( 989): pixels[2*w-1] = -3328; pixels[2*w] = -16777216; pixels[2*w*h-1] = -16735513
第二种:
stride表示数组pixels[]中存储的图片每行的数据,在其中可以附加信息,即
stride = width + padding,如下图所示
这样可以不仅仅存储图片的像素信息,也可以储存相应每行的其它附加信息.
最后,stride参数的意义及用处总结如下:
1 用来表示pixels[]数组中每行的像素个数,用于行与行之间区分,绝对值必须大于参数width,但不必大于所要读取图片的宽度w(在width < w 时成立).(stride负数有何作用不知,存疑).另,pixels.length >= stride * height,否则会抛出ArrayIndexOutOfBoundsException异常
2 stride > width时,可以在pixels[]数组中添加每行的附加信息,可做它用.
附注(Color颜色对应值):