Issue
Fresco has built-in support for circular images and rounded corner, but what about other shapes such as diamond or parallelogram, etc?
It's simple to do with the standard ImageView via custom drawable that uses BitmapShader. For example, the following custom Drawable receives the image Bitmap and a slope height to make a an ImageView look like this picture:
public class MaskDrawable extends Drawable {
private Paint mPaint;
private Path mPath;
private int mSlopeHeight;
public MaskDrawable(Bitmap bitmap, int slopeHeight) {
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(shader);
mSlopeHeight = slopeHeight;
mPath = new Path();
}
@Override
public void draw(Canvas canvas) {
Rect bounds = getBounds();
mPath.moveTo(0, 0);
mPath.lineTo(0, bounds.bottom);
mPath.lineTo(bounds.right, bounds.bottom - mSlopeHeight);
mPath.lineTo(bounds.right, 0);
canvas.drawPath(mPath, mPaint);
}
To do that with Fresco, I need the Bitmap of the image, but I'm not sure how to do that. I read that I can get the Bitmap directly from the ImagePipeline, but that are many gotchas that comes with it. In one case, the returned Bitmap is short lived and shouldn't be used to draw on the screen where in the other case I get a CloseableReference which I need to release at some point which isn't clear to me. What I have seen on the net so far is code similar to this for getting the Bitmap:
ImagePipeline imagePipeline = Fresco.getImagePipeline();
ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(uri)
.setRequestPriority(Priority.HIGH)
.setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
.build();
DataSource<CloseableReference<CloseableBitmap>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, getContext());
DataSubscriber<CloseableReference<CloseableBitmap>> dataSubscriber =
new BaseDataSubscriber<CloseableReference<CloseableBitmap>>() {
@Override
protected void onNewResultImpl(DataSource<CloseableReference<CloseableBitmap>> dataSource) {
mBitmapRef = dataSource.getResult();
// Get the bitmap here and use it in my custom drawable?
}
@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableBitmap>> dataSource) {
}
};
dataSource.subscribe(dataSubscriber, UiThreadImmediateExecutorService.getInstance());
I haven't tried that yet and was wondering if somebody can provide a working solution instead of the bits and bytes I've gathered so far from different places. It has to be done right or else I can easily leak memory, which beats the whole idea of using Fresco from the first place.
Solution
You don't need to and also not recommend to use imagepipeline as you're dealing with view.
One way is to manage those bitmap in postprocessor. You need to override the process method, use the same BitmapShader, paint, canvas implementation, use PlatformBitmapFactory createBitmap to create purgeable bitmap CloseableReference, and finally close the reference when you're done with the bitmap.
See more in http://frescolib.org/docs/modifying-image.html
EDIT
Below is the final implementation I came up with after getting help from Jie Wang. The following code snippet places the image in the shape I presented in the question.
mSimpleDraweeView = (SimpleDraweeView) findViewById(R.id.shaped_picture);
final int slopeHeight = 100;
Postprocessor maskProcessor = new BasePostprocessor() {
@Override
public CloseableReference<Bitmap> process(Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory) {
// Get the size of the downloaded bitmap
final int width = sourceBitmap.getWidth();
final int height = sourceBitmap.getHeight();
// Create a new bitmap and use it to draw the shape that we want.
CloseableReference<Bitmap> bitmapRef = bitmapFactory.createBitmap(width, height);
try {
Bitmap destBitmap = bitmapRef.get();
// Create canvas using the new bitmap we created earlier
Canvas canvas = new Canvas(destBitmap);
// Set up the Paint we will use for filling in the shape
// BitmapShader will fill the shape with the downloaded bitmap
BitmapShader shader = new BitmapShader(sourceBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
// Set up the actual shape. Modify this part with any shape you want to have.
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(0, height);
path.lineTo(width, height - slopeHeight);
path.lineTo(width, 0);
// Draw the shape and fill it with the paint
canvas.drawPath(path, paint);
return CloseableReference.cloneOrNull(bitmapRef);
}
finally {
CloseableReference.closeSafely(bitmapRef);
}
}
};
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
.setPostprocessor(maskProcessor)
.build();
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setOldController(mSimpleDraweeView.getController())
.build();
mSimpleDraweeView.setController(controller);
Answered By - Jie Wang
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.