Android 图片处理

图片处理

本文主要介绍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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值