Using WebP to Reduce Native iOS App Size

Last year Google released WebM as an alternative to h264 encoded video. They followed that up with the release of WebP as an alternative to JPG. Ever since the release I've been thinking about giving it a try on iOS to see how well it might work to reduce application size. As a bonus to reduced size, WebP also supports an alpha channel that JPG doesn't (there is more information available on the original release blog post).

To follow along you will need to grab the source for WebP and compile it. The easiest way to do that is to follow the official compiling WebP instructions. After compiling the source you will be able to encode WebP images, later I describe how to build the same source for use with iOS.

Before trying to build libwebp for iOS I did some testing to see just how much of a reduction I could achieve over JPG. I used ImageMagick to convert the source images into JPG and pushed the quality of the JPG down as far as seemed reasonable to reduce the resulting size. I tried a number of combinations but ended up using the following command to convert the JPGs:

convert -quality 75 -resize 1024x680 inputfile.png outputfile.jpg

Here are a couple samples produced using that command:

Egg Carton

Elephants

For WebP I made the quality 90 and encoded the same images with the following command:

cwebp -q 90 -resize 1024 680 inputfile.png -o outputfile.webp

Here are a couple example images produced using that command (at the time of this post these will only be visible using Chrome):

Egg Carton

Elephants

Even with the advantage I was giving the JPG images by compressing them with a lower quality I was able to get a 25% size reduction in the WebP version. Generally the JPG images would have been usable but some ended up with artifacts that would have required them to be re-encoded at a higher quality. The WebP versions ended up looking better in every case.

This was just my quick attempt to verify that it would be worth the effort of using WebP. A more in depth overview of JPG vs WebP can be found in the example gallery.

With that simple test out of the way I created a script that would compile the libwebp source into a framework compatible with the iOS simulators, iPad and iPhone:

SDK=4.3
PLATFORMS="iPhoneSimulator iPhoneOS-V6 iPhoneOS-V7"
DEVELOPER=/Developer
TOPDIR=`pwd`
BUILDDIR="$TOPDIR/tmp"
FINALDIR="$TOPDIR/WebP.framework"
LIBLIST=''

mkdir -p $BUILDDIR
mkdir -p $FINALDIR
mkdir $FINALDIR/Headers/

for PLATFORM in ${PLATFORMS}
do
  if [ "${PLATFORM}" == "iPhoneOS-V7" ]
  then
    PLATFORM="iPhoneOS"
    ARCH="armv7"
  elif [ "${PLATFORM}" == "iPhoneOS-V6" ]
  then
    PLATFORM="iPhoneOS"
    ARCH="armv6"
  else
    ARCH="i386"
  fi

  ROOTDIR="${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}"
  rm -rf "${ROOTDIR}"
  mkdir -p "${ROOTDIR}"

  export DEVROOT="${DEVELOPER}/Platforms/${PLATFORM}.platform/Developer"
  export SDKROOT="${DEVROOT}/SDKs/${PLATFORM}${SDK}.sdk"
  export CC=${DEVROOT}/usr/bin/gcc
  export LD=${DEVROOT}/usr/bin/ld
  export CPP=${DEVROOT}/usr/bin/cpp
  export CXX=${DEVROOT}/usr/bin/g++
  export AR=${DEVROOT}/usr/bin/ar
  export AS=${DEVROOT}/usr/bin/as
  export NM=${DEVROOT}/usr/bin/nm
  export CXXCPP=$DEVROOT/usr/bin/cpp
  export RANLIB=$DEVROOT/usr/bin/ranlib
  export LDFLAGS="-arch ${ARCH} -pipe -no-cpp-precomp -isysroot ${SDKROOT} -L${ROOTDIR}/lib"
  export CFLAGS="-arch ${ARCH} -pipe -no-cpp-precomp -isysroot ${SDKROOT} -I${ROOTDIR}/include"
  export CXXFLAGS="-arch ${ARCH} -pipe -no-cpp-precomp -isysroot ${SDKROOT} -I${ROOTDIR}/include"

  rm -rf libwebp-0.1.2
  tar xzf libwebp-0.1.2.tar.gz
  cd libwebp-0.1.2

  sh autogen.sh

  ./configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} --disable-shared --enable-static
  make
  make install

  LIBLIST="${LIBLIST} ${ROOTDIR}/lib/libwebp.a"
  cp -Rp ${ROOTDIR}/include/webp/* $FINALDIR/Headers/

  cd ..
done

${DEVROOT}/usr/bin/lipo -create $LIBLIST -output $FINALDIR/WebP

rm -rf libwebp-0.1.2
rm -rf ${BUILDDIR}

The script assumes that the source for libwebp is in the same directory as the script is executed from. If you skipped the first part of the post or want to make sure you are using the same version I used then use the following curl command:

curl http://webp.googlecode.com/files/libwebp-0.1.2.tar.gz > libwebp-0.1.2.tar.gz

After you run the script you will have a WebP.framework directory that represents the library. You can use this as a framework in XCode.

Using the library is easy. The following snipit shows how to get the current WebP library version, get the width and height of an image and then read the image into a buffer:

    // Get the current version of the WebP decoder
    int rc = WebPGetDecoderVersion();
    
    NSLog(@"Version: %d", rc);

    // Get the width and height of the selected WebP image
    int width = 0;
    int height = 0;
    WebPGetInfo([myData bytes], [myData length], &width, &height);
    
    NSLog(@"Image Width: %d Image Height: %d", width, height); 
    
    // Decode the WebP image data into a RGBA value array
    uint8_t *data = WebPDecodeRGBA([myData bytes], [myData length], &width, &height);

There is more documentation on the WebP API and I've put together a complete example on Github. If you grab the WebP iOS example from Github you will find the build script as well as the full example. First build the library from a command line then open the example project in XCode. If you don't first build the library before opening the project you will see that the WebP.framework is missing.

At this point you are on your way to having smaller iOS applications. There is at least one drawback at this point and that is speed. Decoding is a little slow and I think this is because the library isn't constructed to take advantage of any hardware acceleration. The next step is to see if it can be sped up by using the accelerate framework.

This entry was posted in programming and tagged ,

9 Comments

  1. The webp image quality of the eggs is awful compared to the jpeg version.

  2. Vu

    pretty cool and useful article. I love it. However, when I try to run the script, there was an error: "sh: autogen.sh: No such file or directory" although I installed Macports and there was autogen.sh at "/opt/local/var/macports/sources/rsync.macports.org/release/ports/net/pidgin/files". Could you please give me an advice?

  3. Yes, it turns out when you shrink the image down to a size like it is here it doesn't look nearly as good. If you look at the full size image it looks much better.

  4. I'm more worried by the colors shift differences between the 2 versions, it would have been great to provide the original PNG file to see who is wrong. Here is a diff between the 2 images, see how the eggs colors are affected: http://thomasmansencal.com/Sharing/Pictures/Others/Jpeg_WebP_Difference.png

  5. Byron

    please post the full res versions of each conversion. Also, did you compare webp vs a png run through pngcrunch (which Xcode does automatically).

    Additionally, did you calculate the overhead decoding webP format at runtime vs png? This could certainly negate any wins with compression quality in certain applications.

  6. Byron

    One more thing… with the combination on pngcrush and the fact that your ipa file is actually a zip file (and therefore compressed), it's hard to imagine that using a compression format jpg or otherwise would reduce your applications size. Regardless, these fills will all take up the same amount of memory because they need to be decompressed. The only real use for compression is when retrieving these images over a network or storing them on disk.

  7. dean

    There is a loss of fidelity in the full size webp images too, some differences in color as well but cant tell which method did that better without the originals

  8. Thanks for this great recipe, works great!

    I encountered the following issues, just in case anyone else stumbles upon these:

    1) The 5.0 iPhone OS had no 'cpp' binary, I just did 'ln -s llvm-cpp-4.2 cpp' to fix this (there has to be a better way :)

    2) The iPhoneSimulator build picked up jpeglib.h from my system's homebrew. I just uninstalled it (there has to better way :)

    configure: WARNING: jpeglib.h: present but cannot be compiled
    configure: WARNING: jpeglib.h: check for missing prerequisite headers?

    3) Example code for RGBA array -> UIImage:

    https://github.com/PaulSolt/UIImage-Conversion/blob/master/ImageHelper.m

  9. You can shrink size of PNGs too, see this case study.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>