diff --git a/src/Makefile.am b/src/Makefile.am
index e4f7f4f0..eb1b157a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,7 +24,12 @@ liblol_a_SOURCES = \
     \
     shader/shader.cpp shader/shader.h \
     \
-    image/image.cpp image/image.h \
+    image/image.cpp image/image.h image/image-private.h \
+    image/codec/android-image.cpp \
+    image/codec/ios-image.cpp \
+    image/codec/sdl-image.cpp \
+    image/codec/ps3-image.cpp \
+    image/codec/dummy-image.cpp \
     \
     loldebug.h \
     debug/fps.cpp debug/fps.h debug/sphere.cpp debug/sphere.h \
diff --git a/src/image/codec/android-image.cpp b/src/image/codec/android-image.cpp
new file mode 100644
index 00000000..b4b13bf9
--- /dev/null
+++ b/src/image/codec/android-image.cpp
@@ -0,0 +1,136 @@
+//
+// Lol Engine
+//
+// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the Do What The Fuck You Want To
+//   Public License, Version 2, as published by Sam Hocevar. See
+//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+//
+
+#if defined HAVE_CONFIG_H
+#   include "config.h"
+#endif
+
+#if defined __ANDROID__
+
+#include <cmath>
+
+#include <jni.h>
+#include <android/log.h>
+
+#include "core.h"
+#include "image-private.h"
+
+using namespace std;
+
+namespace lol
+{
+
+extern JavaVM *g_vm;
+extern jobject g_activity;
+
+/*
+ * Image implementation class
+ */
+
+DECLARE_IMAGE_LOADER(AndroidImageData, 100)
+{
+public:
+    virtual bool Open(char const *);
+    virtual bool Close();
+
+    virtual void *GetData() const;
+
+private:
+    jobject bmp;
+    jintArray array;
+    jint *pixels;
+};
+
+bool AndroidImageData::Open(char const *path)
+{
+    JNIEnv *env;
+    jint res = g_vm->GetEnv((void **)&env, JNI_VERSION_1_2);
+    if (res < 0)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not get JVM environment\n");
+#endif
+        exit(1);
+    }
+    jclass cls = env->GetObjectClass(g_activity);
+    jmethodID mid;
+
+    mid = env->GetMethodID(cls, "openImage",
+                           "(Ljava/lang/String;)Landroid/graphics/Bitmap;");
+    jstring name = env->NewStringUTF(path);
+    bmp = env->CallObjectMethod(g_activity, mid, name);
+    env->DeleteLocalRef(name);
+    if (!bmp)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not load %s\n", path);
+#endif
+        exit(1);
+    }
+    env->NewGlobalRef(bmp);
+
+    /* Get image dimensions */
+    mid = env->GetMethodID(cls, "getWidth", "(Landroid/graphics/Bitmap;)I");
+    size.x = env->CallIntMethod(g_activity, mid, bmp);
+    mid = env->GetMethodID(cls, "getHeight", "(Landroid/graphics/Bitmap;)I");
+    size.y = env->CallIntMethod(g_activity, mid, bmp);
+
+    /* Get pixels */
+    array = env->NewIntArray(size.x * size.y);
+    env->NewGlobalRef(array);
+    mid = env->GetMethodID(cls, "getPixels", "(Landroid/graphics/Bitmap;[I)V");
+    env->CallVoidMethod(g_activity, mid, bmp, array);
+
+    pixels = env->GetIntArrayElements(array, 0);
+    for (int n = 0; n < size.x * size.y; n++)
+    {
+        uint32_t u = pixels[n];
+        u = (u & 0xff00ff00) | ((u & 0xff0000) >> 16) | ((u & 0xff) << 16);
+        pixels[n] = u;
+    }
+    format = FORMAT_RGBA;
+
+    return true;
+}
+
+bool AndroidImageData::Close()
+{
+    JNIEnv *env;
+    jint res = g_vm->GetEnv((void **)&env, JNI_VERSION_1_2);
+    if (res < 0)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not get JVM environment\n");
+#endif
+        exit(1);
+    }
+    jclass cls = env->GetObjectClass(g_activity);
+    jmethodID mid;
+
+    env->ReleaseIntArrayElements(array, pixels, 0);
+    env->DeleteGlobalRef(array);
+
+    /* Free image */
+    mid = env->GetMethodID(cls, "closeImage", "(Landroid/graphics/Bitmap;)V");
+    env->CallVoidMethod(g_activity, mid, bmp);
+    env->DeleteGlobalRef(bmp);
+
+    return true;
+}
+
+void * AndroidImageData::GetData() const
+{
+    return pixels;
+}
+
+} /* namespace lol */
+
+#endif /* __ANDROID__ */
+
diff --git a/src/image/codec/dummy-image.cpp b/src/image/codec/dummy-image.cpp
new file mode 100644
index 00000000..3b8a9962
--- /dev/null
+++ b/src/image/codec/dummy-image.cpp
@@ -0,0 +1,76 @@
+//
+// Lol Engine
+//
+// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the Do What The Fuck You Want To
+//   Public License, Version 2, as published by Sam Hocevar. See
+//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+//
+
+#if defined HAVE_CONFIG_H
+#   include "config.h"
+#endif
+
+#include <cmath>
+
+#include "core.h"
+#include "image/image-private.h"
+
+using namespace std;
+
+namespace lol
+{
+
+/*
+ * Image implementation class
+ */
+
+DECLARE_IMAGE_LOADER(DummyImageData, 0)
+{
+public:
+    virtual bool Open(char const *);
+    virtual bool Close();
+
+    virtual void *GetData() const;
+
+private:
+    uint8_t *pixels;
+};
+
+/*
+ * Public Image class
+ */
+
+bool DummyImageData::Open(char const *path)
+{
+    size = 256;
+    format = Image::FORMAT_RGBA;
+    pixels = (uint8_t *)malloc(256 * 256 * 4 * sizeof(*pixels));
+    uint8_t *parser = pixels;
+    for (int j = 0; j < 256; j++)
+        for (int i = 0; i < 256; i++)
+        {
+            *parser++ = ((i ^ j) & 1) * 0xff;
+            *parser++ = (uint8_t)i;
+            *parser++ = (uint8_t)j;
+            *parser++ = (((i >> 4) ^ (j >> 4)) & 1) * 0xff;
+        }
+
+    return true;
+}
+
+bool DummyImageData::Close()
+{
+    free(pixels);
+
+    return true;
+}
+
+void * DummyImageData::GetData() const
+{
+    return pixels;
+}
+
+} /* namespace lol */
+
diff --git a/src/image/codec/ios-image.cpp b/src/image/codec/ios-image.cpp
new file mode 100644
index 00000000..b4d440b4
--- /dev/null
+++ b/src/image/codec/ios-image.cpp
@@ -0,0 +1,101 @@
+//
+// Lol Engine
+//
+// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the Do What The Fuck You Want To
+//   Public License, Version 2, as published by Sam Hocevar. See
+//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+//
+
+#if defined HAVE_CONFIG_H
+#   include "config.h"
+#endif
+
+#if defined __APPLE__ && defined __MACH__
+
+#include <cmath>
+#import <UIKit/UIKit.h>
+
+#include "core.h"
+
+using namespace std;
+
+namespace lol
+{
+
+/*
+ * Image implementation class
+ */
+
+DECLARE_IMAGE_LOADER(IosImageData, 100)
+{
+public:
+    virtual bool Open(char const *);
+    virtual bool Close();
+
+    virtual void *GetData() const;
+
+private:
+    uint8_t *pixels;
+};
+
+/*
+ * Public Image class
+ */
+
+bool IosImageData::Open(char const *path)
+{
+    NSString *fullpath = [NSString stringWithUTF8String:path];
+    NSArray *chunks = [fullpath componentsSeparatedByString: @"/"];
+    NSString *filename = [chunks objectAtIndex: [chunks count] - 1];
+    chunks = [filename componentsSeparatedByString: @"."];
+    NSString *prefix = [chunks objectAtIndex: 0];
+    NSString *mypath = [[NSBundle mainBundle] pathForResource:prefix ofType:@"png"];
+    NSData *pngdata = [[NSData alloc] initWithContentsOfFile:mypath];
+    UIImage *image = [[UIImage alloc] initWithData:pngdata];
+    if (!image)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not load %s\n", path);
+#endif
+        exit(1);
+    }
+
+    int w = CGImageGetWidth(image.CGImage);
+    int h = CGImageGetHeight(image.CGImage);
+    size = ivec2(w, h);
+    format = FORMAT_RGBA;
+
+    CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
+    pixels = (uint8_t *)malloc(w * h * 4);
+    CGContextRef ctx =
+            CGBitmapContextCreate(pixels, w, h, 8, 4 * w, cspace,
+                    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
+    CGColorSpaceRelease(cspace);
+    CGContextClearRect(ctx, CGRectMake(0, 0, w, h));
+    CGContextTranslateCTM(ctx, 0, h - h);
+    CGContextDrawImage(ctx, CGRectMake(0, 0, w, h), image.CGImage);
+    CGContextRelease(ctx);
+    [image release];
+    [pngdata release];
+
+    return true;
+}
+
+bool IosImageData::Close()
+{
+    free(pixels);
+
+    return true;
+}
+
+void * IosImageData::GetData() const
+{
+    return pixels;
+}
+
+} /* namespace lol */
+
+#endif /* defined __APPLE__ && defined __MACH__ */
+
diff --git a/src/image/codec/ps3-image.cpp b/src/image/codec/ps3-image.cpp
new file mode 100644
index 00000000..3ba5dc10
--- /dev/null
+++ b/src/image/codec/ps3-image.cpp
@@ -0,0 +1,203 @@
+//
+// Lol Engine
+//
+// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the Do What The Fuck You Want To
+//   Public License, Version 2, as published by Sam Hocevar. See
+//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+//
+
+#if defined HAVE_CONFIG_H
+#   include "config.h"
+#endif
+
+#if defined __CELLOS_LV2__
+
+#include <cmath>
+
+#include <cell/sysmodule.h>
+#include <cell/codec/pngdec.h>
+
+#include "core.h"
+
+using namespace std;
+
+namespace lol
+{
+
+/*
+ * Image implementation class
+ */
+
+DECLARE_IMAGE_LOADER(Ps3ImageData, 100)
+{
+public:
+    virtual bool Open(char const *);
+    virtual bool Close();
+
+    virtual void *GetData() const;
+
+private:
+    static void* Malloc(uint32_t size, void* data) { return malloc(size); };
+    static int32_t Free(void* ptr, void* data) { free(ptr); return 0; };
+    uint8_t *pixels;
+};
+
+/*
+ * Public Image class
+ */
+
+bool Ps3ImageData::Open(char const *path)
+{
+    int32_t err;
+
+    /* Initialise decoding library */
+    CellPngDecMainHandle hmain;
+
+    err = cellSysmoduleLoadModule(CELL_SYSMODULE_FS);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not open Fs sysmodule\n");
+#endif
+        exit(1);
+    }
+
+    err = cellSysmoduleLoadModule(CELL_SYSMODULE_PNGDEC);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not open PngDec sysmodule\n");
+#endif
+        exit(1);
+    }
+
+    CellPngDecThreadInParam in_param;
+    in_param.spuThreadEnable = CELL_PNGDEC_SPU_THREAD_ENABLE;
+    in_param.ppuThreadPriority = 1000;
+    in_param.spuThreadPriority = 200;
+    in_param.cbCtrlMallocFunc = ImageData::Malloc;
+    in_param.cbCtrlMallocArg = NULL;
+    in_param.cbCtrlFreeFunc = ImageData::Free;
+    in_param.cbCtrlFreeArg = NULL;
+    CellPngDecThreadOutParam out_param;
+    err = cellPngDecCreate(&hmain, &in_param, &out_param);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not create PngDec library\n");
+#endif
+        exit(1);
+    }
+
+    /* Create decoder */
+    CellPngDecSubHandle hsub;
+
+    char file[1024];
+    sprintf(file, "/app_home/c:/Users/s.hocevar/lolengine/%s", path);
+
+    CellPngDecSrc dec_src;
+    dec_src.srcSelect = CELL_PNGDEC_FILE;
+    dec_src.fileName = file;
+    dec_src.fileOffset = 0;
+    dec_src.fileSize = 0;
+    dec_src.streamPtr = NULL;
+    dec_src.streamSize = 0;
+    dec_src.spuThreadEnable  = CELL_PNGDEC_SPU_THREAD_ENABLE;
+    CellPngDecOpnInfo open_info;
+    err = cellPngDecOpen(hmain, &hsub, &dec_src, &open_info);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not open %s for decoding\n", file);
+#endif
+        exit(1);
+    }
+
+    CellPngDecInfo info;
+    err = cellPngDecReadHeader(hmain, hsub, &info);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not read image header\n");
+#endif
+        exit(1);
+    }
+
+    CellPngDecInParam in_dec_param;
+    in_dec_param.commandPtr = NULL;
+    in_dec_param.outputMode = CELL_PNGDEC_TOP_TO_BOTTOM;
+    in_dec_param.outputColorSpace = CELL_PNGDEC_RGBA;
+    in_dec_param.outputBitDepth = 8;
+    in_dec_param.outputPackFlag = CELL_PNGDEC_1BYTE_PER_1PIXEL;
+    in_dec_param.outputAlphaSelect = CELL_PNGDEC_STREAM_ALPHA;
+    in_dec_param.outputColorAlpha = 0xff;
+    CellPngDecOutParam out_dec_param;
+    err = cellPngDecSetParameter(hmain, hsub, &in_dec_param, &out_dec_param);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not configure PngDec decoder\n");
+#endif
+        exit(1);
+    }
+
+    /* Decode image */
+    size = ivec2(info.imageWidth, info.imageHeight);
+    format = FORMAT_RGBA;
+    pixels = (uint8_t *)malloc(info.imageWidth * 4 * info.imageHeight);
+    CellPngDecDataCtrlParam data_ctrl_param;
+    data_ctrl_param.outputBytesPerLine = info.imageWidth * 4;
+    CellPngDecDataOutInfo data_out_info;
+    err = cellPngDecDecodeData(hmain, hsub, pixels,
+                               &data_ctrl_param, &data_out_info);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not run PngDec decoder\n");
+#endif
+        exit(1);
+    }
+
+    /* Close decoder */
+    err = cellPngDecClose(hmain, hsub);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not close PngDec decoder\n");
+#endif
+        exit(1);
+    }
+
+    /* Deinitialise library */
+    err = cellPngDecDestroy(hmain);
+    if (err != CELL_OK)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not destroy PngDec decoder\n");
+#endif
+        exit(1);
+    }
+    err = cellSysmoduleUnloadModule(CELL_SYSMODULE_PNGDEC);
+    err = cellSysmoduleUnloadModule(CELL_SYSMODULE_FS);
+
+    return true;
+}
+
+bool Ps3ImageData::Close()
+{
+    free(pixels);
+
+    return true;
+}
+
+void * Ps3ImageData::GetData() const
+{
+    return pixels;
+}
+
+} /* namespace lol */
+
+#endif /* defined __CELLOS_LV2__ */
+
diff --git a/src/image/codec/sdl-image.cpp b/src/image/codec/sdl-image.cpp
new file mode 100644
index 00000000..63a7c6c2
--- /dev/null
+++ b/src/image/codec/sdl-image.cpp
@@ -0,0 +1,86 @@
+//
+// Lol Engine
+//
+// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the Do What The Fuck You Want To
+//   Public License, Version 2, as published by Sam Hocevar. See
+//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+//
+
+#if defined HAVE_CONFIG_H
+#   include "config.h"
+#endif
+
+#if defined USE_SDL_IMAGE
+
+#include <cmath>
+
+#include <SDL.h>
+#include <SDL_image.h>
+
+#include "core.h"
+#include "image/image-private.h"
+
+using namespace std;
+
+namespace lol
+{
+
+/*
+ * Image implementation class
+ */
+
+DECLARE_IMAGE_LOADER(SdlImageData, 50)
+{
+public:
+    virtual bool Open(char const *);
+    virtual bool Close();
+
+    virtual void *GetData() const;
+
+private:
+    SDL_Surface *img;
+};
+
+/*
+ * Public Image class
+ */
+
+bool SdlImageData::Open(char const *path)
+{
+    for (char const *name = path; *name; name++)
+        if ((img = IMG_Load(name)))
+            break;
+
+    if (!img)
+    {
+#if !LOL_RELEASE
+        Log::Error("could not load %s\n", path);
+#endif
+        SDL_Quit();
+        exit(1);
+    }
+
+    size = ivec2(img->w, img->h);
+    format = img->format->Amask ? Image::FORMAT_RGBA : Image::FORMAT_RGB;
+
+    return true;
+}
+
+bool SdlImageData::Close()
+{
+    SDL_FreeSurface(img);
+
+    return true;
+}
+
+void * SdlImageData::GetData() const
+{
+    return img->pixels;
+}
+
+} /* namespace lol */
+
+#endif /* defined USE_SDL_IMAGE */
+
diff --git a/src/image/image-private.h b/src/image/image-private.h
new file mode 100644
index 00000000..b03b1d72
--- /dev/null
+++ b/src/image/image-private.h
@@ -0,0 +1,127 @@
+//
+// Lol Engine
+//
+// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
+//   This program is free software; you can redistribute it and/or
+//   modify it under the terms of the Do What The Fuck You Want To
+//   Public License, Version 2, as published by Sam Hocevar. See
+//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+//
+
+//
+// The ImageData class
+// -------------------
+//
+
+#if !defined __LOL_IMAGE_PRIVATE_H__
+#define __LOL_IMAGE_PRIVATE_H__
+
+#include "image.h"
+
+namespace lol
+{
+
+class ImageLoader
+{
+    friend class Image;
+    friend class ImageData;
+
+public:
+    ImageData *(*fun)(char const *path);
+    ImageLoader *next;
+    int priority;
+
+    static bool RegisterAllLoaders();
+
+    static void RegisterLoader(ImageLoader *loader)
+    {
+        Helper(loader);
+    }
+
+private:
+    static ImageData *Load(char const *path)
+    {
+        ImageLoader *parser = Helper(NULL);
+        ImageData *ret = NULL;
+
+        while (parser && !ret)
+        {
+            ret = parser->fun(path);
+            parser = parser->next;
+        }
+
+        return ret;
+    }
+
+    static ImageLoader *Helper(ImageLoader *set)
+    {
+        static ImageLoader *loaders = NULL;
+
+        if (!set)
+            return loaders;
+
+        ImageLoader **parser = &loaders;
+        while (*parser && (*parser)->priority > set->priority)
+            parser = &(*parser)->next;
+        set->next = *parser;
+        *parser = set;
+
+        return NULL;
+    }
+};
+
+class ImageData
+{
+    friend class Image;
+
+public:
+    virtual bool Open(char const *) = 0;
+    virtual bool Close() = 0;
+
+    virtual void *GetData() const = 0;
+
+protected:
+    ivec2 size;
+    Image::format_t format;
+
+private:
+};
+
+#define REGISTER_IMAGE_LOADER(name) \
+    extern void (Register##name)(); \
+    Register##name();
+
+#define DECLARE_IMAGE_LOADER(name, prio) \
+    template<typename T> class name##ImageLoader : public ImageLoader \
+    { \
+    public: \
+        name##ImageLoader() \
+        { \
+            static ImageLoader loader; \
+            loader.fun = Load; \
+            loader.priority = prio; \
+            RegisterLoader(&loader); \
+        } \
+        static ImageData *Load(char const *path) \
+        { \
+            T *ret = new T(); \
+            if (!ret->Open(path)) \
+            { \
+                delete ret; \
+                return NULL; \
+            } \
+            return ret; \
+        } \
+    }; \
+    class name; \
+    name##ImageLoader<name> name##ImageLoaderInstance; \
+    void Register##name() \
+    { \
+        (void)&name##ImageLoaderInstance; \
+    } \
+    class name : public ImageData
+
+} /* namespace lol */
+
+#endif // __LOL_IMAGE_PRIVATE_H__
+
diff --git a/src/image/image.cpp b/src/image/image.cpp
index c55740e3..63a928d3 100644
--- a/src/image/image.cpp
+++ b/src/image/image.cpp
@@ -14,310 +14,42 @@
 
 #include <cmath>
 
-#if defined __APPLE__ && defined __MACH__
-#   import <UIKit/UIKit.h>
-#elif defined USE_SDL_IMAGE
-#   include <SDL.h>
-#   include <SDL_image.h>
-#elif defined __ANDROID__
-#   include <jni.h>
-#   include <android/log.h>
-#elif defined __CELLOS_LV2__
-#   include <cell/sysmodule.h>
-#   include <cell/codec/pngdec.h>
-#endif
-
 #include "core.h"
+#include "image-private.h"
 
 using namespace std;
 
 namespace lol
 {
 
+bool ImageLoader::RegisterAllLoaders()
+{
 #if defined __ANDROID__
-extern JavaVM *g_vm;
-extern jobject g_activity;
+    REGISTER_IMAGE_LOADER(AndroidImageData)
 #endif
-
-/*
- * Image implementation class
- */
-
-class ImageData
-{
-    friend class Image;
-
-private:
-    ivec2 size;
-    Image::format_t format;
-
+    REGISTER_IMAGE_LOADER(DummyImageData)
 #if defined __APPLE__ && defined __MACH__
-    uint8_t *pixels;
-#elif defined USE_SDL_IMAGE
-    SDL_Surface *img;
-#elif defined __ANDROID__
-    jobject bmp;
-    jintArray array;
-    jint *pixels;
-#elif defined __CELLOS_LV2__
-    static void* Malloc(uint32_t size, void* data) { return malloc(size); };
-    static int32_t Free(void* ptr, void* data) { free(ptr); return 0; };
-    uint8_t *pixels;
-#else
-    uint8_t *pixels;
+    REGISTER_IMAGE_LOADER(IosImageData)
 #endif
-};
+#if defined __CELLOS_LV2__
+    REGISTER_IMAGE_LOADER(Ps3ImageData)
+#endif
+#if defined USE_SDL_IMAGE
+    REGISTER_IMAGE_LOADER(SdlImageData)
+#endif
+
+    return true;
+}
 
 /*
  * Public Image class
  */
 
 Image::Image(char const *path)
-  : data(new ImageData())
 {
-#if defined __APPLE__ && defined __MACH__
-    NSString *fullpath = [NSString stringWithUTF8String:path];
-    NSArray *chunks = [fullpath componentsSeparatedByString: @"/"];
-    NSString *filename = [chunks objectAtIndex: [chunks count] - 1];
-    chunks = [filename componentsSeparatedByString: @"."];
-    NSString *prefix = [chunks objectAtIndex: 0];
-    NSString *mypath = [[NSBundle mainBundle] pathForResource:prefix ofType:@"png"];
-    NSData *pngdata = [[NSData alloc] initWithContentsOfFile:mypath];
-    UIImage *image = [[UIImage alloc] initWithData:pngdata];
-    if (!image)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not load %s\n", path);
-#endif
-        exit(1);
-    }
-
-    int w = CGImageGetWidth(image.CGImage);
-    int h = CGImageGetHeight(image.CGImage);
-    data->size = ivec2(w, h);
-    data->format = FORMAT_RGBA;
-
-    CGColorSpaceRef cspace = CGColorSpaceCreateDeviceRGB();
-    data->pixels = (uint8_t *)malloc(w * h * 4);
-    CGContextRef ctx =
-            CGBitmapContextCreate(data->pixels, w, h, 8, 4 * w, cspace,
-                    kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
-    CGColorSpaceRelease(cspace);
-    CGContextClearRect(ctx, CGRectMake(0, 0, w, h));
-    CGContextTranslateCTM(ctx, 0, h - h);
-    CGContextDrawImage(ctx, CGRectMake(0, 0, w, h), image.CGImage);
-    CGContextRelease(ctx);
-    [image release];
-    [pngdata release];
-#elif defined USE_SDL_IMAGE
-    for (char const *name = path; *name; name++)
-        if ((data->img = IMG_Load(name)))
-            break;
-
-    if (!data->img)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not load %s\n", path);
-#endif
-        SDL_Quit();
-        exit(1);
-    }
-
-    data->size = ivec2(data->img->w, data->img->h);
-    data->format = data->img->format->Amask ? FORMAT_RGBA : FORMAT_RGB;
-#elif defined __ANDROID__
-    JNIEnv *env;
-    jint res = g_vm->GetEnv((void **)&env, JNI_VERSION_1_2);
-    if (res < 0)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not get JVM environment\n");
-#endif
-        exit(1);
-    }
-    jclass cls = env->GetObjectClass(g_activity);
-    jmethodID mid;
-
-    mid = env->GetMethodID(cls, "openImage",
-                           "(Ljava/lang/String;)Landroid/graphics/Bitmap;");
-    jstring name = env->NewStringUTF(path);
-    data->bmp = env->CallObjectMethod(g_activity, mid, name);
-    env->DeleteLocalRef(name);
-    if (!data->bmp)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not load %s\n", path);
-#endif
-        exit(1);
-    }
-    env->NewGlobalRef(data->bmp);
-
-    /* Get image dimensions */
-    mid = env->GetMethodID(cls, "getWidth", "(Landroid/graphics/Bitmap;)I");
-    data->size.x = env->CallIntMethod(g_activity, mid, data->bmp);
-    mid = env->GetMethodID(cls, "getHeight", "(Landroid/graphics/Bitmap;)I");
-    data->size.y = env->CallIntMethod(g_activity, mid, data->bmp);
-
-    /* Get pixels */
-    data->array = env->NewIntArray(data->size.x * data->size.y);
-    env->NewGlobalRef(data->array);
-    mid = env->GetMethodID(cls, "getPixels", "(Landroid/graphics/Bitmap;[I)V");
-    env->CallVoidMethod(g_activity, mid, data->bmp, data->array);
-
-    data->pixels = env->GetIntArrayElements(data->array, 0);
-    for (int n = 0; n < data->size.x * data->size.y; n++)
-    {
-        uint32_t u = data->pixels[n];
-        u = (u & 0xff00ff00) | ((u & 0xff0000) >> 16) | ((u & 0xff) << 16);
-        data->pixels[n] = u;
-    }
-    data->format = FORMAT_RGBA;
-#elif defined __CELLOS_LV2__
-    int32_t err;
+    static bool unused = ImageLoader::RegisterAllLoaders();
 
-    /* Initialise decoding library */
-    CellPngDecMainHandle hmain;
-
-    err = cellSysmoduleLoadModule(CELL_SYSMODULE_FS);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not open Fs sysmodule\n");
-#endif
-        exit(1);
-    }
-
-    err = cellSysmoduleLoadModule(CELL_SYSMODULE_PNGDEC);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not open PngDec sysmodule\n");
-#endif
-        exit(1);
-    }
-
-    CellPngDecThreadInParam in_param;
-    in_param.spuThreadEnable = CELL_PNGDEC_SPU_THREAD_ENABLE;
-    in_param.ppuThreadPriority = 1000;
-    in_param.spuThreadPriority = 200;
-    in_param.cbCtrlMallocFunc = ImageData::Malloc;
-    in_param.cbCtrlMallocArg = NULL;
-    in_param.cbCtrlFreeFunc = ImageData::Free;
-    in_param.cbCtrlFreeArg = NULL;
-    CellPngDecThreadOutParam out_param;
-    err = cellPngDecCreate(&hmain, &in_param, &out_param);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not create PngDec library\n");
-#endif
-        exit(1);
-    }
-
-    /* Create decoder */
-    CellPngDecSubHandle hsub;
-
-    char file[1024];
-    sprintf(file, "/app_home/c:/Users/s.hocevar/lolengine/%s", path);
-
-    CellPngDecSrc dec_src;
-    dec_src.srcSelect = CELL_PNGDEC_FILE;
-    dec_src.fileName = file;
-    dec_src.fileOffset = 0;
-    dec_src.fileSize = 0;
-    dec_src.streamPtr = NULL;
-    dec_src.streamSize = 0;
-    dec_src.spuThreadEnable  = CELL_PNGDEC_SPU_THREAD_ENABLE;
-    CellPngDecOpnInfo open_info;
-    err = cellPngDecOpen(hmain, &hsub, &dec_src, &open_info);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not open %s for decoding\n", file);
-#endif
-        exit(1);
-    }
-
-    CellPngDecInfo info;
-    err = cellPngDecReadHeader(hmain, hsub, &info);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not read image header\n");
-#endif
-        exit(1);
-    }
-
-    CellPngDecInParam in_dec_param;
-    in_dec_param.commandPtr = NULL;
-    in_dec_param.outputMode = CELL_PNGDEC_TOP_TO_BOTTOM;
-    in_dec_param.outputColorSpace = CELL_PNGDEC_RGBA;
-    in_dec_param.outputBitDepth = 8;
-    in_dec_param.outputPackFlag = CELL_PNGDEC_1BYTE_PER_1PIXEL;
-    in_dec_param.outputAlphaSelect = CELL_PNGDEC_STREAM_ALPHA;
-    in_dec_param.outputColorAlpha = 0xff;
-    CellPngDecOutParam out_dec_param;
-    err = cellPngDecSetParameter(hmain, hsub, &in_dec_param, &out_dec_param);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not configure PngDec decoder\n");
-#endif
-        exit(1);
-    }
-
-    /* Decode image */
-    data->size = ivec2(info.imageWidth, info.imageHeight);
-    data->format = FORMAT_RGBA;
-    data->pixels = (uint8_t *)malloc(info.imageWidth * 4 * info.imageHeight);
-    CellPngDecDataCtrlParam data_ctrl_param;
-    data_ctrl_param.outputBytesPerLine = info.imageWidth * 4;
-    CellPngDecDataOutInfo data_out_info;
-    err = cellPngDecDecodeData(hmain, hsub, data->pixels,
-                               &data_ctrl_param, &data_out_info);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not run PngDec decoder\n");
-#endif
-        exit(1);
-    }
-
-    /* Close decoder */
-    err = cellPngDecClose(hmain, hsub);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not close PngDec decoder\n");
-#endif
-        exit(1);
-    }
-
-    /* Deinitialise library */
-    err = cellPngDecDestroy(hmain);
-    if (err != CELL_OK)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not destroy PngDec decoder\n");
-#endif
-        exit(1);
-    }
-    err = cellSysmoduleUnloadModule(CELL_SYSMODULE_PNGDEC);
-    err = cellSysmoduleUnloadModule(CELL_SYSMODULE_FS);
-#else
-    data->size = 256;
-    data->format = FORMAT_RGBA;
-    data->pixels = (uint8_t *)malloc(256 * 256 * 4 * sizeof(*data->pixels));
-    uint8_t *parser = data->pixels;
-    for (int j = 0; j < 256; j++)
-        for (int i = 0; i < 256; i++)
-        {
-            *parser++ = ((i ^ j) & 1) * 0xff;
-            *parser++ = (uint8_t)i;
-            *parser++ = (uint8_t)j;
-            *parser++ = (((i >> 4) ^ (j >> 4)) & 1) * 0xff;
-        }
-#endif
+    data = ImageLoader::Load(path);
 }
 
 ivec2 Image::GetSize() const
@@ -332,50 +64,12 @@ Image::format_t Image::GetFormat() const
 
 void * Image::GetData() const
 {
-#if defined __APPLE__ && defined __MACH__
-    return data->pixels;
-#elif defined USE_SDL_IMAGE
-    return data->img->pixels;
-#elif defined __ANDROID__
-    return data->pixels;
-#elif defined __CELLOS_LV2__
-    return data->pixels;
-#else
-    return data->pixels;
-#endif
+    return data->GetData();
 }
 
 Image::~Image()
 {
-#if defined __APPLE__ && defined __MACH__
-    free(data->pixels);
-#elif defined USE_SDL_IMAGE
-    SDL_FreeSurface(data->img);
-#elif defined __ANDROID__
-    JNIEnv *env;
-    jint res = g_vm->GetEnv((void **)&env, JNI_VERSION_1_2);
-    if (res < 0)
-    {
-#if !LOL_RELEASE
-        Log::Error("could not get JVM environment\n");
-#endif
-        exit(1);
-    }
-    jclass cls = env->GetObjectClass(g_activity);
-    jmethodID mid;
-
-    env->ReleaseIntArrayElements(data->array, data->pixels, 0);
-    env->DeleteGlobalRef(data->array);
-
-    /* Free image */
-    mid = env->GetMethodID(cls, "closeImage", "(Landroid/graphics/Bitmap;)V");
-    env->CallVoidMethod(g_activity, mid, data->bmp);
-    env->DeleteGlobalRef(data->bmp);
-#elif defined __CELLOS_LV2__
-    free(data->pixels);
-#else
-    free(data->pixels);
-#endif
+    data->Close();
     delete data;
 }