Glide 系列(七) Glide图像变换

前言

通过前面Glide系列文章的阅读,相信大家对Glide的核心流程及部分关键模块已经有了较为深入的了解,本节我们继续深入介绍Glide中的重要模块。在平时的开发需求中,有时需要对原图片进行一定的处理后再显示出来,以产生更加丰富的展示效果,比如对图片进行裁剪,高斯模糊,圆角处理,美化处理等等,如何添加这些个性化的图片需求,Glide框架提供了相关的接口,可以轻松接入我们需要的图片变换功能。

流程回顾

在讲这部分前,先思考一下,如果我们需要对图片进行显示效果的处理,首先应该拿到原图片,对图片进行相应的处理操作后将结果返回给后续流程,因此图片变换的处理时机应在对数据流解码之后,回顾一下前面章节Glide图片加载的部分流程,如下图所示


由流程图中可以看到,DecodeJob 在发起网络请求获取到数据流之后,会对数据流进行解码处理得到bitmap数据,然后调用到transformEncodeAndTranscode方法,这个方法中先对图像进行变换处理,使用到图像变换器transformation中的transform方法,在这里面完成对图像相应的处理流程,看下相关源码。

    private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
        long startTime = LogTime.getLogTime();
        Resource<T> transformed = transform(decoded); // 调用变换方法,对图像进行处理
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Transformed resource from source", startTime);
        }

        writeTransformedToCache(transformed);

        startTime = LogTime.getLogTime();
        Resource<Z> result = transcode(transformed);
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Transcoded transformed from source", startTime);
        }
        return result;
    }

基本使用

那这个图像变换器transformation是如何设置的呢,设置方法非常简单如下代码所示,只需要调用transform方法传入所需要的图像变换器即可,可以根据需要传入一个或多个图像变换器:

Glide.with(this)
     .load(url)
     .transform(...) // 设置图像变换器
     .into(imageView);

参考前面对Glide核心流程的源码分析,调用load方法之后会返回一个DrawableTypeRequest类型的对象,transform最终调用到其父类GenericRequestBuilder 中的transform方法,用于设置图像变换器参数,并将其封装到GenericRequest中, 供图像解码之后的流程使用,如下部分源码所示:

    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform(
            Transformation<ResourceType>... transformations) {
        isTransformationSet = true;
        if (transformations.length == 1) {
            transformation = transformations[0]; // 保存设置的图像变换参数
        } else {
            transformation = new MultiTransformation<ResourceType>(transformations);
        }
        return this;
    }

    private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
            RequestCoordinator requestCoordinator) {
        return GenericRequest.obtain(
                loadProvider,
                model,
                signature,
                context,
                priority,
                target,
                sizeMultiplier,
                placeholderDrawable,
                placeholderId,
                errorPlaceholder,
                errorId,
                fallbackDrawable,
                fallbackResource,
                requestListener,
                requestCoordinator,
                glide.getEngine(),
                transformation,
                transcodeClass,
                isCacheable,
                animationFactory,
                overrideWidth,
                overrideHeight,
                diskCacheStrategy);
    }

自定义图像变换

可以看到图像变换器的设置方法是非常简单的,图像变换最关键的是处理算法的实现,如何来实现一个具体的图像变换功能呢,Glide中给我们提供了几个现成的图片变换功能,我们以Glide中自带的图形变换功能 CenterCrop作为例子来看如何实现一个图像变换器,并应用到图像加载中。CenterCrop的源码如下所示:

public class CenterCrop extends BitmapTransformation {

    public CenterCrop(Context context) {
        super(context);
    }

    public CenterCrop(BitmapPool bitmapPool) {
        super(bitmapPool);
    }
    
    @SuppressWarnings("PMD.CompareObjectsWithEquals")
    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null
                ? toTransform.getConfig() : Bitmap.Config.ARGB_8888); // 获取可复用的bitmap对象
        Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight); // 对原始bitmap进行图像变换处理
        if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
            toReuse.recycle();
        }
        return transformed;
    }

    @Override
    public String getId() {
        return "CenterCrop.com.bumptech.glide.load.resource.bitmap"; // 图像变换的唯一标识
    }
}

centercrop是一种图像裁剪算法,等比例缩放原图直到填满ImageView为止,对多出来的部分进行裁剪。ImageView 完全填充,图像可能会被裁减掉一部分。Glide 中的CenterCrop 继承自 BitmapTransformation 类 (静态图像的变换类型都是继承BitmapTransformation)。 主要实现了transform 方法,在transform方法中完成对图像裁剪的计算和处理。另外还实现了getId方法,用来区分不同的变换类型。我们继续看transform方法,transform方法中共传入四个参数,分别是用于bitmap复用的bitmapool,图像变换前的原图 toTransform, 指定的变换后的图像宽高(可以通过override方法来设置)。在centercrop变换中,首先从bitmappool中取出一个可复用的bitmap,用于保存变换后的bitmap,(这里穿插一句,为了防止频繁创建bitmap对象而产生大量的内存消耗,Glide中使用 bitmappool来对bitmap进行复用,在平时对bitmap的使用中,从内存优化的角度我们可以借鉴这一做法),然后将这些参数传入TransformationUtils.centerCrop 方法,在这里面根据设置的尺寸及图像原始宽高进行计算,完成的图像裁切功能,如下源码所示:

    public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) {
        if (toCrop == null) {
            return null;
        } else if (toCrop.getWidth() == width && toCrop.getHeight() == height) {
            return toCrop;
        }
        final float scale;
        float dx = 0, dy = 0;
        Matrix m = new Matrix();
        // 根据原图尺寸及所需要展示的尺寸进行裁剪位置计算
        if (toCrop.getWidth() * height > width * toCrop.getHeight()) {
            scale = (float) height / (float) toCrop.getHeight();
            dx = (width - toCrop.getWidth() * scale) * 0.5f;
        } else {
            scale = (float) width / (float) toCrop.getWidth();
            dy = (height - toCrop.getHeight() * scale) * 0.5f;
        }

        m.setScale(scale, scale);
        m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
        final Bitmap result;
        if (recycled != null) {
            result = recycled;
        } else {
            result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop));
        }

        TransformationUtils.setAlpha(toCrop, result);

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint(PAINT_FLAGS);
        canvas.drawBitmap(toCrop, m, paint);
        return result;
    }

图像变换开源库

由此可见,实现一个图像变换器,只需要继承BitmapTransformation 类 (静态图像的变换类型都是继承BitmapTransformation)。 实现transform 方法和getid方法。图片变换的需求多种多样,如果我们想要丰富的图像展示效果,也不必全部自己来研究图像处理算法实现。目前已经有非常多Glide图片变换的开源库可以直接使用,比较全面的是glide-transformations这个库,实现了非常丰富的图片变换效果,例如裁剪变换、颜色变换、模糊变换等等,glide-transformations的GitHub地址是 https://github.com/wasabeef/glide-transformations 。这可以看一下部分效果:

图像变换效果展示

这个库的使用也非常简单,在app/build.gradle文件当中添加如下依赖:

dependencies {
    compile 'jp.wasabeef:glide-transformations:2.0.2'
}

然后就可以开心的玩耍了,例如对图像进行模糊处理只需如下代码即可:

Glide.with(this)
     .load(url)
     .transform(new BlurTransformation(this,20))
     .into(imageView);

使用图像颜色滤镜效果:

Glide.with(this)
     .load(url)
     .transform(new ColorFilterTransformation(this, 0x7900CCCC))
     .into(imageView);

至此对Glide图像变换这一部分的内容就讲解完了,对自定义图像变换模块以及如何使用图像变换功能进行了详细的介绍。后续我们会继续深入介绍Glide关键模块知识,欢迎继续关注。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270