Glide — Custom Transformations

In the previous tutorials, you've learned all the fundamentals required to utilize the standard capabilities of Glide. Starting with this tutorial, and moving forward, we'll dive deep into a bunch of advanced topics. In this tutorial, we'll take a closer look at transformations.

Glide Series Overview

Transformations

Transformations can be used as an image manipulation before the image is being displayed. For example, if your app needs to display an image in grey-scale, but only has access to the original fully-colored version, you could use a transformation to manipulate the bitmap from a happy colored version to a dismal grey version. Don't understand us wrong, transformations are not limited to colors. You can change pretty much anything of an image: size, bounds, colors, pixel positioning, and more! Glide already ships with at least two transformations, and we've looked at them earlier in image resizing: fitCenter and centerCrop. Both options have such a significance, that they earned their own Glide method, so we won't include them in this tutorial.

Glide 4.x included a third option: circleCrop().

Implementing Your Own Transformation

In order to apply your own custom transformations, you'll need to create a new class, which implements the Transformation interface. The methods you are required to implement are pretty complex and you'll have to have quite the insight of the inner architecture of Glide to make it work well. If you just want to transform regular bitmaps for images (no Gifs/videos!), we would recommend to work with the abstract BitmapTransformation class. It simplifies the implementation quite a bit and should cover 95% of the use cases.

So, let's take a look at a sample BitmapTransformation implementation. If you read our content regularly, you know our favorite transformation is blurring images with Renderscript. We can apply almost the same code we've used back then to make it a Glide transformation. Since we have to extend the BitmapTransformation class, we already have our framework:

public class BlurTransformation extends BitmapTransformation {

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

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return null; // todo
    }

    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
        // todo
    }
}

Now we fill in our code from the previous tutorial to blur an image with Renderscript:

Glide 4.x

public class BlurTransformation extends BitmapTransformation {

    private RenderScript rs;

    public BlurTransformation(Context context) {
        super();

        rs = RenderScript.create(context);
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Bitmap blurredBitmap = toTransform.copy(Bitmap.Config.ARGB_8888, true);

        // Allocate memory for Renderscript to work with
        Allocation input = Allocation.createFromBitmap(rs, blurredBitmap, Allocation.MipmapControl.MIPMAP_FULL, Allocation.USAGE_SHARED);
        Allocation output = Allocation.createTyped(rs, input.getType());

        // Load up an instance of the specific script that we want to use.
        ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setInput(input);

        // Set the blur radius
        script.setRadius(10);

        // Start the ScriptIntrinisicBlur
        script.forEach(output);

        // Copy the output to the blurred bitmap
        output.copyTo(blurredBitmap);

        return blurredBitmap;
    }

    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
        messageDigest.update("blur transformation".getBytes());
    }
}

Glide 3.x

public class BlurTransformation extends BitmapTransformation {

    private RenderScript rs;

    public BlurTransformation(Context context) {
        super();

        rs = RenderScript.create( context );
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Bitmap blurredBitmap = toTransform.copy( Bitmap.Config.ARGB_8888, true );

        // Allocate memory for Renderscript to work with
        Allocation input = Allocation.createFromBitmap(
            rs, 
            blurredBitmap, 
            Allocation.MipmapControl.MIPMAP_FULL, 
            Allocation.USAGE_SHARED
        );
        Allocation output = Allocation.createTyped(rs, input.getType());

        // Load up an instance of the specific script that we want to use.
        ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setInput(input);

        // Set the blur radius
        script.setRadius(10);

        // Start the ScriptIntrinisicBlur
        script.forEach(output);

        // Copy the output to the blurred bitmap
        output.copyTo(blurredBitmap);

        toTransform.recycle();

        return blurredBitmap;
    }

    @Override
    public String getId() {
        return "blur";
    }
}

Once again, if you're confused by the code block in transform(), feel free to read up our previous RenderScript tutorial. The updateDiskCacheKey() (Glide 3.x: getId()) method describes an unique identifier for this particular transformation. Glide uses that key as part of the caching system. Makes sure to make it unique to avoid unexpected issues.

In the next section, we'll learn how to apply the transformation we've just created.

Apply a Single Transformation

In order to apply a transformation, you need topass an instance of your transformation class as a parameter to .transform(). You can apply any transformation there, no matter if it's for an image or Gif.

Glide 4.x

GlideApp  
    .with(context)
      .load(eatFoodyImages[0])
      .transform(new BlurTransformation(context))
      .into(imageView2);

Glide 3.x

In Glide 3.x there is a second option to use .bitmapTransform(), which only accepts transformations for bitmaps. This method has been scrapped for Glide 4.x.

Since our implementation above is designed for bitmaps, we could use either one in Glide 3.x:

Glide  
    .with( context )
    .load( eatFoodyImages[0] )
    .transform( new BlurTransformation( context ) )
    //.bitmapTransform( new BlurTransformation( context ) ) // this would work too!
    .into( imageView1 );

This is enough to get Glide to automatically apply our blurring algorithm to the image we've loaded from the Internet. Very helpful!

Apply Multiple Transformations

Usually, Glide's fluent interface allows methods to be chained. However, with transformations this is not the case. Make sure you only call .transform() or .bitmapTransform() once, or the previous configuration will be overwritten! Nevertheless, you can still apply multiple transformations. The way you execute multiple transformations differs between Glide 4.x and Glide 3.x.

Glide 4.x

In Glide 4.x you combine multiple transformations by creating a MultiTransformation object, which takes a list of transformations as constructor parameter:

GlideApp  
    .with(context)
    .load(eatFoodyImages[0])
    .transform(
        new MultiTransformation(
            new GrayscaleTransformation(),
            new BlurTransformation(context)))
    .into(imageView3);

Glide 3.x

In Glide 3.x you can execute multiple transformations by passing the transformation objects as the parameter into .transform() (or .bitmapTransform()):

Glide  
    .with( context )
    .load( eatFoodyImages[1] )
    .transform( new GreyscaleTransformation(), new BlurTransformation( context ) )
    .into( imageView2 );

In this code snippet, we apply a grey-scale to the image, and afterwards blur it. Glide executes both transformations automatically for you. Awesome!

Hint: You cannot use .centerCrop() or .fitCenter() when you use transformations.

Collection of Glide Transformations

If you already have an idea what kind of transformation you might be able to use in your app, take a second to look at the following library: glide-transformations. It provides a whole collection of various glide transformations. It's worth to check if your idea might already be implemented.

The library ships with two different versions. The extended version includes more transformations, which are computed on the device's GPU. They require an additional dependency, so the setup for the two versions is a little bit different. You should look through the list of transformations and decide which version you need.

Setup for Glide Transformations

The setup is easy! For the basic version you can just add another line to your current build.gradle:

Glide 4.x

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

Glide 3.x

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

If you would like to use the GPU transformations:

Glide 4.x

repositories {  
    jcenter()
    mavenCentral()
}

dependencies {  
    compile 'jp.wasabeef:glide-transformations:3.0.1'
    compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
}

Glide 3.x

repositories {  
    jcenter()
    mavenCentral()
}

dependencies {  
    compile 'jp.wasabeef:glide-transformations:2.0.2'
    compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
}

Usage of Glide Transformations

After you've synced Android Studio with the build.gradle file, you can go ahead and use the transformation collection. The usage pattern is the same as with your own, custom transformations. Let's assume we would like to blur an image with the collection's blur transformation:

Glide 4.x

GlideApp  
    .with(context)
    .load(eatFoodyImages[2])
    .transform(
        new MultiTransformation(
            new jp.wasabeef.glide.transformations.BlurTransformation(25, 2),
            new CropCircleTransformation()))
    .into(imageView3);

Glide 3.x

Glide  
    .with( context )
    .load( eatFoodyImages[2] )
    .bitmapTransform( new jp.wasabeef.glide.transformations.BlurTransformation( context, 25 ) )
    .into( imageView3 );

You can also apply a list of transformations via MultiTransformation, just like you've seen above.

Outlook

In this tutorial, you've learned a very useful tool of Glide: transformations. You've learned how to implement and apply pre-defined and custom transformations. We hope this gives you everything you need to implement it in your app! If you've questions, let us know below in the comments.

After looking at a very customizable feature in this tutorial, we'll continue to do so in the next one. Next week, we'll look at custom animations.

Explore the Library

Find interesting tutorials and solutions for your problems.