A fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.
—
The Glide transformations system provides powerful image manipulation capabilities through a flexible and extensible architecture. Built around the BitmapTransformation base class, it includes comprehensive built-in transformations for common operations like cropping and scaling, plus a robust framework for creating custom transformations and combining multiple effects.
The foundation of Glide's transformation system is the abstract BitmapTransformation class, which provides the structure for all bitmap-based transformations.
public abstract class BitmapTransformation implements Transformation<Bitmap> {
/**
* Transforms the given bitmap
* @param pool BitmapPool for recycling bitmaps
* @param toTransform The bitmap to transform
* @param outWidth Desired output width
* @param outHeight Desired output height
* @return Transformed bitmap
*/
protected abstract Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight
);
/**
* Updates the cache key for this transformation
* @param messageDigest The digest to update
*/
@Override
public abstract void updateDiskCacheKey(MessageDigest messageDigest);
/**
* Transforms the resource
* @param context Application context
* @param resource The resource to transform
* @param outWidth Target width
* @param outHeight Target height
* @return Transformed resource
*/
@Override
public final Resource<Bitmap> transform(
Context context,
Resource<Bitmap> resource,
int outWidth,
int outHeight
);
}Essential cropping transformations for different scaling behaviors:
/**
* Centers and crops the image to fill the target dimensions
* Crops excess pixels from the larger dimension
*/
public class CenterCrop extends BitmapTransformation {
/**
* Gets singleton instance
*/
public static CenterCrop get() {
// Implementation returns singleton instance
}
}
/**
* Centers the image within target bounds without cropping
* Scales to fit completely within dimensions
*/
public class CenterInside extends BitmapTransformation {
public static CenterInside get() {
// Implementation returns singleton instance
}
}
/**
* Scales image to fit target dimensions exactly
* Centers the image and adds letterboxing if needed
*/
public class FitCenter extends BitmapTransformation {
public static FitCenter get() {
// Implementation returns singleton instance
}
}
/**
* Crops image into a perfect circle
* Centers and crops to square, then applies circular mask
*/
public class CircleCrop extends BitmapTransformation {
public static CircleCrop get() {
// Implementation returns singleton instance
}
}Specialized transformations for shape modifications:
/**
* Rounds the corners of the bitmap
*/
public class RoundedCorners extends BitmapTransformation {
private final int roundingRadius;
/**
* Creates rounded corners transformation
* @param roundingRadius Radius for rounding in pixels
*/
public RoundedCorners(int roundingRadius) {
this.roundingRadius = roundingRadius;
}
@Override
protected Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight
) {
// Implementation transforms bitmap with rounded corners
}
@Override
public boolean equals(Object other) {
// Implementation compares transformations
}
@Override
public int hashCode() {
// Implementation returns hash code
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
// Implementation updates cache key
}
}
/**
* Rotates the bitmap by specified degrees
*/
public class Rotate extends BitmapTransformation {
private final int degrees;
/**
* Creates rotation transformation
* @param degrees Degrees to rotate (multiple of 90)
*/
public Rotate(int degrees) {
this.degrees = degrees;
}
@Override
protected Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight
) {
// Implementation rotates bitmap
}
@Override
public boolean equals(Object other) {
// Implementation compares transformations
}
@Override
public int hashCode() {
// Implementation returns hash code
}
}Combine multiple transformations into a single operation for complex effects:
/**
* Applies multiple transformations in sequence
*/
public class MultiTransformation<T> implements Transformation<T> {
private final Transformation<T>[] transformations;
/**
* Creates multi-transformation from varargs
* @param transformations Transformations to combine
*/
@SafeVarargs
public MultiTransformation(Transformation<T>... transformations) {
this.transformations = transformations;
}
/**
* Creates multi-transformation from collection
* @param transformationList Collection of transformations
*/
public MultiTransformation(Collection<Transformation<T>> transformationList) {
this.transformations = transformationList.toArray(new Transformation[0]);
}
@Override
public Resource<T> transform(
Context context,
Resource<T> resource,
int outWidth,
int outHeight
) {
// Implementation applies transformations in sequence
}
@Override
public boolean equals(Object other) {
// Implementation compares transformations
}
@Override
public int hashCode() {
// Implementation returns hash code
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
// Implementation updates cache key
}
}Create custom transformations by extending BitmapTransformation:
public class GrayscaleTransformation extends BitmapTransformation {
@Override
protected Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight
) {
int width = toTransform.getWidth();
int height = toTransform.getHeight();
Bitmap bitmap = pool.get(width, height, Bitmap.Config.ARGB_8888);
bitmap.setHasAlpha(false);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0f);
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(toTransform, 0f, 0f, paint);
return bitmap;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
messageDigest.update("grayscale_transformation".getBytes(StandardCharsets.UTF_8));
}
@Override
public boolean equals(Object other) {
return other instanceof GrayscaleTransformation;
}
@Override
public int hashCode() {
return "grayscale_transformation".hashCode();
}
}Create transformations with configurable parameters:
public class BlurTransformation extends BitmapTransformation {
private final int radius;
private final int sampling;
public BlurTransformation() {
this(15, 1);
}
public BlurTransformation(int radius, int sampling) {
this.radius = radius;
this.sampling = sampling;
}
@Override
protected Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight
) {
int width = toTransform.getWidth();
int height = toTransform.getHeight();
int scaledWidth = width / sampling;
int scaledHeight = height / sampling;
Bitmap scaledBitmap = pool.get(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(scaledBitmap);
canvas.scale(1f / sampling, 1f / sampling);
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(toTransform, 0f, 0f, paint);
// Apply blur effect
Bitmap blurredBitmap = applyBlur(scaledBitmap, radius);
return Bitmap.createScaledBitmap(blurredBitmap, width, height, true);
}
private Bitmap applyBlur(Bitmap bitmap, int radius) {
// Implementation would use RenderScript or other blur algorithm
return bitmap;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
messageDigest.update(("blur_" + radius + sampling).getBytes(StandardCharsets.UTF_8));
}
@Override
public boolean equals(Object other) {
return other instanceof BlurTransformation &&
((BlurTransformation) other).radius == radius &&
((BlurTransformation) other).sampling == sampling;
}
@Override
public int hashCode() {
return Objects.hash("blur_transformation", radius, sampling);
}
}Apply individual transformations to image requests:
// Center crop transformation
Glide.with(context)
.load(imageUrl)
.centerCrop()
.into(imageView);
// Rounded corners
Glide.with(context)
.load(imageUrl)
.transform(new RoundedCorners(16))
.into(imageView);
// Circle crop
Glide.with(context)
.load(imageUrl)
.circleCrop()
.into(imageView);
// Custom transformation
Glide.with(context)
.load(imageUrl)
.transform(new GrayscaleTransformation())
.into(imageView);Combine multiple transformations for complex effects:
// Using MultiTransformation
Glide.with(context)
.load(imageUrl)
.transform(
new MultiTransformation<Bitmap>(
new CenterCrop(),
new RoundedCorners(20)
)
)
.into(imageView);
// Multiple custom transformations
Glide.with(context)
.load(imageUrl)
.transform(
new MultiTransformation<Bitmap>(
new CenterCrop(),
new BlurTransformation(25, 1),
new GrayscaleTransformation()
)
)
.into(imageView);
// Using varargs syntax
Glide.with(context)
.load(imageUrl)
.transform(
new CenterCrop(),
new RoundedCorners(12),
new GrayscaleTransformation()
)
.into(imageView);Apply transformations through RequestOptions for reusability:
RequestOptions thumbnailOptions = new RequestOptions()
.transform(
new MultiTransformation<Bitmap>(
new CenterCrop(),
new RoundedCorners(8)
)
)
.override(200, 200);
RequestOptions profileImageOptions = new RequestOptions()
.circleCrop()
.placeholder(R.drawable.default_avatar)
.error(R.drawable.avatar_error);
// Apply to multiple requests
Glide.with(context)
.load(thumbnailUrl)
.apply(thumbnailOptions)
.into(thumbnailView);
Glide.with(context)
.load(profileUrl)
.apply(profileImageOptions)
.into(profileView);Apply different transformations based on conditions:
public class ConditionalTransformationActivity extends AppCompatActivity {
public void loadImageWithConditionalTransform(String imageUrl, boolean isProfile) {
Transformation<Bitmap> transformation;
if (isProfile) {
transformation = new MultiTransformation<Bitmap>(
new CenterCrop(),
new CircleCrop()
);
} else {
transformation = new MultiTransformation<Bitmap>(
new CenterCrop(),
new RoundedCorners(12)
);
}
Glide.with(this)
.load(imageUrl)
.transform(transformation)
.into(imageView);
}
}Optimize performance with proper cache key implementation:
public class ColorFilterTransformation extends BitmapTransformation {
private final ColorMatrix colorMatrix;
private final String id;
public ColorFilterTransformation(ColorMatrix colorMatrix) {
this.colorMatrix = colorMatrix;
this.id = "color_filter_" + System.currentTimeMillis();
}
@Override
protected Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight
) {
Bitmap bitmap = pool.get(toTransform.getWidth(), toTransform.getHeight(), toTransform.getConfig());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(toTransform, 0f, 0f, paint);
return bitmap;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
messageDigest.update(id.getBytes(StandardCharsets.UTF_8));
}
@Override
public boolean equals(Object other) {
return other instanceof ColorFilterTransformation &&
((ColorFilterTransformation) other).colorMatrix.equals(colorMatrix);
}
@Override
public int hashCode() {
return Objects.hash(id, colorMatrix);
}
}Optimize transformations for memory and performance:
public class OptimizedRoundedCornersTransformation extends BitmapTransformation {
private final int radius;
// Cache paint and path objects to avoid allocation
private final Paint paint;
private final Path path;
public OptimizedRoundedCornersTransformation(int radius) {
this.radius = radius;
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
this.paint.setShader(null);
this.path = new Path();
}
@Override
protected Bitmap transform(
BitmapPool pool,
Bitmap toTransform,
int outWidth,
int outHeight
) {
int width = toTransform.getWidth();
int height = toTransform.getHeight();
Bitmap bitmap = pool.get(width, height, Bitmap.Config.ARGB_8888);
bitmap.setHasAlpha(true);
Canvas canvas = new Canvas(bitmap);
// Reuse path object
path.reset();
path.addRoundRect(
new RectF(0f, 0f, (float) width, (float) height),
(float) radius,
(float) radius,
Path.Direction.CW
);
canvas.clipPath(path);
canvas.drawBitmap(toTransform, 0f, 0f, paint);
return bitmap;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
messageDigest.update(("optimized_rounded_corners_" + radius).getBytes());
}
@Override
public boolean equals(Object other) {
return other instanceof OptimizedRoundedCornersTransformation &&
((OptimizedRoundedCornersTransformation) other).radius == radius;
}
@Override
public int hashCode() {
return Objects.hash("optimized_rounded_corners", radius);
}
}BitmapPool to recycle bitmapsupdateDiskCacheKey() for cache invalidationequals() and hashCode() for proper comparison// Reusable transformation options
public class TransformationPresets {
public static final RequestOptions PROFILE_IMAGE = new RequestOptions()
.circleCrop()
.placeholder(R.drawable.default_avatar)
.override(100, 100);
public static final RequestOptions THUMBNAIL = new RequestOptions()
.centerCrop()
.transform(new RoundedCorners(8))
.override(200, 200);
public static final RequestOptions BANNER = new RequestOptions()
.centerCrop()
.transform(new RoundedCorners(16));
}
// Apply transformations conditionally
public void loadWithStyle(String url, ImageStyle style) {
RequestOptions options;
switch (style) {
case PROFILE:
options = TransformationPresets.PROFILE_IMAGE;
break;
case THUMBNAIL:
options = TransformationPresets.THUMBNAIL;
break;
case BANNER:
options = TransformationPresets.BANNER;
break;
default:
options = new RequestOptions();
}
Glide.with(context)
.load(url)
.apply(options)
.into(imageView);
}This comprehensive transformation system allows for both simple built-in effects and complex custom transformations, providing the flexibility needed for any image manipulation scenario while maintaining optimal performance and memory management.
Install with Tessl CLI
npx tessl i tessl/maven-com-github-bumptech-glide--glide