Glide 系列(七) Glide圖像變換

前言

通過前面Glide系列文章的閱讀,相信大家對(duì)Glide的核心流程及部分關(guān)鍵模塊已經(jīng)有了較為深入的了解,本節(jié)我們繼續(xù)深入介紹Glide中的重要模塊。在平時(shí)的開發(fā)需求中,有時(shí)需要對(duì)原圖片進(jìn)行一定的處理后再顯示出來,以產(chǎn)生更加豐富的展示效果,比如對(duì)圖片進(jìn)行裁剪,高斯模糊,圓角處理,美化處理等等,如何添加這些個(gè)性化的圖片需求,Glide框架提供了相關(guān)的接口,可以輕松接入我們需要的圖片變換功能。

流程回顧

在講這部分前,先思考一下,如果我們需要對(duì)圖片進(jìn)行顯示效果的處理,首先應(yīng)該拿到原圖片,對(duì)圖片進(jìn)行相應(yīng)的處理操作后將結(jié)果返回給后續(xù)流程,因此圖片變換的處理時(shí)機(jī)應(yīng)在對(duì)數(shù)據(jù)流解碼之后,回顧一下前面章節(jié)Glide圖片加載的部分流程,如下圖所示


由流程圖中可以看到,DecodeJob 在發(fā)起網(wǎng)絡(luò)請(qǐng)求獲取到數(shù)據(jù)流之后,會(huì)對(duì)數(shù)據(jù)流進(jìn)行解碼處理得到bitmap數(shù)據(jù),然后調(diào)用到transformEncodeAndTranscode方法,這個(gè)方法中先對(duì)圖像進(jìn)行變換處理,使用到圖像變換器transformation中的transform方法,在這里面完成對(duì)圖像相應(yīng)的處理流程,看下相關(guān)源碼。

    private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
        long startTime = LogTime.getLogTime();
        Resource<T> transformed = transform(decoded); // 調(diào)用變換方法,對(duì)圖像進(jìn)行處理
        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;
    }

基本使用

那這個(gè)圖像變換器transformation是如何設(shè)置的呢,設(shè)置方法非常簡(jiǎn)單如下代碼所示,只需要調(diào)用transform方法傳入所需要的圖像變換器即可,可以根據(jù)需要傳入一個(gè)或多個(gè)圖像變換器:

Glide.with(this)
     .load(url)
     .transform(...) // 設(shè)置圖像變換器
     .into(imageView);

參考前面對(duì)Glide核心流程的源碼分析,調(diào)用load方法之后會(huì)返回一個(gè)DrawableTypeRequest類型的對(duì)象,transform最終調(diào)用到其父類GenericRequestBuilder 中的transform方法,用于設(shè)置圖像變換器參數(shù),并將其封裝到GenericRequest中, 供圖像解碼之后的流程使用,如下部分源碼所示:

    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform(
            Transformation<ResourceType>... transformations) {
        isTransformationSet = true;
        if (transformations.length == 1) {
            transformation = transformations[0]; // 保存設(shè)置的圖像變換參數(shù)
        } 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);
    }

自定義圖像變換

可以看到圖像變換器的設(shè)置方法是非常簡(jiǎn)單的,圖像變換最關(guān)鍵的是處理算法的實(shí)現(xiàn),如何來實(shí)現(xiàn)一個(gè)具體的圖像變換功能呢,Glide中給我們提供了幾個(gè)現(xiàn)成的圖片變換功能,我們以Glide中自帶的圖形變換功能 CenterCrop作為例子來看如何實(shí)現(xiàn)一個(gè)圖像變換器,并應(yīng)用到圖像加載中。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); // 獲取可復(fù)用的bitmap對(duì)象
        Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight); // 對(duì)原始bitmap進(jìn)行圖像變換處理
        if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
            toReuse.recycle();
        }
        return transformed;
    }

    @Override
    public String getId() {
        return "CenterCrop.com.bumptech.glide.load.resource.bitmap"; // 圖像變換的唯一標(biāo)識(shí)
    }
}

centercrop是一種圖像裁剪算法,等比例縮放原圖直到填滿ImageView為止,對(duì)多出來的部分進(jìn)行裁剪。ImageView 完全填充,圖像可能會(huì)被裁減掉一部分。Glide 中的CenterCrop 繼承自 BitmapTransformation 類 (靜態(tài)圖像的變換類型都是繼承BitmapTransformation)。 主要實(shí)現(xiàn)了transform 方法,在transform方法中完成對(duì)圖像裁剪的計(jì)算和處理。另外還實(shí)現(xiàn)了getId方法,用來區(qū)分不同的變換類型。我們繼續(xù)看transform方法,transform方法中共傳入四個(gè)參數(shù),分別是用于bitmap復(fù)用的bitmapool,圖像變換前的原圖 toTransform, 指定的變換后的圖像寬高(可以通過override方法來設(shè)置)。在centercrop變換中,首先從bitmappool中取出一個(gè)可復(fù)用的bitmap,用于保存變換后的bitmap,(這里穿插一句,為了防止頻繁創(chuàng)建bitmap對(duì)象而產(chǎn)生大量的內(nèi)存消耗,Glide中使用 bitmappool來對(duì)bitmap進(jìn)行復(fù)用,在平時(shí)對(duì)bitmap的使用中,從內(nèi)存優(yōu)化的角度我們可以借鑒這一做法),然后將這些參數(shù)傳入TransformationUtils.centerCrop 方法,在這里面根據(jù)設(shè)置的尺寸及圖像原始寬高進(jìn)行計(jì)算,完成的圖像裁切功能,如下源碼所示:

    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();
        // 根據(jù)原圖尺寸及所需要展示的尺寸進(jìn)行裁剪位置計(jì)算
        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;
    }

圖像變換開源庫

由此可見,實(shí)現(xiàn)一個(gè)圖像變換器,只需要繼承BitmapTransformation 類 (靜態(tài)圖像的變換類型都是繼承BitmapTransformation)。 實(shí)現(xiàn)transform 方法和getid方法。圖片變換的需求多種多樣,如果我們想要豐富的圖像展示效果,也不必全部自己來研究圖像處理算法實(shí)現(xiàn)。目前已經(jīng)有非常多Glide圖片變換的開源庫可以直接使用,比較全面的是glide-transformations這個(gè)庫,實(shí)現(xiàn)了非常豐富的圖片變換效果,例如裁剪變換、顏色變換、模糊變換等等,glide-transformations的GitHub地址是 https://github.com/wasabeef/glide-transformations 。這可以看一下部分效果:

圖像變換效果展示

這個(gè)庫的使用也非常簡(jiǎn)單,在app/build.gradle文件當(dāng)中添加如下依賴:

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

然后就可以開心的玩耍了,例如對(duì)圖像進(jìn)行模糊處理只需如下代碼即可:

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

使用圖像顏色濾鏡效果:

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

至此對(duì)Glide圖像變換這一部分的內(nèi)容就講解完了,對(duì)自定義圖像變換模塊以及如何使用圖像變換功能進(jìn)行了詳細(xì)的介紹。后續(xù)我們會(huì)繼續(xù)深入介紹Glide關(guān)鍵模塊知識(shí),歡迎繼續(xù)關(guān)注。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容