Android fresco lib , apply custom filter - android

i'm using fresco to display images to my app. Right now i'm trying to apply some filters to my images but the problem is that the filter library only results Bitmap. But the draweeView.setImageBitmap is deprecated.
I also tried with a post processor like this
MeshPostprocessor meshPostprocessor = new MeshPostprocessor();
meshPostprocessor.setFilter(filters.get(0));
draweeView = (SimpleDraweeView) view.findViewById(R.id.filter_image);
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(image)
.setPostprocessor(meshPostprocessor)
.setResizeOptions(new ResizeOptions(100, 100))
.build();
PipelineDraweeController controller = (PipelineDraweeController)
Fresco.newDraweeControllerBuilder()
.setImageRequest(request)
.setOldController(draweeView.getController())
.build();
draweeView.setController(controller);
and here is the PostProcessor
public static class MeshPostprocessor extends BaseRepeatedPostProcessor {
private AbstractConfig.ImageFilterInterface filter;
public void setFilter(AbstractConfig.ImageFilterInterface filter) {
this.filter = filter;
update();
}
#Override
public String getName() {
return "meshPostprocessor";
}
#Override
public void process(Bitmap bitmap) {
bitmap = filter.renderImage(bitmap);
}
}
so when I click on a filter i just run this
meshPostprocessor.setFilter(colorFilterConfig.get(position));
I tried with the debugger, the code goes through all the methods (setFilter , process etc..) but the image is not changing at all...
What am i missing?

I think you don't need a BaseRepeatedPostProcessor in your case.
A normal BasePostProcessor should be sufficient here.
However, the issue seems to be your custom filter:
#Override
public void process(Bitmap bitmap) {
bitmap = filter.renderImage(bitmap);
}
I suppose it returns a different Bitmap? This does not work in Java / for Fresco.
If your filter can do the processing in place, you can use process(Bitmap bitmap) and directly modify the given bitmap (e.g. bitmap.setPixel(...)).
If you cannot do it in place, you can override process(Bitmap destBitmap, Bitmap sourceBitmap) instead and modify destBitmap.
If your bitmap changes it's size, you can override CloseableReference<Bitmap> process(Bitmap sourceBitmap, PlatformBitmapFactory bitmapFactory). However, in this case make sure to actually use the provided bitmapFactory to create the new bitmap to be efficient.
For more information, take a look at http://frescolib.org/docs/modifying-image.html for more information or check out the JavaDoc for BasePostprocessor.

ok so the way I solved this is by adding a super call on the process like this
#Override
public void process(Bitmap dest, Bitmap source) {
Bitmap filtered = filter.renderImage(source, intensity);
super.process(dest, filtered);
}
i didn't noticed that you have to call super in order for the changes to have effect.

Related

Can I load images synchronously with Glide?

I know it isn't very practical to load bitmaps from the device storage synchronously, but I really have to do it. I haven't figured out any way to do this.
Yes is possible and is in glide documentation.
For example if you need to retrive the Bitmap synchronously you can do:
Glide V3:
Bitmap myBitmap = Glide.with(applicationContext)
.load(yourUrl)
.asBitmap()
.into(500, 500)
.get()
Glide v4:
FutureTarget<Bitmap> futureBitmap = Glide.with(applicationContext)
.asBitmap()
.load(yourURL))
.submit();
Bitmap myBitmap = futureBitmap.get();
Note: This code need to be run in the background or the app will crash.
Using interfaces to load data after image is loaded is a better option.
Create a new file called OnglideLoaded.java file like this:
import android.graphics.drawable.Drawable;
public interface OnGlideLoaded {
void onGlideLoaded(Drawable drawable);
}
Now create setter method for it like this where glide is:
public void setOnGlideLoaded(OnGlideLoaded onGlideLoaded) {
this.onGlideLoaded = onGlideLoaded;
}
Not call it inside Glide Target:
Glide.with(context)
.asBitmap()
.load(uri)
.into(new CustomTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
icon = resource;
if (onGlideLoaded!=null){
//This will send the image to every registered interface
onGlideLoaded.onGlideLoaded(icon);
}
}
#Override
public void onLoadCleared(#Nullable Drawable placeholder) {
}
});
Now call the interface like this where you want the image:
reference.setOnGlideLoaded(new OnGlideLoaded() {
#Override
public void onGlideLoaded(Drawable drawable) {
imageView.setImageBitmap(drawable);
}
});
Here, reference will be the object where you created the setter method.

Android + Volley: how to get image bitmap using ImageLoader?

I'm trying to get images for list view using Volley Library. I created simple HTTP helper with the following method.
/**
* Processing Image request and gets the image with given URL
*/
public Bitmap makeImageRequest(String url) {
ImageLoader il = new ImageLoader(queue, new BitmapLruCache());
il.get(url, new ImageLoader.ImageListener() {
#Override
public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
mBitmap = processImageResponse(response);
}
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(Constants.Global.ERROR, "Error: " + error.getMessage());
mBitmap = null;
}
});
return mBitmap;
}
But problem is that:
new BitmapLruCache()
Method is not recognized.
So i tried to create ImageLoader using the following code which i found on the URL:
http://www.androidhive.info/2014/05/android-working-with-volley-library-1/
ImageLoader imageLoader = AppController.getInstance().getImageLoader();
But in thos code i cannot find out where to get
AppController
Because the method code is triggered from the custom
public class HttpHelperClass
And called from the activity using the:
// Try to load remote image from URL
Bitmap bm = http.makeImageRequest("http://camranger.com/wp-content/uploads/2014/10/Android-Icon.png");
ImageView iv = (ImageView) findViewById(R.id.imageView);
iv.setImageBitmap(bm);
Is it the right approach how to load images and how can i repair my code to make succcessfull request?
Many thanks for any advice.
I think your makeImageRequest method will always return null because it takes some time for the onResponse or onErrorResponse listeners to get called but you return the mBitmap immidately!
If you want to use Volley's ImageLoader you'd better get image inside your activity or ... not from another class like your HttpHelperClass.
Also AppController is a class that extends Application and you should create it yourself. (It's in your AndroidHive link. Section 3. Creating Volley Singleton class)
And also for caching images you should not create a new ImageLoader everytime because in this way the caching gets meaningless. You should get that from your AppController class.
Furthermore I suggest you using Picasso because it's way better that Volley in image loading and a lot easier!
With Picasso you only need to call the following line to load an image from web into an ImageView:
Picasso.with(context).load(urlString).to(imageView);

Loading an ImageView from an URL in a widget

I'm trying to do a dynamic widget that has an ImageView. I'm trying to download an image from an URL using AsyncTask and then trying to apply the newly downloaded bitmap on to the ImageView, but nothing appears in the widget.
Here's the Java code going on in the widget:
public class MyWidget extends AppWidgetProvider
{
private static RemoteViews remoteViews;
private static String thumbnailUrl = "..."; // The image I'd like to show
// Async task for downloading an image and setting it to the ImageView
class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
{
#Override
protected Bitmap doInBackground(String... urls)
{
String url = urls[0];
Bitmap icon = null;
try
{
InputStream in = new java.net.URL(urldisplay).openStream();
icon = BitmapFactory.decodeStream(in);
}
catch(Exception e)
{
e.printStackTrace();
}
return icon;
}
#Override
protected void onPostExecute(Bitmap bitmap)
{
if(bitmap == null)
{
System.out.println("Bitmap is null!");
return;
}
remoteViews.setImageViewBitmap(R.id.thumbnailView, bitmap);
}
}
#Override
public void onUpdate(Context context, AppWidgetManager manager, int[] widgetIds)
{
if(remoteViews == null)
{
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
}
new DownloadImageTask().execute(thumbnailUrl);
}
} // End of MyWidget
The widget does work, because if I put a static TextView there, it shows up. However, this ImageView does not. The view's ID is correct, and the URL is valid. The bitmap is not null when onPostExecute() is called. Why doesn't the bitmap show up?
First, replace the AsyncTask with an IntentService. There is every possibility that your process will be terminated before you get your work done. Have your AppWidgetProvider delegate to the IntentService.
Second, use better code for downloading the image than what you have, as it will not deal well with typical mobile issues like sporadic connectivity. There are plenty of image-loading libraries available (Picasso, Universal Image Loader, etc.) that will be more resilient in the face of problems.
Third, you actually have to do something with the RemoteViews. Right now, you call setImageViewBitmap()... and then drop the RemoteViews on the floor. You need to use AppWidgetManager to tell Android to update the app widget with the RemoteViews.
And, as Ashish points out, bear in mind that there is a 1MB limit on IPC transactions, which will cap how big your Bitmap can be. Many of those image-loading libraries also have resampling logic built in to scale your image to an appropriate size, so you can aim to avoid this limit.
check you logcat output, are you getting something like -
ERROR/JavaBinder(20204): !!! FAILED BINDER TRANSACTION !!!
If yes, then you are hitting the file size limit :P

Parsing URI stored in local storage on device to ImageView

I'm creating the app where i have to display multiple URI's the user has chosen from their own images, currently i'm just trying to display one:
Current code:
(TouchImageView is just a library i'm used that works exactly like imageview)
TouchImageView img = (TouchImageView) findViewById(R.id.img);
img.setImageURI(Uri.parse("file://storage/emulated/0/DCIM/Camera/1411724483755.jpg"));
I have also tried parsing the URI to a bitmap but i can't get it to work.
You may not want to use setImageURI - as the documentation points out, this will decode the image on the UI thread, which is usually not a good idea.
Depending on the rest of the context, I'd suggest using an AsyncTask to load the file into a Bitmap on a separate thread, then after that is done, you can use ImageView.setImageBitmap on the UI thread.
So for example, something like this:
private class ImageLoadTask extends AsyncTask<Uri, Void, Bitmap> {
#Override
protected Bitmap doInBackground(Uri... uris) {
return BitmapFactory.decodeFile(uris[0].getPath());
}
#Override
protected void onPostExecute(Bitmap bmp) {
mImageView.setImageBitmap(bmp);
}
}
...
mImageView = (TouchImageView)findViewById(R.id.img);
new ImageLoadTask.execute(uri);
okay the answer was to parse it as a file and do a .toString()
This is the code that works:
TouchImageView img = (TouchImageView) findViewById(R.id.img);
img.setImageURI(Uri.parse((new File("file://storage/emulated/0/DCIM/Camer/1411724483755.jpg").toString())));

Caching Images - Bitmap not collected after different bitmap was loaded or ImageView's activity closed

I am trying to implement something that is very similar to UrlImageViewHelper (https://github.com/koush/UrlImageViewHelper) where you can easily using a simple one line of code, load images from a url, and if the image was already downloaded it is loaded from the cache instead. The major difference is that I want the same effect but instead of downloading it from a url, I want to receive the images from my own server using my own client-server communication. Every image on my server can be uniquely identified by a string, and I use this as the id for the image.
My main idea was this: Use an LRU Cache to hold the images, but instead of holding the Bitmaps (that are very large) I want to hold the raw image data binary, so I can use the same image to build bitmaps of different sizes and qualities on demand depending on the specific situation.
This is my implementation so far:
public class ImageHandler {
private static class BitmapCache extends LruCache<String, byte[]>
{
public WigoBitmapCache(int maxSize) {
super(maxSize);
}
#Override
protected int sizeOf(String key, byte[] value) {
return value.length;
}
}
private static class ImageHandlerThread extends Thread
{
/* THIS THREAD WILL DECODE THE IMAGE AND SET THE BITMAP TO THE IMAGEVIEW IN THE BACKGROUND */
Activity activity;
ImageView imageView;
byte[] imageBytes;
public ImageHandlerThread(Activity activity, ImageView imageView, byte[] imageBytes)
{
this.activity=activity;
this.imageView=imageView;
this.imageBytes=imageBytes;
}
public void run() {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o);
int factor1=o.outHeight/height;
int factor2=o.outWidth/width;
/* height and width are for now constant */
o = null;
o = new BitmapFactory.Options();
if (factor1>factor2)
o.inSampleSize=factor1;
else
o.inSampleSize=factor2;
Bitmap bit = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length,o);
setBitmap(bit);
bit = null;
}
private void setBitmap(final Bitmap bit) {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
imageView.setImageBitmap(bit);
}
});
}
}
private static class QueueItem
{ /*USED TO HOLD INFO ABOUT THE IMAGE REQUEST UNTIL THE IMAGE GETS FROM THE SERVER */
String imageName;
Activity activity;
ImageView imageView;
public QueueItem(String imageName, Activity activity, ImageView imageView)
{
this.imageName=imageName;
this.activity = activity;
this.imageView = imageView;
}
}
private BitmapCache cache; // this cache holds the image binaries
private ArrayList<QueueItem> queue; // this queue holds the info about the request, until the server sends the image
public ImageHandler(int maxSize)
{
cache=new BitmapCache(maxSize);
queue = new ArrayList<QueueItem>();
}
public synchronized void setBitmap(Activity activity, ImageView imageView, String imageName)
{
byte[] imageBytes = cache.get(imageName);
if (imageBytes==null)
{
QueueItem item = new QueueItem(imageName, activity, imageView);
queue.add(item);
/* HERE IS THE CODE TO RETRIEVE THE IMAGE BINARY FROM MY SERVER, THIS CODE WORKS FINE, SO THERE IS NO REASON TO BOHER YOU WITH IT */
}
else
{
ImageHandlerThread thread = new ImageHandlerThread(activity, imageView, imageBytes);
thread.start();
}
}
public synchronized void insert (String imageName, byte[] imageBytes)
{
/* THIS METHOD IS THE CALLBACK THAT IS CALLED WHEN THE IMAGE BINARY IS RECEIVED FROM THE SERVER */
cache.put(imageName, imageBytes);
for (QueueItem item: queue)
{
if (item.imageName.equals(imageName))
{
ImageHandlerThread thread = new ImageHandlerThread(item.activity, item.imageView, imageBytes);
thread.start();
queue.remove(item);
}
}
}
}
Basically, the main method here is setBitmap(), it gets the activity, the imageView that needs the bitmap, and the name of the image name. If the image is already in the cache, a new thread is started to decode the bytes into the proper size bitmap and set the bitmap to the imageView. If the image is not present in the cache, the request is put in a queue until the image is received, the image is retrieved from the server and then the same thread as before is started.
All this works absolutely fine, the problem is that when the imageView is set another bitmap for the image or even when the activity is destroyed, the bitmap is still resident in memory and is not collected by the GC.
At first I though that it is because I was keeping a reference to the activity, and that reference keeps the activity alive, but it seems not to be the case, my reference to an activity is very short lived, and once the image arrives from the server this reference is cleared.
I am running out of memory fast with this implementation, and I have no idea why or what to do to fix it. The bitmaps I create are not collected although I keep no references to them. Could this be an artifact of the way I decode the images? or do the threads keep references that are not collected properly? Anyone has any ideas?
Ok, so I misunderstood the problem, what was happening is that the GC was not collecting the finished thread objects because I was running this in debug mode, and not in run mode. When I ran this in run mode, the memory usage of my app did not go above 8MB at all, while when it was in debug mode I got to the 25MB+ area.
Conclusion: Do not trust the GC and memory usage info in debug mode, especially if you have many short term threads running.

Categories

Resources