Android 图片处理

来源:互联网 发布:家谱排版软件 编辑:程序博客网 时间:2024/05/24 03:42

图片处理

本文主要介绍Android平台下图片的获取和处理,欢迎点赞,拍砖,留言

图片的加载和创造

  • 图片的加载

图片的来源十分的丰富,可以是本地资源文件,也是可以是本地文件,同样也支持对于流的加载。实例如下:

本地资源:BitmapFactory.decodeResource(this.getResources(),R.id.XX,opts)

本地文件:BitmapFactory.decodeFile(“文件路径”)

本地流:BitmapFactory.decodeStream(流);

其中需要重点关注的是它们都可以带有参数来进行加载,这个参数就是BitmapFactory.Options,它可以决定加载的时候是采用什么样的模式!最明显是例子就是在很多的情况下,并不会直接把一张图片加载到内存里面,而是才用先查看图片的大小,然后设定一定的缩放比例进行加载。

查看图片的宽高,需要设置options.inJustDecodeBounds = true;通过options.outHeight;options.outWidth;来获取宽高

缩放是通过option.inJustDecodeBounds = false 并且,ptions.inSampleSize = 缩放的比例

这个地方的option很有点值得考虑的,在Android2.3之前有,设置inPuraeable 参数可以放到匿名的内存区来存放图片。后来就不行了,但是有个inBitmap的参数可以重复利用之前的图片空间。图片加载经典的三级缓存,其中用到了软引用和inBitmap来极大程序的缓解图片的加载问题。
https://developer.android.google.cn/topic/performance/graphics/manage-memory.html

加载出来的图片一般是不可变,就是没法做下面的变换动作。

  • 图片的创建

创造一张图片使用的Bitmap.createBitmap()函数,里面需要一些参数指定图片的宽高之类的,其中需要注意是Config这个参数,拿Bitmap.Config.ARGB_8888举例子,它表示图片的每个像素是由A(透明度值),R(红色),G(绿色),B(蓝色)组成,并且每个值用八位表示!其它的参数也大概是这样的意思。

图片的变换

  • 利用系统矩阵转换

首先介绍下矩阵的基础知识,Android中用Matrix这个来表示,它是一个3X3的矩阵

    public static final int MSCALE_X = 0;       public static final int MSKEW_X  = 1;       public static final int MTRANS_X = 2;       public static final int MSKEW_Y  = 3;       public static final int MSCALE_Y = 4;       public static final int MTRANS_Y = 5;      public static final int MPERSP_0 = 6;      public static final int MPERSP_1 = 7;      public static final int MPERSP_2 = 8;  

其每个值的意义如上,其实这个是没什么必要的,知道具体的概念后自己可以任意构造。每个像素点是一个3X1的一维矩阵来表示,也许很奇怪,为什么是三维,其实X坐标,Y坐标,以及透视值。需要注意是就是这个透视值,在视觉中,即时是同一张图片你看的远近不一样,其形成的视觉效果也是不一样的。具体的知识可以查看透视图相关。

理论上,我们有了这个矩阵后可以做任意操作,但是Matrix帮你封装了一些基本的操作,这里进行一些演示:
首先需要建立一个可以绘制的位图,如果下所示:

bitmap = Bitmap.createBitmap(具体宽,具体高, Bitmap.Config.ARGB_8888);canvas = new Canvas(bitmap);Paint paint = new Paint();canvas.drawBitmap(temp,0f,0f,paint);imageView.setImageBitmap(bitmap);

进行旋转操作:

//先进行清理,如果是一次性的可以不用Paint paint2 = new Paint();paint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));                canvas.drawPaint(paint2);paint2.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));Matrix matrix = new Matrix();matrix.setRotate(50);paint2.setAntiAlias(true);canvas.drawBitmap(temp,matrix,paint2);imageView.postInvalidate();

进行镜像操作:

Paint paint3 = new Paint();paint3.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));canvas.drawPaint(paint3);paint3.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));Matrix  matrixXY  = new Matrix();matrixXY.setScale(-1,1);//这里进行处理matrixXY.postTranslate((float) temp.getWidth(),0);paint3.setAntiAlias(true);canvas.drawBitmap(temp,matrixXY,paint3);imageView.postInvalidate();

修改透视值,形成视觉上的缩放:

Paint paint4 = new Paint();paint4.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));canvas.drawPaint(paint4);paint4.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));Matrix  matrixX  = new Matrix();matrixX.setValues(new float[]{        1,0,0,        0,1,0,        0,0,2});paint4.setAntiAlias(true);canvas.drawBitmap(temp,matrixX,paint4);imageView.postInvalidate();

以上的的矩阵变化都是指的空间变换,其实还有一个ColorMatrix用来控制RGBA的属性变换,挺好玩的,大家可以琢磨下,掌握了这些简单的东西后就可以完全的控制一张图片的表示了。(因为后面要黏代码,原理的有时间搁在其它文章里面讲)

  • 手动处理图片的像素点

具体到每个像素点的操作,这更应该像C程序来做的事哈!其实为了效率,一般是也是扔给C来做的,不过也可以使用java代码,下面来看两个有意思的代码

高斯模糊:

    public static Bitmap doBlur(Bitmap sentBitmap, int radius,                                boolean canReuseInBitmap) {        Bitmap bitmap;        if (canReuseInBitmap) {            bitmap = sentBitmap;        } else {            bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);        }        if (radius < 1) {            return (null);        }        int w = bitmap.getWidth();        int h = bitmap.getHeight();        int[] pix = new int[w * h];        bitmap.getPixels(pix, 0, w, 0, 0, w, h);        int wm = w - 1;        int hm = h - 1;        int wh = w * h;        int div = radius + radius + 1;        int r[] = new int[wh];        int g[] = new int[wh];        int b[] = new int[wh];        int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;        int vmin[] = new int[Math.max(w, h)];        int divsum = (div + 1) >> 1;        divsum *= divsum;        int dv[] = new int[256 * divsum];        for (i = 0; i < 256 * divsum; i++) {            dv[i] = (i / divsum);        }        yw = yi = 0;        int[][] stack = new int[div][3];        int stackpointer;        int stackstart;        int[] sir;        int rbs;        int r1 = radius + 1;        int routsum, goutsum, boutsum;        int rinsum, ginsum, binsum;        for (y = 0; y < h; y++) {            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;            for (i = -radius; i <= radius; i++) {                p = pix[yi + Math.min(wm, Math.max(i, 0))];                sir = stack[i + radius];                sir[0] = (p & 0xff0000) >> 16;                sir[1] = (p & 0x00ff00) >> 8;                sir[2] = (p & 0x0000ff);                rbs = r1 - Math.abs(i);                rsum += sir[0] * rbs;                gsum += sir[1] * rbs;                bsum += sir[2] * rbs;                if (i > 0) {                    rinsum += sir[0];                    ginsum += sir[1];                    binsum += sir[2];                } else {                    routsum += sir[0];                    goutsum += sir[1];                    boutsum += sir[2];                }            }            stackpointer = radius;            for (x = 0; x < w; x++) {                r[yi] = dv[rsum];                g[yi] = dv[gsum];                b[yi] = dv[bsum];                rsum -= routsum;                gsum -= goutsum;                bsum -= boutsum;                stackstart = stackpointer - radius + div;                sir = stack[stackstart % div];                routsum -= sir[0];                goutsum -= sir[1];                boutsum -= sir[2];                if (y == 0) {                    vmin[x] = Math.min(x + radius + 1, wm);                }                p = pix[yw + vmin[x]];                sir[0] = (p & 0xff0000) >> 16;                sir[1] = (p & 0x00ff00) >> 8;                sir[2] = (p & 0x0000ff);                rinsum += sir[0];                ginsum += sir[1];                binsum += sir[2];                rsum += rinsum;                gsum += ginsum;                bsum += binsum;                stackpointer = (stackpointer + 1) % div;                sir = stack[(stackpointer) % div];                routsum += sir[0];                goutsum += sir[1];                boutsum += sir[2];                rinsum -= sir[0];                ginsum -= sir[1];                binsum -= sir[2];                yi++;            }            yw += w;        }        for (x = 0; x < w; x++) {            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;            yp = -radius * w;            for (i = -radius; i <= radius; i++) {                yi = Math.max(0, yp) + x;                sir = stack[i + radius];                sir[0] = r[yi];                sir[1] = g[yi];                sir[2] = b[yi];                rbs = r1 - Math.abs(i);                rsum += r[yi] * rbs;                gsum += g[yi] * rbs;                bsum += b[yi] * rbs;                if (i > 0) {                    rinsum += sir[0];                    ginsum += sir[1];                    binsum += sir[2];                } else {                    routsum += sir[0];                    goutsum += sir[1];                    boutsum += sir[2];                }                if (i < hm) {                    yp += w;                }            }            yi = x;            stackpointer = radius;            for (y = 0; y < h; y++) {                // Preserve alpha channel: ( 0xff000000 & pix[yi] )                pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16)                        | (dv[gsum] << 8) | dv[bsum];                rsum -= routsum;                gsum -= goutsum;                bsum -= boutsum;                stackstart = stackpointer - radius + div;                sir = stack[stackstart % div];                routsum -= sir[0];                goutsum -= sir[1];                boutsum -= sir[2];                if (x == 0) {                    vmin[y] = Math.min(y + r1, hm) * w;                }                p = x + vmin[y];                sir[0] = r[p];                sir[1] = g[p];                sir[2] = b[p];                rinsum += sir[0];                ginsum += sir[1];                binsum += sir[2];                rsum += rinsum;                gsum += ginsum;                bsum += binsum;                stackpointer = (stackpointer + 1) % div;                sir = stack[stackpointer];                routsum += sir[0];                goutsum += sir[1];                boutsum += sir[2];                rinsum -= sir[0];                ginsum -= sir[1];                binsum -= sir[2];                yi += w;            }        }        bitmap.setPixels(pix, 0, w, 0, 0, w, h);        return (bitmap);    }

打马赛克:

    /**     *     * @param bitmap     * @param targetRect     * @param blockSize     * @return     * @throws Exception     */    public static Bitmap makeMosaic(Bitmap bitmap, Rect targetRect,                                    int blockSize) throws OutOfMemoryError {        if (bitmap == null || bitmap.getWidth() == 0 || bitmap.getHeight() == 0                || bitmap.isRecycled()) {            throw new RuntimeException("bad bitmap to add mosaic");        }        if (blockSize < 4) {            blockSize = 4;        }        if (targetRect == null) {            targetRect = new Rect();        }        int bw = bitmap.getWidth();        int bh = bitmap.getHeight();        if (targetRect.isEmpty()) {            targetRect.set(0, 0, bw, bh);        }        //        int rectW = targetRect.width();        int rectH = targetRect.height();        int[] bitmapPxs = new int[bw * bh];        // 获取图片像素        bitmap.getPixels(bitmapPxs, 0, bw, 0, 0, bw, bh);        //目标矩形区域根据块的大小切割出来的行数与列数        int rowCount = (int) Math.ceil((float) rectH / blockSize);        int columnCount = (int) Math.ceil((float) rectW / blockSize);        //最大的X,Y为图片的宽高        int maxX = bw;        int maxY = bh;        for (int r = 0; r < rowCount; r++) { // row loop            for (int c = 0; c < columnCount; c++) {// column loop                int startX = targetRect.left + c * blockSize + 1;                int startY = targetRect.top + r * blockSize + 1;                dimBlock(bitmapPxs, startX, startY, blockSize, maxX, maxY);            }        }        return Bitmap.createBitmap(bitmapPxs, bw, bh, Bitmap.Config.ARGB_8888);    }    /**     * 从块内取样,并放大,从而达到马赛克的模糊效果     *     * @param pxs     * @param startX     * @param startY     * @param blockSize     * @param maxX     * @param maxY     */    private static void dimBlock(int[] pxs, int startX, int startY,                                 int blockSize, int maxX, int maxY) {        int stopX = startX + blockSize - 1;        int stopY = startY + blockSize - 1;        if (stopX > maxX) {            stopX = maxX;        }        if (stopY > maxY) {            stopY = maxY;        }        //        int sampleColorX = startX + blockSize / 2;        int sampleColorY = startY + blockSize / 2;        //        if (sampleColorX > maxX) {            sampleColorX = maxX;        }        if (sampleColorY > maxY) {            sampleColorY = maxY;        }        //在像素点数组中所处的行数        int colorLinePosition = (sampleColorY - 1) * maxX;        //在像素点数所处的位置        int sampleColor = pxs[colorLinePosition + sampleColorX - 1];// 像素从1开始,但是数组层0开始        //把块中的颜色都已取样的像素点来填充,用块中的像素点取样        for (int y = startY; y <= stopY; y++) {            int p = (y - 1) * maxX;            for (int x = startX; x <= stopX; x++) {                // 像素从1开始,但是数组层0开始                pxs[p + x - 1] = sampleColor;            }        }    }

图片的存储

图片的缓存的三级结构

  • 内存缓存* LruCache*(提供map的功能,可以通过key获取和存储图片)
  • 文件缓存 内部存储的Cache和外部存储的Cache,或者直接就放在外置SDCard的公共目录下
  • 网络 最后的选择才是从网路上请求

一般的使用顺序就是,当想要获取一张图片的时候,先去内存缓存中查找,再去文件查找,都没有的情况下才去请求网络,网络请求的图片保存在文件缓存中。以上内存不是我们关注的重点。跳过,有兴趣的可以交流下!

图片的保存

图片可以压缩成PNG,JPG等格式,在压缩成JPG的时候提供其压缩率,这个值只对JPG有效,因为PNG是无所压缩的。使用的函数:
compress (Bitmap.CompressFormat format,int quality,OutputStream stream),我们可以看到起格式有JPG,PNG,WEBP三种,后面的质量只对JPG有效,一般设置为90即可。输出流一般是用于保存文件路径。

详情请见下回分晓,哇哈哈哈~~

0 0
原创粉丝点击