In the hope that it might be useful for someone else out there, here are the steps that I use to build FreeImage for Android. These instructions were written for FreeImage 3.15.4 and NDK r8d. Using other versions of FreeImage or the NDK build system may require changes.

  1. Download the FreeImage source distribution.

  2. Setup the NDK project. Due to the size of FreeImage, it makes a lot of sense to compile it separately, rather than including it directly within your project. That's the approach taken here.

    Since ndk-build will attempt to compile code located within a folder named jni, that gives us the following structure:

    FreeImageAndroid
      -- jni
        -- Android.mk
        -- Application.mk
        -- [FreeImage source]
    

    Android.mk:

    LOCAL_PATH := $(call my-dir)
    
    include $(LOCAL_PATH)/Makefile.srcs
    
    include $(CLEAR_VARS)
    LOCAL_MODULE       := FreeImage
    LOCAL_C_INCLUDES   := $(LOCAL_PATH)/Source \
                            $(LOCAL_PATH)/Source/Metadata \
                            $(LOCAL_PATH)/Source/FreeImageToolkit \
                            $(LOCAL_PATH)/Source/LibJPEG \
                            $(LOCAL_PATH)/Source/LibPNG \
                            $(LOCAL_PATH)/Source/LibTIFF4 \
                            $(LOCAL_PATH)/Source/ZLib \
                            $(LOCAL_PATH)/Source/LibOpenJPEG \
                            $(LOCAL_PATH)/Source/OpenEXR \
                            $(LOCAL_PATH)/Source/OpenEXR/Half \
                            $(LOCAL_PATH)/Source/OpenEXR/Iex \
                            $(LOCAL_PATH)/Source/OpenEXR/IlmImf \
                            $(LOCAL_PATH)/Source/OpenEXR/IlmThread \
                            $(LOCAL_PATH)/Source/OpenEXR/Imath \
                            $(LOCAL_PATH)/Source/LibRawLite \
                            $(LOCAL_PATH)/Source/LibRawLite/dcraw \
                            $(LOCAL_PATH)/Source/LibRawLite/internal \
                            $(LOCAL_PATH)/Source/LibRawLite/libraw \
                            $(LOCAL_PATH)/Source/LibRawLite/src
    LOCAL_SRC_FILES    := $(SRCS)
    LOCAL_CPP_FEATURES := rtti exceptions
    LOCAL_CFLAGS       := -O3 -fPIC
    include $(BUILD_STATIC_LIBRARY)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE           := FreeImageShared
    LOCAL_STATIC_LIBRARIES := FreeImage
    include $(BUILD_SHARED_LIBRARY)

    The shared library is defined simply to make sure that the static library is built. Without it, the build system will infer that the static library isn't been used, and won't compile it.

    Application.mk:

    APP_STL   := gnustl_static
    APP_ABI   := all
    APP_OPTIM := release

    Pretty simple, the only thing you might want to change is the APP_ABI field, depending on the platform you want to target.

  3. Before you can compile the code successfully, there are a few minor tweaks you'll have to make:

    1. Since the NDK doesn't include search.h, you'll need to remove the following define from Source/LibTIFF4/tif_config.h:

      /* Define to 1 if you have the <search.h> header file. */
      #define HAVE_SEARCH_H 1

      Note that although the answer to this StackOverflow question states that you need to define lfind, that's not actually the case. In 2009, LibTIFF switched from using lfind to its own local equivalent. That renders the above configuration option unneccessary, since search.h is never used anywhere else.

    2. You'll also need to add a definition for the swab function in Source\LibRawLite\internal\dcraw_common.cpp, as the NDK doesn't provide one. You can find an example implementation here.

  4. Run ndk-build within the top-level FreeImageAndroid folder. At the start of the post, I mentioned that these steps are designed to work with NDK r8d. While NDK r8e is available, it has a bug that prevents it from compiling FreeImage successfully.

    Once the build has finished, you can extract libFreeImage.a from the obj folder and include it in your own project.