Bitmap 之 getPixels() 的 stride

学习Graphics中遇到位图(Bitmap)中getPixels()方法,对该方法的用法大体理解,但对其中的stride参数却不明白具体的用法以及用意,现记述过程如下:

getPixels()方法的用处为获取位图(Bitmap)中的像素值(颜色值),存入类型为int的pixels数组中,至于从RGB转换为int数值的算法是什么,暂时不知,存疑!! 

Android英文SDK中有关getPixels()方法的介绍如下



public void getPixels (int[] pixels, int offset, int stride, int x, int y, int width, int height)

Since: API Level 1

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
pixelsThe array to receive the bitmap's colors
offsetThe first index to write into pixels[]
strideThe number of entries in pixels[] to skip between rows (must be >= bitmap's width). Can be negative.
xThe x coordinate of the first pixel to read from the bitmap
yThe y coordinate of the first pixel to read from the bitmap
widthThe number of pixels to read from each row
heightThe number of rows to read
Throws
IllegalArgumentExceptionif x, y, width, height exceed the bounds of the bitmap, or if abs(stride) < width.
ArrayIndexOutOfBoundsExceptionif 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                 如果xywidthheight越界或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.

Stride can be different from the image width. 

Most of the images are 4 byte aligned.

For ex. a 24 bit (RGB) image with width of 50 pixels. The total bytes required will be 150 (3(RGB)*50). As image will be 4 byte aligned, in this case the byte required will become 154. 
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): w = 100; h = 100
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颜色对应值):

Constants

public static final int BLACK

Since: API Level 1

Constant Value: -16777216 (0xff000000)

public static final int BLUE

Since: API Level 1

Constant Value: -16776961 (0xff0000ff)

public static final int CYAN

Since: API Level 1

Constant Value: -16711681 (0xff00ffff)

public static final int DKGRAY

Since: API Level 1

Constant Value: -12303292 (0xff444444)

public static final int GRAY

Since: API Level 1

Constant Value: -7829368 (0xff888888)

public static final int GREEN

Since: API Level 1

Constant Value: -16711936 (0xff00ff00)

public static final int LTGRAY

Since: API Level 1

Constant Value: -3355444 (0xffcccccc)

public static final int MAGENTA

Since: API Level 1

Constant Value: -65281 (0xffff00ff)

public static final int RED

Since: API Level 1

Constant Value: -65536 (0xffff0000)

public static final int TRANSPARENT

Since: API Level 1

Constant Value: 0 (0x00000000)

public static final int WHITE

Since: API Level 1

Constant Value: -1 (0xffffffff)

public static final int YELLOW

Since: API Level 1

Constant Value: -256 (0xffffff00)


引用参考:

1, int, int, int, int, int, int)]Android英文文档getPixels()方法介绍

2 Android中文文档getPixels()方法介绍

3 StackOverflow中关于getPixels()问答.

4 Using the LockBits method to access image data

5 本文引用参考


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/404401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

i++ 和 ++i 效率的分析以及自定义类型的自增/自减运算符重载实例

From: http://blog.csdn.net/leo115/article/details/8101541 我们通常在写for循环 的时候&#xff0c;要实现变量 i 的自增 1 &#xff1b;往往会在i 和i中随便挑一种写&#xff0c;对于i和i的理解&#xff0c;我们往往停留在返回的值的不同&#xff0c;其实i与i在实现效率上…

day②:字典的fromkeys方法

start##fromkeys方法本身就是把所有的key都指向同一个对象了>>> cdict.fromkeys(range(5),[]) >>> c{0: [], 1: [], 2: [], 3: [], 4: []}>>> c[0].append({"B":{123}})>>> c{0: [{B: set([123])}], 1: [{B: se…

[react-router] React-Router的实现原理是什么?

[react-router] React-Router的实现原理是什么&#xff1f; 1.react-router依赖基础 - history&#xff0c;是一个独立的第三方js库&#xff0c;可以用来兼容在不同浏览器、不同环境下对历史记录的管理&#xff0c;拥有统一的API。具体来说里面的history分为三类: HashRouter…

Linux Bash Shell入门教程

BASH 的基本语法 最简单的例子 —— Hello World! 关于输入、输出和错误输出 BASH 中对变量的规定&#xff08;与 C 语言的异同&#xff09; BASH 中的基本流程控制语法 函数的使用 2.1 最简单的例子 —— Hello World! 几乎所有的讲解编程的书给读者的第一个例子都是…

[react] 请描述下事件在react中的处理方式是什么?

[react] 请描述下事件在react中的处理方式是什么&#xff1f; 所有时间都挂载到 document 上&#xff0c;然后捕获对应的 dom。 类似事件委托 个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录…

BZOJ 1146: [CTSC2008]网络管理Network( 树链剖分 + 树状数组套主席树 )

树链剖分完就成了一道主席树裸题了, 每次树链剖分找出相应区间然后用BIT(可持久化)权值线段树就可以完成计数. 但是空间问题很严重....在修改时不必要的就不要新建, 直接修改原来的..详见代码. 时间复杂度O(N*log^3(N))------------------------------------------------------…

标准RTSP 消息的错误代码

From: http://blog.csdn.net/ameyume/article/details/7441060 标准RTSP消息的错误代码–在应答消息的第一行表示 ”100” ; Continue (all 100 range) “110”; Connect Timeout “200” ; OK ”201” ; Created ”250” ; Low on Storage Space ”300” ; Multiple Choic…

[react-router] React-Router怎么设置重定向?

[react-router] React-Router怎么设置重定向&#xff1f; 采用 Redirect 进行重定向 个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

struts2遍历select

2019独角兽企业重金招聘Python工程师标准>>> <s:bean name"org.apache.struts2.util.Counter" id"counter"> <s:param name"first" value"1" /> <s:param name"last" value"pageCount"…

[建议]我对软工有话说(上)

我对软工有话说 经历了一学期的软件工程课程实践&#xff0c;先给个自己在这门课中学到的东西打个分&#xff1a; 编码能力&#xff1a;★★★代码质量&#xff1a;★★★★团队管理&#xff1a;★★★★★风险控制&#xff1a;★★★★解决问题的能力&#xff1a;★★★★★下…

[react-router] React-Router的路由有几种模式?

[react-router] React-Router的路由有几种模式&#xff1f; 1、BrowserRouter&#xff1a;浏览器的路由方式&#xff0c;也就是在开发中最常使用的路由方式 2、HashRouter&#xff1a;在路径前加入#号成为一个哈希值&#xff0c;Hash模式的好处是&#xff0c;再也不会因为我们…

Spring MVC X-Frame-Options

https://docs.spring.io/spring-security/site/docs/current/reference/html/headers.html#headers-frame-options 转载于:https://www.cnblogs.com/heshuai/p/5132141.html

[react] react组件间的通信有哪些?

[react] react组件间的通信有哪些&#xff1f; PropsContextref&#xff1a;通过Ref获取组建的实例&#xff0c;在通过实例拿到组件的属性值或者方法的回调 第三方的&#xff0c;Redux是基于Conext基础上的库&#xff0c;Mobx这是依赖收集&#xff0c;EventBus是发布订阅模式&…

Fedora开机自动登录指定用户(root或普通用户)

From: http://www.360doc.com/content/12/0709/11/6828497_223143657.shtml 2010-07-19 12:53:04| 分类&#xff1a;Linux学习 | 标签&#xff1a;|字号大中小 订阅 自从Fedora 10开始&#xff0c;以后版本默认均无法在GUI下界面以root账号登录。其实这个设计是比较好的&…

android中心类库

android开发过程中总会用到各种各样的类库&#xff0c;也即jar包&#xff0c;以下地址可以满足您的需求 地址&#xff1a;http://search.maven.org/转载于:https://www.cnblogs.com/kangweifeng/p/5133233.html

[react-router] 在history模式中push和replace有什么区别?

[react-router] 在history模式中push和replace有什么区别&#xff1f; push()&#xff1a;添加一个新的记录到历史堆栈, history.length1。&#xff08;一般会用来跳转到一个新页面, 用户点击浏览器的回退按钮可以回到之前的路径。&#xff09; replace()&#xff1a;替换掉当…

Web安全开发注意事项

1.sql注入&#xff1a;这个很常规了&#xff0c;不要拼字符串以及过滤关键字都可以防住&#xff0c;需要注意的是&#xff0c;Cookie提交的参数也是可以导致注入漏洞的。 2.旁注&#xff1a;就是说在保证自己的程序没问题的同时&#xff0c;也要保证同台服务器的其他站点没问题…

[react-router] React-Router 4怎样在路由变化时重新渲染同一个组件?

[react-router] React-Router 4怎样在路由变化时重新渲染同一个组件&#xff1f; 1.在同一个组件添加不同的key,以下重新封装了组件&#xff1a; export default function (props) {return (<组件 {...props} key{search参数} />) }2.但是不知道在路由跳转的地方加入ke…

Oracle 彻底 kill session

为什么80%的码农都做不了架构师&#xff1f;>>> --*************************** -- Oracle 彻底 kill session --*************************** kill session 是DBA经常碰到的事情之一。如果kill 掉了不该kill 的session&#xff0c;则具有破坏性&#xff0c;因此尽…

消灭编译警告(Warning)

From: http://blog.csdn.net/liuysheng/article/details/7165143 如何看待编译警告 当编译程序发现程序中某个地方有疑问&#xff0c;可能有问题时就会给出一个警告信息。警告信息可能意味着程序中隐含的大错误&#xff0c;也可能确实没有问题。对于警告的正确处理方式应该…