The ImagePreview library

Render previews with only ~200 bytes of image data

A library to render an image preview before requesting the actual image from the server. The only thing that you’ll need is ~200 bytes of unique data for each image (~25% of its total size).

https://www.youtube.com/watch?v=SC7o4cK5lWo

This library is inspired by this blog post post by Facebook about how they render the blurry previews of profile images. It looked like a fun thing to try, and if it might enhance the user experience, then it’s a good thing to implement.

Why should I use this?

  • Images are loaded asynchronously. It can take a couple of seconds for mobile devices with (bad) 3G to load the images.
  • The user might recognise the preview and can use this as a way to navigate in your app / site.
  • This looks better (in my opinion)

Where can I find it?

Currently we’ve only implemented this for C# and Swift.

Feel free to write your own implementation and share this with us so we can refer to your code.

How can I use this?

Let’s say we have the following use case.

  • Server with a JSON API
  • Client application (iOS) which needs to render a view based on server content.
  • Website (same platform as the API) which is rendered server side.

Step 1: create the preview (server)

Add the library to your project.

Install-Package Q42.ImagePreview

Get the ~200 bytes of data when the original image is created or modified.

var image = Image.FromFile("[path to your image]");
var body = ImagePreviewConverter.CreateImagePreview(image);

Store the body (byte[]) (probably in the same place you store the image information).

Step 2: send the information to the client

Because we have a JSON API, we can’t return the byte information directly. We’ll need to convert it to a Base64 String. Sadly, this means the data is increased from 197 to 264 bytes. If you want to reduce the amount of data send, take a look at a binary protocol, like MessagePack.

Convert.ToBase64String(body)

Now add the information to your API response.

{ 
  "items": [
    {
      "title": "First Object",
      "imageUrl": "[url to image]",
      "imagePreview": "AQAOAB7/2gAMAwEAAhEDEQA/AOt1HWtK0u5jtb/UI4pGj3cAttGO5GcZ5xVefSNAubdrmO9t1jU7TIjKVB64Pb8K5y20jTIm3rBJtZccsM9PXFTSaVps67Y4GTrk8Zz29vzFc0c4pxelzollkpLVIsvKlvKILbVLPG7KLvXJI5zx06ViPqkVzJhdWtwBk524B5+lXX8P6f8AL5QmXaMAkrkfjjNQroNmCRI0uTzlAgz/AOO1ss+orW34HP8A2NUf2vxP/9k="  
    }
  ]
}

Step 3: iOS, render the preview

Include the Cocoapod for your project

pod "ImagePreview"

Now create an UIImage.

let base64body = apiResponseItem.imagePreview // your response object

let image = NSData(base64EncodedString: base64body, options: [])
  .flatMap { UIImage(body: $0) }

The image is pretty ugly, but if we add a blur, it looks a lot nicer.

let blurredImage = image?.blurredImageWithRadius(0.4, tintColor: nil, saturationDeltaFactor: 1)

Take a look at the Swift example project for the complete implementation.

Step 4: website, server side template rendering

You can use the ImagePreviewConverter class to create the complete preview when you provide the body. And it’s easy to render the result.

<img src="denied:data:image/jpg;base64,@ImagePreviewConverter.Base64ImageFromBody(body)" width="300" alt="image">

Don’t forget to add the blur.

But how does it work?

Only 197 bytes are needed to render the example (body data), but the complete image is 802 in size. The remaining 605 bytes are in the client library (header data).

Let’s explain how it works. At first, we create a small image with a maximum size of 30x30. The following settings produced the most optimal result.

using (var graphics = Graphics.FromImage(image)) {    
  graphics.CompositingMode = CompositingMode.SourceCopy;        
  graphics.CompositingQuality = CompositingQuality.HighSpeed;
  graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  graphics.SmoothingMode = SmoothingMode.None;
  graphics.PixelOffsetMode = PixelOffsetMode.None;

We save the image with the default .NET JPEG codec at 70% quality.

Now we have an image, let’s find the overlapping parts. If we take a look at the JPEG structure and multiple preview images, you’ll notice that the parts up to the 0xFF,0xDA marker (also known as the Start Of Scan part of the image) are almost identical. 605 bytes match, The only difference is somewhere in the middle of those 605 bytes, the SOF0 part (Start Of Frame baseline).

The SOF0 part contains the image width and height. If you’ve read the Facebook post, you’ve probably noticed that Facebook implemented the preview with a fixed ratio (42x42). We don’t want that. We want our previews to be available in every possible aspect ratio.

To achieve this, we want to remove the width and height bytes from the header and add them to the body. Look up the 0xFF,0xC0 pattern, skip 5 bytes (start of the w/h bytes) and remove the 4 bytes of information from the header and add them to the body.

To prepare for future releases, we also add a 1 byte version number to the body.

The body can be stored on the server, the header can be stored in the client library.

On the client, you need to rebuild the byte array by reversing the process above.

It’s that easy.


Check out our Engineering Blog for more in depth articles on pragmatic code for happy users!