Android开发Android开发Android技术知识

Android关于裁剪图片透明区域的算法

2019-05-27  本文已影响2人  SerBad

最近项目中遇到这么一个需求,需要裁剪掉图片的透明区域。找了很久,最后确定,只能通过自己读取Bitmap的像素点来读取图片的边界来裁剪。下面记录一下过程。

原图如下

PorterDuffXfermode

最开始想的是使用PorterDuffXfermode来处理,因为这种方式其实很快的,但是,虽然这种方式可以用来处理图片,但是无法满足获取图片边界的需求。
代码如下:

    public static Bitmap cutStickerBitmap(String name, Context context, Bitmap bitmap) {
        Calendar calendar = Calendar.getInstance();
        Bitmap bit = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        bit.eraseColor(Color.WHITE);
        Canvas canvas = new Canvas(bit);

        Paint paint = new Paint();
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

        canvas.drawBitmap(bitmap, 0, 0, paint);
        Paint paintRect = new Paint();
        paintRect.setColor(Color.RED);
        paintRect.setAntiAlias(true);
        paintRect.setStyle(Paint.Style.STROKE);
        paintRect.setStrokeWidth(4);
        Rect rect = new Rect();
        //画一个框表示边界
        rect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
        canvas.drawRect(rect, paintRect);
        Log.i("xx", " 运行时间 cutStickerBitmap " + (Calendar.getInstance().getTimeInMillis() - calendar.getTimeInMillis()));
        return bit;
    }

代码处理的时间,可以看到时间很快,可惜不符合需求:



处理的结果如下:

自己写算法读像素

关于这个算法,其实,我写过两个版本。 自己写算法的话,需要自己评估你自己的需求和结果。一开始,为了防止图形是不连续的,我采取的思路是从图片四周来获取边界。思路如图:



这么做的确是可以保证百分百正确的,但是这么做有一个问题,那就是因为每次得到的图片分辨率不一样,在有的时候处理一次需要10s以上,这实在是太慢了。

后来仔细观察之后发现,其实不需要那么严格的,因为得到的所有的图形都是连续的,所以新的算法就是从中间开始处理的。



沿着中线,先向右边获取边界,再沿着中线从左边获取边界,同时为了加快速度,采取的一次读两个像素的方式,如果要求严谨可以改成每次读一个像素,算法代码如下。

    //处理贴纸
    public static Bitmap cutStickerBitmap(Bitmap bitmap) {
        Calendar calendar = Calendar.getInstance();
        int top = 0;
        int left = 0;
        int right = bitmap.getWidth() - 1;
        int bottom = 0;

        for (int w = bitmap.getWidth() / 2; w < bitmap.getWidth(); w += 2) {
            int h = 0;
            for (h = 0; h < bitmap.getHeight(); h+=2) {
                int color = bitmap.getPixel(w, h);
                if (color != 0) {
                    if (top == 0) {
                        top = h;
                    }
                    top = Math.min(top, h);
                    break;
                }
            }

            if (h >= bitmap.getHeight() - 1) {
                right = w;
                break;
            }
        }

        for (int w = bitmap.getWidth() / 2 - 1; w >= 0; w -= 2) {
            int h = 0;
            for (h = 0; h < bitmap.getHeight(); h+=2) {
                int color = bitmap.getPixel(w, h);
                if (color != 0) {
                    if (top == 0) {
                        top = h;
                    }
                    top = Math.min(top, h);
                    break;
                }
            }

            if (h >= bitmap.getHeight() - 1) {
                left = w;
                break;
            }
        }

        for (int w = left; w <= right; w++) {
            for (int h = bitmap.getHeight() - 1; h >= top; h--) {
                int color = bitmap.getPixel(w, h);
                if (color != 0) {
                    bottom = Math.max(bottom, h);
                    break;
                }
            }
        }

        //对边界值做一次判断,保证严谨
        int x = (int) Math.max(left  , 0f);
        int y = (int) Math.max(top  , 0f);
        int w = (int) Math.min(right - left  , bitmap.getWidth() - x);
        int h = (int) Math.min(bottom - top  , bitmap.getHeight() - y);
        if (x + w > bitmap.getWidth()) {
            x = 0;
            w = bitmap.getWidth();
        }

        if (y + h > bitmap.getHeight()) {
            y = 0;
            h = bitmap.getHeight();
        }
        Log.i("xx", " 运行时间 cutStickerBitmap " + (Calendar.getInstance().getTimeInMillis() - calendar.getTimeInMillis()));

        return Bitmap.createBitmap(bitmap, x, y, w, h);
    }
代码处理的时间如下:
上一篇下一篇

猜你喜欢

热点阅读