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 a web page I did some testing to see just how easy is to handle wordpress hosting to add pictures and videos to blog posts. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *