Cloudinary Blog

Easy Image Loading and Optimization with Cloudinary and Fresco

Easy Image Loading and Optimization with Cloudinary and Fresco

As mobile developers, when talking about images and videos, one of our main concerns is creating a smooth and amazing experience for our users, no matter what kind of device or network connection they are using. In this article, I’m going to show you how you can easily improve this experience using Cloudinary and Fresco.

In Android, working with images (bitmaps) is really difficult because the application runs out of memory (OOM) very frequently. OOM is the biggest nightmare for Android developers.

Leslie Knope

There are some well known open source libraries that can help us deal with such problems like Picasa, Glide, and Fresco.

Fresco (by Facebook) is my favorite. Fresco is written in C/C++. It uses ashmem heap instead of VM heap. Intermediate byte buffers are also stored in the native heap. This leaves a lot more memory available for applications to use and reduces the risk of OOMs. It also reduces the amount of garbage collection required, leading to better performance and a smoother experience in our app. Another cool thing is that Fresco supports multiple images (multi-URI), requesting different image qualities per-situation, which help us further improve the user experience in cases of poor connectivity for example.

Multiple Image (Multi-URI) Requests

Suppose you want to show your users a high-resolution, relatively slow-to-download image. Rather than let them stare at a placeholder or a loading spinner for a while, you might want to quickly download a smaller thumbnail first. With Fresco this can be done by setting two image URIs, one for the low-resolution image, and one for the high-resolution one:

Uri lowResUri, highResUri;
DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setLowResImageRequest(ImageRequest.fromUri(lowResUri))
                .setImageRequest(ImageRequest.fromUri(highResUri))
                .setOldController(mSimpleDraweeView.getController()).build();
mSimpleDraweeView.setController(controller);

But How I Can Generate Two Image Quality URIs?

Cloudinary’s fetch functionality enables on-the-fly manipulation of remote images and optimized delivery via a super fast CDN. It allows us to easily and dynamically generate different image quality versions, regardless of the location of image.

Let’s say this is my original image, stored in my AWS S3 bucket:

donut
https://s3.amazonaws.com/myappmedia/donut.png

You can see that this image’s size is almost 1MB. Loading many such images can sometimes harm your user’s experience while they are waiting for the image to fully load.

With Cloudinary, it’s super easy to fetch that image and generate both low and high-res image versions.

Fetching Remote Images With Cloudinary

Here’s the basic URL template for fetching any remote image with Cloudinary:

https://res.cloudinary.com/<cloud>/image/fetch/<transformations>/<remote_image_url>

Add Dynamic Transformations For Low Resolution

And here’s what the URL looks like when you add parameters that adjust the quality:

http://res.cloudinary.com/demo/image/fetch/f_webp,q_auto:low,w_400/https://s3.amazonaws.com/myappmedia/donut.png

This transformation converts the image (“donut”) to WebP, scales it down to a 400-pixel width, sets the quality to auto:low (an algorithm automatically does a quality vs. size trade-off where relatively low quality is considered acceptable). These transformations reduce the image size from nearly a megabyte to 2.37 KB (!)

Ruby:
cl_image_tag("https://s3.amazonaws.com/myappmedia/donut.png", :quality=>"auto:low", :width=>400, :fetch_format=>:auto, :crop=>"scale", :type=>"fetch")
PHP:
cl_image_tag("https://s3.amazonaws.com/myappmedia/donut.png", array("quality"=>"auto:low", "width"=>400, "fetch_format"=>"auto", "crop"=>"scale", "type"=>"fetch"))
Python:
CloudinaryImage("https://s3.amazonaws.com/myappmedia/donut.png").image(quality="auto:low", width=400, fetch_format="auto", crop="scale", type="fetch")
Node.js:
cloudinary.image("https://s3.amazonaws.com/myappmedia/donut.png", {quality: "auto:low", width: 400, fetch_format: "auto", crop: "scale", type: "fetch"})
Java:
cloudinary.url().transformation(new Transformation().quality("auto:low").width(400).fetchFormat("auto").crop("scale")).type("fetch").imageTag("https://s3.amazonaws.com/myappmedia/donut.png");
JS:
cloudinary.imageTag('https://s3.amazonaws.com/myappmedia/donut.png', {quality: "auto:low", width: 400, fetchFormat: "auto", crop: "scale", type: "fetch"}).toHtml();
jQuery:
$.cloudinary.image("https://s3.amazonaws.com/myappmedia/donut.png", {quality: "auto:low", width: 400, fetch_format: "auto", crop: "scale", type: "fetch"})
React:
<Image publicId="https://s3.amazonaws.com/myappmedia/donut.png" type="fetch">
  <Transformation quality="auto:low" width="400" fetchFormat="auto" crop="scale" />
</Image>
Angular:
<cl-image public-id="https://s3.amazonaws.com/myappmedia/donut.png" type="fetch">
  <cl-transformation quality="auto:low" width="400" fetch-format="auto" crop="scale">
  </cl-transformation>
</cl-image>
.Net:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Quality("auto:low").Width(400).FetchFormat("auto").Crop("scale")).Type("fetch").BuildImageTag("https://s3.amazonaws.com/myappmedia/donut.png")
Android:
MediaManager.get().url().transformation(new Transformation().quality("auto:low").width(400).fetchFormat("auto").crop("scale")).type("fetch").generate("https://s3.amazonaws.com/myappmedia/donut.png");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setType( "fetch").setTransformation(CLDTransformation().setQuality("auto:low").setWidth(400).setFetchFormat("auto").setCrop("scale")).generate("https://s3.amazonaws.com/myappmedia/donut.png")!, cloudinary: cloudinary)
donut

Note that in order to work with WebP, the only thing you need to do is add the webpsupport library to your dependencies, like described here.

Add Dynamic Transformations For High Resolution

It’s important to note that you can also dynamically optimize your high quality 1MB image in order to make it more ideal for Android device screen sizes. So for your high-resolution version you can just change the quality parameter to “auto:best” and leave the width as it was for the low resolution. This transformation would generate a nice looking, small sized image of 6.88 KB.

http://res.cloudinary.com/demo/image/fetch/f_webp,q_auto:best,w_400/https://s3.amazonaws.com/myappmedia/donut.png

Ruby:
cl_image_tag("https://s3.amazonaws.com/myappmedia/donut.png", :quality=>"auto:best", :width=>400, :crop=>"scale", :format=>"webp", :type=>"fetch")
PHP:
cl_image_tag("https://s3.amazonaws.com/myappmedia/donut.png", array("quality"=>"auto:best", "width"=>400, "crop"=>"scale", "format"=>"webp", "type"=>"fetch"))
Python:
CloudinaryImage("https://s3.amazonaws.com/myappmedia/donut.png").image(quality="auto:best", width=400, crop="scale", format="webp", type="fetch")
Node.js:
cloudinary.image("https://s3.amazonaws.com/myappmedia/donut.png", {quality: "auto:best", width: 400, crop: "scale", format: "webp", type: "fetch"})
Java:
cloudinary.url().transformation(new Transformation().quality("auto:best").width(400).crop("scale")).format("webp").type("fetch").imageTag("https://s3.amazonaws.com/myappmedia/donut.png");
JS:
cloudinary.imageTag('https://s3.amazonaws.com/myappmedia/donut.png', {quality: "auto:best", width: 400, crop: "scale", format: "webp", type: "fetch"}).toHtml();
jQuery:
$.cloudinary.image("https://s3.amazonaws.com/myappmedia/donut.png", {quality: "auto:best", width: 400, crop: "scale", format: "webp", type: "fetch"})
React:
<Image publicId="https://s3.amazonaws.com/myappmedia/donut.png" format="webp" type="fetch">
  <Transformation quality="auto:best" width="400" crop="scale" />
</Image>
Angular:
<cl-image public-id="https://s3.amazonaws.com/myappmedia/donut.png" format="webp" type="fetch">
  <cl-transformation quality="auto:best" width="400" crop="scale">
  </cl-transformation>
</cl-image>
.Net:
cloudinary.Api.UrlImgUp.Transform(new Transformation().Quality("auto:best").Width(400).Crop("scale")).Format("webp").Type("fetch").BuildImageTag("https://s3.amazonaws.com/myappmedia/donut.png")
Android:
MediaManager.get().url().transformation(new Transformation().quality("auto:best").width(400).crop("scale")).format("webp").type("fetch").generate("https://s3.amazonaws.com/myappmedia/donut.png");
iOS:
imageView.cldSetImage(cloudinary.createUrl().setFormat("webp").setType( "fetch").setTransformation(CLDTransformation().setQuality("auto:best").setWidth(400).setCrop("scale")).generate("https://s3.amazonaws.com/myappmedia/donut.png")!, cloudinary: cloudinary)
donut

To complete the example using Fresco, you just need to set those URLs for the low and high versions:

String originalImageURL = "https://s3.amazonaws.com/myappmedia/donut.png";
String lowResUri = "http://res.cloudinary.com/demo/image/fetch/f_webp,q_auto:low,w_400/e_blur:90/" + originalImageURL;
String highResUri = "http://res.cloudinary.com/demo/image/fetch/f_webp,q_auto:best,w_400/" + originalImageURL;
DraweeController controller = Fresco.newDraweeControllerBuilder() .setLowResImageRequest(ImageRequest.fromUri(Uri.parse(low_res_url))) .setImageRequest(ImageRequest.fromUri(Uri.parse(high_res_url))) .setOldController(mSimpleDraweeView.getController()).build();
mSimpleDraweeView.setController(controller);

Pretty easy, right?

Images and videos are the core component of any mobile app. Using both Cloudinary and Fresco can dramatically improve your Android users’ experience with a small effort from your side as developers.

Feel free to comment below if you have any questions about this post or any other media optimization related issues. In our next post we are going to talk about how to’ optimize video in your Android application, stay tuned!

Recent Blog Posts

Hipcamp Optimizes Images and Improves Page Load Times With Cloudinary

When creating a website that allows campers to discover great destinations, Hipcamp put a strong emphasis on featuring high-quality images that showcased the list of beautiful locations, regardless of whether users accessed the site on a desktop, tablet, or phone. Since 2015, Hipcamp has relied on Cloudinary’s image management solution to automate cropping and image optimization, enabling instant public delivery of photos, automatic tagging based on content recognition, and faster loading of webpages. In addition, Hipcamp was able to maintain the high standards it holds for the look and feel of its website.

Read more
New Image File Format: FUIF: Why Do We Need a New Image Format

In my last post, I introduced FUIF, a new, free, and universal image format I’ve created. In this post and other follow-up pieces, I will explain the why, what, and how of FUIF.

Even though JPEG is still the most widely-used image file format on the web, it has limitations, especially the subset of the format that has been implemented in browsers and that has, therefore, become the de facto standard. Because JPEG has a relatively verbose header, it cannot be used (at least not as is) for low-quality image placeholders (LQIP), for which you need a budget of a few hundred bytes. JPEG cannot encode alpha channels (transparency); it is restricted to 8 bits per channel; and its entropy coding is no longer state of the art. Also, JPEG is not fully “responsive by design.” There is no easy way to find a file’s truncation offsets and it is limited to a 1:8 downscale (the DC coefficients). If you want to use the same file for an 8K UHD display (7,680 pixels wide) and for a smart watch (320 pixels wide), 1:8 is not enough. And finally, JPEG does not work well with nonphotographic images and cannot do fully lossless compression.

Read more
 New Image File Format: FUIF:Lossy, Lossless, and Free

I've been working to create a new image format, which I'm calling FUIF, or Free Universal Image Format. That’s a rather pretentious name, I know. But I couldn’t call it the Free Lossy Image Format (FLIF) because that acronym is not available any more (see below) and FUIF can do lossless, too, so it wouldn’t be accurate either.

Read more
Optimizing Video Streaming and Delivery: Q&A with Doug Sillars

Doug Sillars, a digital nomad and a freelance mobile-performance expert, answers questions about video streaming and delivery, website optimization, and more.

Doug Sillars, a freelance mobile-performance expert and developer advocate, is a Google Developer Expert and the author of O’Reilly’s High Performance Android Apps. Given his extensive travels across the globe—from the UK to Siberia—with his wife, kids, and 11-year-old dog, Max, he has been referred to as a “digital nomad.” So far in 2018, Doug has spoken at more than 75 meetups and conferences!

Read more