Hello everyone, it’s time to learn Glide again. Some time ago, due to the tight development of the project, and then I fell ill again, so I stopped working for a month, but now I can finally resume the normal update. Today is the fifth article in this series. In the previous four articles, we have learned the basic usage of Glide, the working principle and execution process of Glide, the caching mechanism of Glide, and the callback mechanism of Glide, etc. If you can grasp the above four passages well, congratulations, you have become a green hand now.

If you have not read the first four articles, then you can click on the link behind the Android picture loading framework to read the most complete analysis (four), play Glide callback and listening.

But Glide’s framework is so powerful that it can do much more than what we’ve learned so far. So, today we are going to learn a new function module, and it is a very important module – Glide picture change function.

A problem

Before we begin to learn the picture change function of Glide, let’s first look at a problem that many people may have encountered when using Glide, just under the topic of this content we will incidentally solve the problem.

First we try to load an image using Glide. The URL of the image is:

https://www.baidu.com/img/bd_logo1.png
Copy the code

This is a picture of baidu’s home page logo with a size of 540 x 258 pixels.

Next we’ll write a very simple layout file that looks like this:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load Image"
        android:onClick="loadImage"
        />

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</LinearLayout>
Copy the code

The layout file has only one button and an ImageView for displaying images. Notice that the width and the height of the ImageView are both wrAP_content.

Then write the following code to load the image:

public class MainActivity extends AppCompatActivity { ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView) findViewById(R.id.image_view); } public void loadImage(View view) { String url = "https://www.baidu.com/img/bd_logo1.png"; Glide.with(this) .load(url) .into(imageView); }}Copy the code

This simple code should be child’s play by now, and I don’t have to explain it to you. Now run the program and click the Load image button. The result is as shown below.

The images are loading normally, but did you notice a problem? The size of this Baidu logo image is only 540258 pixels, but the resolution of my phone is 10801920 pixels, and we set the width and height of ImageView to wrAP_content, so the width of the image should only be half of the screen width of the phone. But it’s full screen. Why is that?

If you’ve been stumped by this question before, congratulations, this article is just what you need. This phenomenon is caused by Glide’s picture conversion function. So next we will first analyze how to solve this problem, and then learn more about Glide picture changes.

For those of you who know a little about Android, you probably know that ImageView has a scaleType attribute. However, most people do not know what the default scaleType of ImageView is if you do not specify the scaleType attribute.

I can’t answer this question if you ask me directly. But doing is the only way to test the truth. If you want to know the answer, try it yourself.

public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView) findViewById(R.id.image_view); Log.d(TAG, "imageView scaleType is " + imageView.getScaleType()); }... }Copy the code

As you can see, we printed the ImageView default scaleType in the onCreate() method and re-run the program. The result is as follows:

From this we can see that the default scaleType of ImageView is FIT_CENTER when not explicitly specified.

With this premise, we can continue to analyze Glide’s source code. Of course, the source code in this article is still built on the basis of the second source code analysis, has not seen this article friends, it is recommended to read the Android picture loading framework the most complete analysis (two), from the perspective of the source code to understand Glide’s execution process.

Recall the into() method we examined in the second article, which is in the GenericRequestBuilder class and looks like this:

public Target<TranscodeType> into(ImageView view) { Util.assertMainThread(); if (view == null) { throw new IllegalArgumentException("You must pass in a non null View"); } if (! isTransformationSet && view.getScaleType() ! = null) { switch (view.getScaleType()) { case CENTER_CROP: applyCenterCrop(); break; case FIT_CENTER: case FIT_START: case FIT_END: applyFitCenter(); break; default: } } return into(glide.buildImageViewTarget(view, transcodeClass)); }Copy the code

Remember when we analyzed this code, we skipped all the previous code and went straight to the last line. At that time, our main task was to analyze Glide’s main line of execution and not to read the details, but now it is time for us to read the details.

As you can see, there is a switch judgment here at line 7. If the ImageView scaleType is CENTER_CROP, the applyCenterCrop() method is called, If scaleType is FIT_CENTER, FIT_START, or FIT_END, the applyFitCenter() method is called. The applyCenterCrop() and applyFitCenter() methods here add an image transform operation to Glide’s load process, but we won’t go into the source code.

Now it’s pretty clear that since the default scaleType of the ImageView is FIT_CENTER, a FitCenter image transformation is automatically added, and something is done during the image transformation that causes the image to fill the full screen.

So how do we solve this problem? The most straightforward way is to look at the source code to change. Isn’t an image transform operation automatically added when ImageView scaleType is CENTER_CROP, FIT_CENTER, FIT_START, or FIT_END? So let’s just change scaleType to something else. ImageView scaleType options include CENTER, CENTER_INSIDE, FIT_XY, and so on. This is certainly a solution, but it’s a stupid solution because we changed the ImageView scaleType to solve this problem, If you really need an ImageView whose scaleType is CENTER_CROP or FIT_CENTER, you might be confused.

The above is only a solution we obtained through the analysis of the source code and is not recommended. Glide actually provides us with a special API to add and cancel image transforms. To solve this problem, we can use the following code:

Glide.with(this)
     .load(url)
     .dontTransform()
     .into(imageView);
Copy the code

As you can see, the dontTransform() method is called to let Glide load the image without transforming it, and the applyCenterCrop() and applyFitCenter() calls are invalid.

Now let’s rerun the code and see what it looks like:

The image will only take up half the screen width, which means our code is working.

The problem with using the dontTransform() method is that all image transform operations are invalid. What if I have some image transform operations that MUST be performed? Don’t worry, there is always a way. In this case, we just need to use the Override () method to force the image size to be the original size, as shown below:

Glide.with(this)
     .load(url)
     .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
     .into(imageView);
Copy the code

The problem is also solved by specifying the width and height of the image to target.size_original by using the override() method. The final result of the program is exactly the same as above, so I won’t re-capture it.

From this, we can see that the reason for this problem, and Glide picture conversion function is inseparable. So also through this problem, we have a basic understanding of Glide picture transformation. Now, let’s get to the point of this article.

Basic usage of picture transformation

As the name implies, the picture transformation means that Glide from loading the original picture to the final display to the user before, and some transformation processing, so as to achieve some more rich picture effects, such as picture rounded, rounded, blurred and so on.

Adding the image transform usage is very simple, we just call the transform() method and pass the image transform we want to perform as an argument to the transform() method, as shown below:

Glide.with(this)
     .load(url)
     .transform(...)
     .into(imageView);
Copy the code

As for the specific picture transformation operation, this is usually needed to write our own. But Glide has built in two image transformation operations that we can use directly, CenterCrop and FitCenter.

Neither of these built-in image transformations requires the transform() method, and Glide provides a ready-made API for us to use:

Glide.with(this)
     .load(url)
     .centerCrop()
     .into(imageView);

Glide.with(this)
     .load(url)
     .fitCenter()
     .into(imageView);
Copy the code

Of course, the centerCrop() and fitCenter() methods are just a wrapper around the transform() method, and the source code behind them is still implemented using the transform() method, as shown below:

public class DrawableRequestBuilder<ModelType> extends GenericRequestBuilder<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable> implements BitmapOptions, DrawableOptions { ... /** * Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}. * * @see #fitCenter() * @see #transform(BitmapTransformation...) * @see #bitmapTransform(Transformation[]) * @see #transform(Transformation[]) * * @return This request builder. */ @SuppressWarnings("unchecked") public DrawableRequestBuilder<ModelType> centerCrop() { return transform(glide.getDrawableCenterCrop()); } /** * Transform {@link GlideDrawable}s using {@link com.bumptech.glide.load.resource.bitmap.FitCenter}. * * @see #centerCrop() * @see #transform(BitmapTransformation...) * @see #bitmapTransform(Transformation[]) * @see #transform(Transformation[]) * * @return This request builder. */ @SuppressWarnings("unchecked") public DrawableRequestBuilder<ModelType> fitCenter() { return transform(glide.getDrawableFitCenter()); }... }Copy the code

So what can these two built-in image transformation operations achieve? The FitCenter effect we saw earlier is to fill the screen with the original aspect ratio. What about CenterCrop? Let’s give it a try.

In order to make the effect more obvious, I will not use the baidu home page Logo, but a beautiful picture of bing home page. Glide load Bing this image without applying any image transforms as shown below.

Now we add a CenterCrop image transform operation as follows:

String url = "http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg";
Glide.with(this)
     .load(url)
     .centerCrop()
     .into(imageView);
Copy the code

Run the program again and click the load image button. The result is as shown below.

As you can see, the image shown here is the result of cropping the central area of the original image.

In addition, the centerCrop() method can work with the Override () method to achieve richer effects, such as specifying the image cropping scale:

String url = "http://cn.bing.com/az/hprichbg/rb/AvalancheCreek_ROW11173354624_1920x1080.jpg";
Glide.with(this)
     .load(url)
     .override(500, 500)
     .centerCrop()
     .into(imageView);
Copy the code

As you can see, here we have set the image size to 500 by 500 pixels, so that the cropping ratio becomes 1:1. Now run the program again and it will look like the image below.

In this way, we have grasped the use of the picture transformation interface built into Glide. But have to say, Glide’s built-in image conversion interface function is very single and limited, there is no way to meet our usual development needs. Therefore, it is particularly important to master the function of custom picture transformation.

However, before we start learning about custom image transformations, let’s take a look at the source code of CenterCrop image transformations to make it more comfortable to perform custom image transformations.

Source code analysis

So without further ado, let’s go straight to the CenterCrop class and look at its source code, as shown below:

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 transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight); if (toReuse ! = null && toReuse ! = transformed && ! pool.put(toReuse)) { toReuse.recycle(); } return transformed; } @Override public String getId() { return "CenterCrop.com.bumptech.glide.load.resource.bitmap"; }}Copy the code

This code isn’t that long, but I’m going to highlight it so it’s easier for you to see.

First, CenterCrop inherits from BitmapTransformation, which is important because the entire image transformation function is based on this inheritance structure.

The next most important CenterCrop method is the transform() method, the others we can ignore for now. There are four parameters in the transform() method, each of which is important, so let’s look at each one. The first parameter pool, which is a Bitmap cache pool in Glide, is used to reuse Bitmap objects that would otherwise be very memory consuming to recreate each image transformation. The second parameter, toTransform, is the Bitmap of the original image that we are going toTransform. The third and fourth parameters are simpler, representing the width and height of the transformed image, respectively, which are the values passed in the Override () method.

Let’s look at the details of the transform() method. The first line attempts to retrieve a reusable Bitmap object from the Bitmap cache pool. Then he placed the object along with toTransform, outWidth, outHeight parameter passed to the TransformationUtils. CenterCrop () method. Let’s take a look at the source of this method, as follows:

public final class TransformationUtils { ... 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.p ostTranslate ((int) (dx + 0.5 f), (int) (dy + 0.5 f)); 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; }... }Copy the code

This code is the core of the image transformation function of the code. As can be seen, lines 5-9 mainly do some verification first. If the original image is empty, or the size of the original image is the same as the target clipping size, then the clipping is abandoned. Next lines 11-22 do the math to figure out the scale and offset of the canvas. Line 24-29 is to determine whether the Bitmap taken from the cache pool is empty. If it is not empty, it can be used directly. If it is empty, a new Bitmap should be created. Line 32 copies the alpha value of the original Bitmap to the cropped Bitmap. Finally, lines 34-37 trim the Bitmap object to draw and return the final result. That’s all the logic is, and it’s pretty simple in general, and maybe it’s just the math that needs a little bit of thinking.

So now that we have the cropped Bitmap, we’ll go back to CenterCrop and you’ll see that before we finally return the Bitmap, we’ll try to put the reused Bitmap back into the cache pool for further use.

Ok, so we have a complete analysis of how CenterCrop image transform works. FitCenter source code is basically similar, so we won’t repeat the analysis here. With that in mind, it’s time to start learning about custom image transformations.

Custom image transformation

Glide has customized a picture transformation framework for us. The general process is that we can get the original picture, and then transform the picture, and then return the transformed picture to Glide, and finally display the picture by Glide. In theory, you can do anything you want with the image transformation step. This can include rounded corners, circles, black and white, blurs, and even completely replacing the original image with another one.

But obviously I can’t show you all the possibilities of image transformation, and the possibilities of image transformation are infinite. So here we choose a common image transformation effect to customize – the image of the circular transformation.

The circular image function is now very common in mobile applications. For example, MOBILE QQ will transform the user’s profile picture into a circular shape to make the interface more beautiful.

The custom image transform function has a fairly fixed implementation logic, and we’ve just looked at the CenterCrop source code, so you have a basic understanding of the process. You simply define a class that inherits BitmapTransformation, override the transform() method, and implement the image transformation logic there. An empty image transform implementation would look something like this:

public class CircleCrop extends BitmapTransformation { public CircleCrop(Context context) { super(context); } public CircleCrop(BitmapPool bitmapPool) { super(bitmapPool); } @Override public String getId() { return "com.example.glidetest.CircleCrop"; } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { return null; }}Copy the code

One thing to note here is that the getId() method requires that a unique string be returned as the ID to distinguish it from other image transforms. Normally, we just return the full class name of the current class.

In addition, we have chosen to inherit BitmapTransformation with the restriction that only image transformations can be performed on static diagrams. Of course, this is enough to cover more than 95% of your daily development needs. If you have a special need to transform giFs, you’ll need to implement the Transformation interface yourself. But this is a very complicated one, and we’re not going to talk about it today.

Ok, so we continue to implement the circular transform function, then just need to do the specific logic implementation in the transform() method, the code is as follows:

public class CircleCrop extends BitmapTransformation { public CircleCrop(Context context) { super(context); } public CircleCrop(BitmapPool bitmapPool) { super(bitmapPool); } @Override public String getId() { return "com.example.glidetest.CircleCrop"; } @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { int diameter = Math.min(toTransform.getWidth(), toTransform.getHeight()); final Bitmap toReuse = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888); final Bitmap result; if (toReuse ! = null) { result = toReuse; } else { result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888); } int dx = (toTransform.getWidth() - diameter) / 2; int dy = (toTransform.getHeight() - diameter) / 2; Canvas canvas = new Canvas(result); Paint paint = new Paint(); BitmapShader shader = new BitmapShader(toTransform, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP); if (dx ! = 0 || dy ! = 0) { Matrix matrix = new Matrix(); matrix.setTranslate(-dx, -dy); shader.setLocalMatrix(matrix); } paint.setShader(shader); paint.setAntiAlias(true); float radius = diameter / 2f; canvas.drawCircle(radius, radius, radius, paint); if (toReuse ! = null && ! pool.put(toReuse)) { toReuse.recycle(); } return result; }}Copy the code

Here’s a quick explanation of the logic in the transform() method. First of all, in line 18, the smaller value in the width and height of the original picture should be calculated, because the circular transformation of the picture must take the smaller value as the diameter for clipping. Lines 20-26 try to retrieve a Bitmap object from the Bitmap cache pool for reuse as before, or create one if there are no reusable bitmaps. Lines 28-41 are the specific part of the circular transformation, where the offset value of the canvas is calculated, and the radius is calculated according to the diameter just obtained to draw a circle. Finally, it tries to put the reused Bitmap object back into the cache pool and return the circular transformed Bitmap object.

Now that we have a custom image transform, let’s try it out. To do this, it’s as simple as passing the custom image transform instance to the transform() method as shown below:

Glide.with(this)
     .load(url)
     .transform(new CircleCrop(this))
     .into(imageView);
Copy the code

Now let’s rerun the program. The result should look like the picture below.

More image transformation functions

Glide’s image transformation framework is already powerful enough to allow us to easily customize image transformation effects, but it would be hard to write every image transformation ourselves. In fact, there is no need to completely rely on their own to achieve a variety of image transformation effects, because most of the image transformation is relatively universal, each project will use the same effect, we each to re-implement their own is like repeating the wheel.

So you can make a lot of picture transformations on the Internet. You can make a lot of picture transformations on the Internet. You can make a lot of picture transformations on the Internet. It has realized a lot of general image transformation effects, such as cropping transformation, color transformation, fuzzy transformation and so on, so that we can be very easy to carry out a variety of image transformation.

Glide – Doubling projects homepage is github.com/wasabeef/gl… .

Let’s take a look at the power of this library. First we need to introduce this library into our project by adding the following dependencies to our app/build.gradle file:

Count {compile 'jp. Wasabeef :glide- double :2.0 '}Copy the code

Now if I want to blur the image, I can use the BlurTransformation class in the Glide – Doubling library as follows:

Glide.with(this)
     .load(url)
     .bitmapTransform(new BlurTransformation(this))
     .into(imageView);
Copy the code

Note that we call the bitmapTransform() method, not the transform() method, because glide- Transformations are designed specifically for static image transformations. Now run the degree again, as shown below.

That’s right, so we can blur it pretty easily.

Let’s try the image black and white again using the GrayscaleTransformation class as follows:

Glide.with(this)
     .load(url)
     .bitmapTransform(new GrayscaleTransformation(this))
     .into(imageView);
Copy the code

Now run the degree again, as shown below.

We can also combine multiple image transformations, such as blur and black and white at the same time:

Glide.with(this)
     .load(url)
     .bitmapTransform(new BlurTransformation(this), new GrayscaleTransformation(this))
     .into(imageView);
Copy the code

As you can see, when performing multiple image transformations at the same time, you simply pass them all into the bitmapTransform() method. Now run the program again. The result should look like the following image.

You can go to the GitHub homepage to learn more about transformations. You can make transformations easily and easily.

Ok, so today’s article here, I believe everyone’s harvest is a lot of it. In the next article, we will continue to explore Glide, learn about the function of the custom module, interested friends please continue to read Android picture loading framework most complete analysis (6), explore the function of the custom module Glide.

Pay attention to my technical public account “Guo Lin”, there are high-quality technical articles pushed every day. Pay attention to my entertainment public number, work, study tired when relax yourself.