diff --git a/src/image/codec/android-image.cpp b/src/image/codec/android-image.cpp
index f615a8fc..70ecc3a6 100644
--- a/src/image/codec/android-image.cpp
+++ b/src/image/codec/android-image.cpp
@@ -35,11 +35,11 @@ extern ANativeActivity *g_activity;
  * Image implementation class
  */
 
-DECLARE_IMAGE_LOADER(AndroidImageData, 100)
+DECLARE_IMAGE_CODEC(AndroidImageCodec, 100)
 {
 public:
-    virtual bool Open(char const *);
-    virtual bool Save(char const *);
+    virtual bool Load(Image *image, char const *path);
+    virtual bool Save(Image *image, char const *path);
     virtual bool Close();
 
     virtual uint8_t *GetData() const;
@@ -50,7 +50,7 @@ private:
     jint *pixels;
 };
 
-bool AndroidImageData::Open(char const *path)
+bool AndroidImageCodec::Load(Image *image, char const *path)
 {
     JNIEnv *env;
     jint res = g_activity->vm->GetEnv((void **)&env, JNI_VERSION_1_2);
@@ -109,14 +109,14 @@ bool AndroidImageData::Open(char const *path)
     return true;
 }
 
-bool AndroidImageData::Save(char const *path)
+bool AndroidImageCodec::Save(Image *image, char const *path)
 {
     UNUSED(path);
 
     /* TODO: unimplemented */
 }
 
-bool AndroidImageData::Close()
+bool AndroidImageCodec::Close()
 {
     JNIEnv *env;
     jint res = g_activity->vm->GetEnv((void **)&env, JNI_VERSION_1_2);
@@ -141,7 +141,7 @@ bool AndroidImageData::Close()
     return true;
 }
 
-uint8_t *AndroidImageData::GetData() const
+uint8_t *AndroidImageCodec::GetData() const
 {
     return (uint8_t *)pixels;
 }
diff --git a/src/image/codec/dummy-image.cpp b/src/image/codec/dummy-image.cpp
index ab3a98f4..e9ded0a2 100644
--- a/src/image/codec/dummy-image.cpp
+++ b/src/image/codec/dummy-image.cpp
@@ -24,61 +24,49 @@ namespace lol
  * Image implementation class
  */
 
-DECLARE_IMAGE_LOADER(DummyImageData, 0)
+DECLARE_IMAGE_CODEC(DummyImageCodec, 0)
 {
 public:
-    virtual bool Open(char const *);
-    virtual bool Save(char const *);
+    virtual bool Load(Image *image, char const *path);
+    virtual bool Save(Image *image, char const *path);
     virtual bool Close();
-
-    virtual uint8_t *GetData() const;
-
-private:
-    uint8_t *pixels;
 };
 
 /*
  * Public Image class
  */
 
-bool DummyImageData::Open(char const *path)
+bool DummyImageCodec::Load(Image *image, char const *path)
 {
     UNUSED(path);
 
-    m_size = ivec2(256);
-    m_format = PixelFormat::RGBA_8;
-    pixels = new uint8_t[256 * 256 * 4 * sizeof(*pixels)];
-    uint8_t *parser = pixels;
+    image->SetSize(ivec2(256));
+    u8vec4 *pixels = image->Lock<PixelFormat::RGBA_8>();
     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;
+            pixels->r = ((i ^ j) & 1) * 0xff;
+            pixels->g = (uint8_t)i;
+            pixels->b = (uint8_t)j;
+            pixels->a = (((i >> 4) ^ (j >> 4)) & 1) * 0xff;
+            ++pixels;
         }
+    image->Unlock();
 
     return true;
 }
 
-bool DummyImageData::Save(char const *path)
+bool DummyImageCodec::Save(Image *image, char const *path)
 {
     UNUSED(path);
 
     return true;
 }
 
-bool DummyImageData::Close()
+bool DummyImageCodec::Close()
 {
-    delete[] pixels;
-
     return true;
 }
 
-uint8_t * DummyImageData::GetData() const
-{
-    return pixels;
-}
-
 } /* namespace lol */
 
diff --git a/src/image/codec/gdiplus-image.cpp b/src/image/codec/gdiplus-image.cpp
index 18e3f0ce..97592c6d 100644
--- a/src/image/codec/gdiplus-image.cpp
+++ b/src/image/codec/gdiplus-image.cpp
@@ -31,11 +31,11 @@ namespace lol
  * Image implementation class
  */
 
-DECLARE_IMAGE_LOADER(GdiPlusImageData, 100)
+DECLARE_IMAGE_CODEC(GdiPlusImageCodec, 100)
 {
 public:
-    virtual bool Open(char const *);
-    virtual bool Save(char const *);
+    virtual bool Load(Image *image, char const *path);
+    virtual bool Save(Image *image, char const *path);
     virtual bool Close();
 
     virtual uint8_t *GetData() const;
@@ -49,7 +49,7 @@ private:
  * Public Image class
  */
 
-bool GdiPlusImageData::Open(char const *path)
+bool GdiPlusImageCodec::Load(Image *image, char const *path)
 {
     Gdiplus::Status status;
     ULONG_PTR token;
@@ -110,10 +110,11 @@ bool GdiPlusImageData::Open(char const *path)
         return false;
     }
 
-    m_size = ivec2(m_bitmap->GetWidth(), m_bitmap->GetHeight());
-    m_format = PixelFormat::RGBA_8;
+    image->m_data->m_size = ivec2(m_bitmap->GetWidth(),
+                                  m_bitmap->GetHeight());
+    image->m_data->m_format = PixelFormat::RGBA_8;
 
-    Gdiplus::Rect rect(0, 0, m_size.x, m_size.y);
+    Gdiplus::Rect rect(0, 0, image->m_data->m_size.x, image->m_data->m_size.y);
     if (m_bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead,
                            PixelFormat32bppARGB, &m_bdata) != Gdiplus::Ok)
     {
@@ -128,8 +129,8 @@ bool GdiPlusImageData::Open(char const *path)
      * know about ARGB, only RGBA. So we swap bytes. We could also fix
      * this in the shader. */
     uint8_t *p = static_cast<uint8_t *>(m_bdata.Scan0);
-    for (int y = 0; y < m_size.y; y++)
-        for (int x = 0; x < m_size.x; x++)
+    for (int y = 0; y < image->m_data->m_size.y; y++)
+        for (int x = 0; x < image->m_data->m_size.x; x++)
         {
             uint8_t tmp = p[2];
             p[2] = p[0];
@@ -140,7 +141,7 @@ bool GdiPlusImageData::Open(char const *path)
     return true;
 }
 
-bool GdiPlusImageData::Save(char const *path)
+bool GdiPlusImageCodec::Save(Image *image, char const *path)
 {
     ULONG_PTR token;
     Gdiplus::GdiplusStartupInput input;
@@ -192,11 +193,12 @@ bool GdiPlusImageData::Save(char const *path)
     /* FIXME: we create a new image because there is no guarantee that
      * the image comes from GDI. But the engine architecture doesn't
      * allow us to save other images anyway. */
-    Gdiplus::Bitmap *b = new Gdiplus::Bitmap(m_size.x, m_size.y,
+    Gdiplus::Bitmap *b = new Gdiplus::Bitmap(image->m_data->m_size.x,
+                                             image->m_data->m_size.y,
                                              PixelFormat32bppARGB);
 
     Gdiplus::BitmapData bdata;
-    Gdiplus::Rect rect(0, 0, m_size.x, m_size.y);
+    Gdiplus::Rect rect(0, 0, image->m_data->m_size.x, image->m_data->m_size.y);
 
     if (b->LockBits(&rect, (unsigned int)Gdiplus::ImageLockModeWrite,
                     PixelFormat32bppARGB, &bdata) != Gdiplus::Ok)
@@ -210,8 +212,8 @@ bool GdiPlusImageData::Save(char const *path)
 
     u8vec4 *psrc = static_cast<u8vec4 *>(m_bdata.Scan0);
     u8vec4 *pdst = static_cast<u8vec4 *>(bdata.Scan0);
-    for (int y = 0; y < m_size.y; y++)
-        for (int x = 0; x < m_size.x; x++)
+    for (int y = 0; y < image->m_data->m_size.y; y++)
+        for (int x = 0; x < image->m_data->m_size.x; x++)
         {
             *pdst++ = (*psrc++).bgra;
         }
@@ -233,7 +235,7 @@ bool GdiPlusImageData::Save(char const *path)
     return true;
 }
 
-bool GdiPlusImageData::Close()
+bool GdiPlusImageCodec::Close()
 {
     m_bitmap->UnlockBits(&m_bdata);
     delete m_bitmap;
@@ -241,7 +243,7 @@ bool GdiPlusImageData::Close()
     return true;
 }
 
-uint8_t * GdiPlusImageData::GetData() const
+uint8_t * GdiPlusImageCodec::GetData() const
 {
     return static_cast<uint8_t *>(m_bdata.Scan0);
 }
diff --git a/src/image/codec/ios-image.cpp b/src/image/codec/ios-image.cpp
index 0d6d4810..d052938d 100644
--- a/src/image/codec/ios-image.cpp
+++ b/src/image/codec/ios-image.cpp
@@ -28,11 +28,11 @@ namespace lol
  * Image implementation class
  */
 
-DECLARE_IMAGE_LOADER(IosImageData, 100)
+DECLARE_IMAGE_CODEC(IosImageCodec, 100)
 {
 public:
-    virtual bool Open(char const *);
-    virtual bool Save(char const *);
+    virtual bool Load(Image *image, char const *path);
+    virtual bool Save(Image *image, char const *path);
     virtual bool Close();
 
     virtual uint8_t *GetData() const;
@@ -45,7 +45,7 @@ private:
  * Public Image class
  */
 
-bool IosImageData::Open(char const *path)
+bool IosImageCodec::Load(Image *image, char const *path)
 {
     NSString *fullpath = [NSString stringWithUTF8String:path];
     NSArray *chunks = [fullpath componentsSeparatedByString: @"/"];
@@ -84,7 +84,7 @@ bool IosImageData::Open(char const *path)
     return true;
 }
 
-bool IosImageData::Save(char const *path)
+bool IosImageCodec::Save(Image *image, char const *path)
 {
     UNUSED(path);
 
@@ -92,14 +92,14 @@ bool IosImageData::Save(char const *path)
     return true;
 }
 
-bool IosImageData::Close()
+bool IosImageCodec::Close()
 {
     free(pixels);
 
     return true;
 }
 
-uint8_t * IosImageData::GetData() const
+uint8_t * IosImageCodec::GetData() const
 {
     return pixels;
 }
diff --git a/src/image/codec/ps3-image.cpp b/src/image/codec/ps3-image.cpp
index ac3cb322..55202691 100644
--- a/src/image/codec/ps3-image.cpp
+++ b/src/image/codec/ps3-image.cpp
@@ -30,11 +30,11 @@ namespace lol
  * Image implementation class
  */
 
-DECLARE_IMAGE_LOADER(Ps3ImageData, 100)
+DECLARE_IMAGE_CODEC(Ps3ImageCodec, 100)
 {
 public:
-    virtual bool Open(char const *);
-    virtual bool Save(char const *);
+    virtual bool Load(Image *image, char const *path);
+    virtual bool Save(Image *image, char const *path);
     virtual bool Close();
 
     virtual uint8_t *GetData() const;
@@ -49,7 +49,7 @@ private:
  * Public Image class
  */
 
-bool Ps3ImageData::Open(char const *path)
+bool Ps3ImageCodec::Load(Image *image, char const *path)
 {
     int32_t err;
 
@@ -74,9 +74,9 @@ bool Ps3ImageData::Open(char const *path)
     in_param.spuThreadEnable = CELL_PNGDEC_SPU_THREAD_ENABLE;
     in_param.ppuThreadPriority = 1000;
     in_param.spuThreadPriority = 200;
-    in_param.cbCtrlMallocFunc = Ps3ImageData::Malloc;
+    in_param.cbCtrlMallocFunc = Ps3ImageCodec::Malloc;
     in_param.cbCtrlMallocArg = nullptr;
-    in_param.cbCtrlFreeFunc = Ps3ImageData::Free;
+    in_param.cbCtrlFreeFunc = Ps3ImageCodec::Free;
     in_param.cbCtrlFreeArg = nullptr;
     CellPngDecThreadOutParam out_param;
     err = cellPngDecCreate(&hmain, &in_param, &out_param);
@@ -174,7 +174,7 @@ bool Ps3ImageData::Open(char const *path)
     return true;
 }
 
-bool Ps3ImageData::Open(char const *path)
+bool Ps3ImageCodec::Load(Image *image, char const *path)
 {
     UNUSED(path);
 
@@ -182,14 +182,14 @@ bool Ps3ImageData::Open(char const *path)
     return true;
 }
 
-bool Ps3ImageData::Close()
+bool Ps3ImageCodec::Close()
 {
     free(pixels);
 
     return true;
 }
 
-uint8_t * Ps3ImageData::GetData() const
+uint8_t * Ps3ImageCodec::GetData() const
 {
     return pixels;
 }
diff --git a/src/image/codec/sdl-image.cpp b/src/image/codec/sdl-image.cpp
index 640919db..7e732140 100644
--- a/src/image/codec/sdl-image.cpp
+++ b/src/image/codec/sdl-image.cpp
@@ -37,36 +37,34 @@ namespace lol
  * Image implementation class
  */
 
-DECLARE_IMAGE_LOADER(SdlImageData, 50)
+DECLARE_IMAGE_CODEC(SdlImageCodec, 50)
 {
 public:
-    virtual bool Open(char const *);
-    virtual bool Save(char const *);
+    virtual bool Load(Image *image, char const *path);
+    virtual bool Save(Image *image, char const *path);
     virtual bool Close();
 
-    virtual uint8_t *GetData() const;
-
     static SDL_Surface *Create32BppSurface(ivec2 size);
 
 private:
-    SDL_Surface *m_img;
+    SDL_Surface *m_surface;
 };
 
 /*
  * Public Image class
  */
 
-bool SdlImageData::Open(char const *path)
+bool SdlImageCodec::Load(Image *image, char const *path)
 {
     Array<String> pathlist = System::GetPathList(path);
     for (int i = 0; i < pathlist.Count(); i++)
     {
-        m_img = IMG_Load(pathlist[i].C());
-        if (m_img)
+        m_surface = IMG_Load(pathlist[i].C());
+        if (m_surface)
             break;
     }
 
-    if (!m_img)
+    if (!m_surface)
     {
 #if !LOL_BUILD_RELEASE
         Log::Error("could not load image %s\n", path);
@@ -74,42 +72,37 @@ bool SdlImageData::Open(char const *path)
         return false;
     }
 
-    m_size = ivec2(m_img->w, m_img->h);
-
-    if (m_img->format->BytesPerPixel != 4)
+    if (m_surface->format->BytesPerPixel != 4)
     {
-        SDL_Surface *tmp = Create32BppSurface(m_size);
-        SDL_BlitSurface(m_img, nullptr, tmp, nullptr);
-        SDL_FreeSurface(m_img);
-        m_img = tmp;
+        SDL_Surface *tmp = Create32BppSurface(image->GetSize());
+        SDL_BlitSurface(m_surface, nullptr, tmp, nullptr);
+        SDL_FreeSurface(m_surface);
+        m_surface = tmp;
     }
 
-    m_format = m_img->format->Amask ? PixelFormat::RGBA_8
-                                    : PixelFormat::RGB_8;
+    image->SetSize(ivec2(m_surface->w, m_surface->h));
+    u8vec4 *data = image->Lock<PixelFormat::RGBA_8>();
+    memcpy(data, m_surface->pixels, 4 * m_surface->w * m_surface->h);
+    image->Unlock();
 
     return true;
 }
 
-bool SdlImageData::Save(char const *path)
+bool SdlImageCodec::Save(Image *image, char const *path)
 {
-    int ret = SDL_SaveBMP(m_img, path);
+    int ret = SDL_SaveBMP(m_surface, path);
 
     return ret == 0;
 }
 
-bool SdlImageData::Close()
+bool SdlImageCodec::Close()
 {
-    SDL_FreeSurface(m_img);
+    SDL_FreeSurface(m_surface);
 
     return true;
 }
 
-uint8_t * SdlImageData::GetData() const
-{
-    return (uint8_t *)m_img->pixels;
-}
-
-SDL_Surface *SdlImageData::Create32BppSurface(ivec2 size)
+SDL_Surface *SdlImageCodec::Create32BppSurface(ivec2 size)
 {
     uint32_t rmask, gmask, bmask, amask;
 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
diff --git a/src/image/codec/zed-image.cpp b/src/image/codec/zed-image.cpp
index 45bc7107..0dc3bb77 100644
--- a/src/image/codec/zed-image.cpp
+++ b/src/image/codec/zed-image.cpp
@@ -25,11 +25,11 @@ namespace lol
  * Image implementation class
  */
 
-DECLARE_IMAGE_LOADER(ZedImageData, 0)
+DECLARE_IMAGE_CODEC(ZedImageCodec, 0)
 {
 public:
-    virtual bool Open(char const *);
-    virtual bool Save(char const *);
+    virtual bool Load(Image *image, char const *path);
+    virtual bool Save(Image *image, char const *path);
     virtual bool Close();
 
     virtual uint8_t *GetData() const;
@@ -51,7 +51,7 @@ private:
  * Public Image class
  */
 
-bool ZedImageData::Open(char const *path)
+bool ZedImageCodec::Load(Image *image, char const *path)
 {
     if (!lol::String(path).EndsWith(".RSC"))
         return false;
@@ -239,10 +239,8 @@ bool ZedImageData::Open(char const *path)
         tex_size <<= 1;
 
     //Prepare final imqge
-    m_size = ivec2(tex_size);
-    m_format = PixelFormat::Y_8;
-    m_pixels = new uint8_t[tex_size * tex_size * 1 * sizeof(*m_pixels)];
-    uint8_t *image = m_pixels;
+    image->SetSize(ivec2(tex_size));
+    uint8_t *pixels = image->Lock<PixelFormat::Y_8>();
 
     //Data refactor stage
     ivec2 pos = ivec2(0);
@@ -278,7 +276,7 @@ bool ZedImageData::Open(char const *path)
                         for (int x_cur = 0; x_cur < t_size.x / 4; x_cur++)
                         {
                             int32_t img_pos = img_off + pass + 4 * x_cur + y_cur * (int32_t)tex_size;
-                            image[img_pos] = file_convert[file_off++];
+                            pixels[img_pos] = file_convert[file_off++];
                         }
                     }
                 }
@@ -297,24 +295,24 @@ bool ZedImageData::Open(char const *path)
             j++;
         }
     }
+    image->Unlock();
+
     return true;
 }
 
-bool ZedImageData::Save(char const *path)
+bool ZedImageCodec::Save(Image *image, char const *path)
 {
     UNUSED(path);
     /* FIXME: do we need to implement this? */
     return true;
 }
 
-bool ZedImageData::Close()
+bool ZedImageCodec::Close()
 {
-    delete[] m_pixels;
-
     return true;
 }
 
-uint8_t * ZedImageData::GetData() const
+uint8_t * ZedImageCodec::GetData() const
 {
     return m_pixels;
 }
diff --git a/src/image/codec/zed-palette-image.cpp b/src/image/codec/zed-palette-image.cpp
index 64405713..f464fd0c 100644
--- a/src/image/codec/zed-palette-image.cpp
+++ b/src/image/codec/zed-palette-image.cpp
@@ -24,24 +24,19 @@ namespace lol
  * Image implementation class
  */
 
-DECLARE_IMAGE_LOADER(ZedPaletteImageData, 0)
+DECLARE_IMAGE_CODEC(ZedPaletteImageCodec, 0)
 {
 public:
-    virtual bool Open(char const *);
-    virtual bool Save(char const *);
+    virtual bool Load(Image *image, char const *path);
+    virtual bool Save(Image *image, char const *path);
     virtual bool Close();
-
-    virtual uint8_t *GetData() const;
-
-private:
-    uint8_t *m_pixels;
 };
 
 /*
  * Public Image class
  */
 
-bool ZedPaletteImageData::Open(char const *path)
+bool ZedPaletteImageCodec::Load(Image *image, char const *path)
 {
     if (!lol::String(path).EndsWith(".pal"))
         return false;
@@ -61,47 +56,40 @@ bool ZedPaletteImageData::Open(char const *path)
     int32_t tex_size = 2;
     while (tex_size < tex_sqrt)
         tex_size <<= 1;
-    m_size = ivec2(tex_size);
+    image->m_data->m_size = ivec2(tex_size);
 #else
     int32_t tex_sqrt = file_size / 3;
     int32_t tex_size = 2;
     while (tex_size < tex_sqrt)
         tex_size <<= 1;
-    m_size = ivec2(tex_size, 1);
+    image->SetSize(ivec2(tex_size, 1));
 #endif
 
-    m_format = PixelFormat::RGBA_8;
-    m_pixels = new uint8_t[tex_size * tex_size * 4 * sizeof(*m_pixels)];
-    uint8_t *parser = m_pixels;
+    u8vec4 *pixels = image->Lock<PixelFormat::RGBA_8>();
     for (int i = 0; i < file_buffer.Count();)
     {
-        *parser++ = file_buffer[i++];
-        *parser++ = file_buffer[i++];
-        *parser++ = file_buffer[i++];
-        *parser++ = (i == 0) ? 0 : 255;
+        pixels->r = file_buffer[i++];
+        pixels->g = file_buffer[i++];
+        pixels->b = file_buffer[i++];
+        pixels->a = (i == 0) ? 0 : 255;
+        ++pixels;
     }
+    image->Unlock();
 
     return true;
 }
 
-bool ZedPaletteImageData::Save(char const *path)
+bool ZedPaletteImageCodec::Save(Image *image, char const *path)
 {
     UNUSED(path);
     /* FIXME: do we need to implement this? */
     return true;
 }
 
-bool ZedPaletteImageData::Close()
+bool ZedPaletteImageCodec::Close()
 {
-    delete[] m_pixels;
-
     return true;
 }
 
-uint8_t * ZedPaletteImageData::GetData() const
-{
-    return m_pixels;
-}
-
 } /* namespace lol */
 
diff --git a/src/image/image-private.h b/src/image/image-private.h
index 236a0619..3fa2a25a 100644
--- a/src/image/image-private.h
+++ b/src/image/image-private.h
@@ -9,8 +9,8 @@
 //
 
 //
-// The ImageData class
-// -------------------
+// The ImageCodecData class
+// ------------------------
 //
 
 #if !defined __LOL_IMAGE_PRIVATE_H__
@@ -19,103 +19,114 @@
 namespace lol
 {
 
+class ImageData
+{
+    friend class Image;
+    friend class ImageBank;
+    friend class ImageCodec;
+
+public:
+    ImageData()
+      : m_size(0, 0),
+        m_format(PixelFormat::Unknown),
+        m_refcount(0),
+        m_codecdata(nullptr)
+    {}
+
+    ivec2 m_size;
+
+    Map<int, void *> m_pixels;
+    PixelFormat m_format;
+
+    int m_refcount;
+
+/* protected: */ /* FIXME: fix the ImageCodec subclasses access rights */
+    class ImageCodecData *m_codecdata;
+};
+
 class ImageCodec
 {
     friend class ImageBank;
-    friend class ImageData;
+    friend class ImageCodecData;
 
 public:
-    ImageData *(*fun)(char const *path);
-    ImageCodec *next;
-    int priority;
+    bool (*m_tryload)(Image *image, char const *path);
+    ImageCodec *m_next;
+    int m_priority;
 
-    static void RegisterLoader(ImageCodec *loader)
+    static void RegisterCodec(ImageCodec *codec)
     {
-        Helper(loader);
+        Helper(codec);
     }
 
 private:
-    static ImageData *Load(char const *path)
+    static void Load(Image *image, char const *path)
     {
-        ImageCodec *parser = Helper(nullptr);
-        ImageData *ret = nullptr;
+        ImageCodec *codec = Helper(nullptr);
+        bool success = false;
 
-        while (parser && !ret)
+        while (codec && !success)
         {
-            ret = parser->fun(path);
-            parser = parser->next;
+            success = codec->m_tryload(image, path);
+            codec = codec->m_next;
         }
-
-        return ret;
     }
 
     static ImageCodec *Helper(ImageCodec *set)
     {
-        static ImageCodec *loaders = nullptr;
+        static ImageCodec *codec_list = nullptr;
 
         if (!set)
-            return loaders;
+            return codec_list;
 
-        ImageCodec **parser = &loaders;
-        while (*parser && (*parser)->priority > set->priority)
-            parser = &(*parser)->next;
-        set->next = *parser;
-        *parser = set;
+        ImageCodec **codec = &codec_list;
+        while (*codec && (*codec)->m_priority > set->m_priority)
+            codec = &(*codec)->m_next;
+        set->m_next = *codec;
+        *codec = set;
 
         return nullptr;
     }
 };
 
-class ImageData
+/*
+ * Codec-specific data that is stored in some images
+ */
+class ImageCodecData
 {
     friend class Image;
     friend class ImageBank;
 
 public:
-    inline ImageData()
-      : m_size(0, 0),
-        m_format(PixelFormat::Unknown),
-        m_refcount(0)
-    { }
-
-    virtual ~ImageData() {}
+    inline ImageCodecData() {}
+    virtual ~ImageCodecData() {}
 
-    virtual bool Open(char const *) = 0;
-    virtual bool Save(char const *) = 0;
-    virtual bool Close() = 0;
+    virtual bool Load(Image *, char const *) = 0;
+    virtual bool Save(Image *, char const *) = 0;
 
-    virtual uint8_t *GetData() const = 0;
     virtual bool RetrieveTiles(Array<ivec2, ivec2>& tiles) { return false; }
-
-protected:
-    ivec2 m_size;
-    PixelFormat m_format;
-    int m_refcount;
 };
 
-#define REGISTER_IMAGE_LOADER(name) \
+#define REGISTER_IMAGE_CODEC(name) \
     extern void Register##name(); \
     Register##name();
 
-#define DECLARE_IMAGE_LOADER(name, prio) \
+#define DECLARE_IMAGE_CODEC(name, prio) \
     template<typename T> class name##ImageCodec : public ImageCodec \
     { \
     public: \
         name##ImageCodec() \
         { \
-            static ImageCodec loader; \
-            loader.fun = Load; \
-            loader.priority = prio; \
-            RegisterLoader(&loader); \
+            static ImageCodec codec; \
+            codec.m_tryload = Load; \
+            codec.m_priority = prio; \
+            RegisterCodec(&codec); \
         } \
-        static ImageData *Load(char const *path) \
+        static bool Load(Image *image, char const *path) \
         { \
-            T *ret = new T(); \
-            if (!ret->Open(path)) \
-            { \
-                delete ret; \
-                return nullptr; \
-            } \
+            T *codecdata = new T(); \
+            bool ret = codecdata->Load(image, path); \
+            delete codecdata; \
             return ret; \
         } \
     }; \
@@ -125,7 +136,7 @@ protected:
     { \
         (void)&name##ImageCodecInstance; \
     } \
-    class name : public ImageData
+    class name : public ImageCodecData
 
 } /* namespace lol */
 
diff --git a/src/image/image.cpp b/src/image/image.cpp
index 9dac7c27..c9e0d401 100644
--- a/src/image/image.cpp
+++ b/src/image/image.cpp
@@ -21,7 +21,7 @@ namespace lol
 {
 
 /* HACK: We cannot make this an ImageCodec member function, because the
- * REGISTER_IMAGE_LOADER macro forward-declares free functions from
+ * REGISTER_IMAGE_CODEC macro forward-declares free functions from
  * the "lol" namespace. An apparent bug in Visual Studio's compiler
  * makes it think these functions are actually in the top-level
  * namespace when the forward declaration is in a class member function.
@@ -30,26 +30,26 @@ namespace lol
  * The bug was reported to Microsoft and fixed by them, but the fix
  * is not yet available.
  * https://connect.microsoft.com/VisualStudio/feedback/details/730878/ */
-static bool RegisterAllLoaders()
+static bool RegisterAllCodecs()
 {
 #if defined __ANDROID__
-    REGISTER_IMAGE_LOADER(AndroidImageData)
+    REGISTER_IMAGE_CODEC(AndroidImageCodec)
 #endif
-    REGISTER_IMAGE_LOADER(DummyImageData)
+    REGISTER_IMAGE_CODEC(DummyImageCodec)
 #if defined USE_GDIPLUS
-    REGISTER_IMAGE_LOADER(GdiPlusImageData)
+    REGISTER_IMAGE_CODEC(GdiPlusImageCodec)
 #endif
 #if defined __APPLE__ && defined __MACH__ && defined __arm__
-    REGISTER_IMAGE_LOADER(IosImageData)
+    REGISTER_IMAGE_CODEC(IosImageCodec)
 #endif
 #if defined __CELLOS_LV2__
-    REGISTER_IMAGE_LOADER(Ps3ImageData)
+    REGISTER_IMAGE_CODEC(Ps3ImageCodec)
 #endif
 #if defined USE_SDL_IMAGE
-    REGISTER_IMAGE_LOADER(SdlImageData)
+    REGISTER_IMAGE_CODEC(SdlImageCodec)
 #endif
-    REGISTER_IMAGE_LOADER(ZedImageData)
-    REGISTER_IMAGE_LOADER(ZedPaletteImageData)
+    REGISTER_IMAGE_CODEC(ZedImageCodec)
+    REGISTER_IMAGE_CODEC(ZedPaletteImageCodec)
 
     return true;
 }
@@ -63,8 +63,6 @@ static class ImageBank
 public:
     void Init();
     Image *Create(char const *path);
-    bool Store(Image *img);
-    Image *Load(char const *path);
 
     void Unref(Image *img);
 
@@ -75,51 +73,27 @@ g_image_bank;
 
 void ImageBank::Init()
 {
-    /* Initialise loaders (see above) */
-    static bool init = RegisterAllLoaders();
+    /* Initialise codecs (see above) */
+    static bool init = RegisterAllCodecs();
     UNUSED(init);
 }
 
 Image *ImageBank::Create(char const *path)
 {
-    /* Is our image already in the bank? If so, no need to create it. */
-    Image *img;
+    Init();
 
-    if (m_images.HasKey(path))
-    {
-        img = m_images[path];
-    }
-    else
+    /* Is our image already in the bank? If so, no need to create it. */
+    if (!m_images.HasKey(path))
     {
-        img = Load(path);
-        m_images[path] = img;
+        m_images[path] = new Image();
+        ImageCodec::Load(m_images[path], path);
     }
 
+    Image *img = m_images[path];
     ++img->m_data->m_refcount;
     return img;
 }
 
-Image *ImageBank::Load(char const *path)
-{
-    Init();
-
-    Image *img = new Image(path);
-    img->m_data = ImageCodec::Load(img->GetPath().C());
-    return img;
-}
-
-bool ImageBank::Store(Image *img)
-{
-    /* Is our image already in the bank? If so, no need to create it. */
-    if (m_images.HasKey(img->GetPath().C()))
-        return false;
-
-    m_images[img->GetPath().C()] = img;
-
-    ++img->m_data->m_refcount;
-    return true;
-}
-
 void ImageBank::Unref(Image *img)
 {
     ASSERT(img->m_data->m_refcount > 0);
@@ -145,24 +119,19 @@ Image *Image::Create(char const *path)
     return g_image_bank.Create(path);
 }
 
-Image *Image::Load(char const *path)
-{
-    return g_image_bank.Load(path);
-}
-
-bool Image::Store(Image *img)
-{
-    return g_image_bank.Store(img);
-}
-
 /*
  * Public Image class
  */
 
-Image::Image(char const* path)
-  : m_data(nullptr)
+Image::Image()
+  : m_data(new ImageData())
 {
-    m_path = path;
+}
+
+Image::~Image()
+{
+    delete m_data->m_codecdata;
+    delete m_data;
 }
 
 void Image::Destroy()
@@ -172,7 +141,10 @@ void Image::Destroy()
 
 bool Image::Save(char const *path)
 {
-    return m_data->Save(path);
+    /* FIXME: add autoloading of save codecs */
+    if (!m_data->m_codecdata)
+        return false;
+    return m_data->m_codecdata->Save(this, path);
 }
 
 ivec2 Image::GetSize() const
@@ -180,30 +152,73 @@ ivec2 Image::GetSize() const
     return m_data->m_size;
 }
 
+void Image::SetSize(ivec2 size)
+{
+    if (m_data->m_size != size)
+    {
+        /* FIXME: delete data or resize it */
+        if (m_data->m_format != PixelFormat::Unknown)
+        {
+            m_data->m_pixels.Remove((int)m_data->m_format);
+        }
+    }
+    m_data->m_size = size;
+
+    /* FIXME: don’t do this! */
+    if (m_data->m_format != PixelFormat::Unknown)
+    {
+        Lock<PixelFormat::RGBA_8>();
+        Unlock();
+    }
+}
+
 PixelFormat Image::GetFormat() const
 {
     return m_data->m_format;
 }
 
-uint8_t *Image::GetData() const
+void *Image::LockGeneric()
 {
-    return m_data->GetData();
+    ASSERT(m_data->m_format != PixelFormat::Unknown);
+
+    return m_data->m_pixels[(int)m_data->m_format];
 }
 
-String Image::GetPath() const
+/* The Lock() method and its explicit specialisations */
+template<PixelFormat T>
+typename PixelType<T>::type *Image::Lock()
 {
-    return m_path;
+    /* TODO: convert data if this doesn’t match */
+    ASSERT(m_data->m_format == T || m_data->m_format == PixelFormat::Unknown);
+    m_data->m_format = (PixelFormat)T;
+
+    if (!m_data->m_pixels.HasKey((int)T))
+    {
+        m_data->m_pixels[(int)T] =
+          new typename PixelType<T>::type(m_data->m_size.x * m_data->m_size.y);
+    }
+
+    return (typename PixelType<T>::type *)m_data->m_pixels[(int)T];
 }
 
-bool Image::RetrieveTiles(Array<ivec2, ivec2>& tiles) const
+template typename PixelType<PixelFormat::Y_8>::type *
+Image::Lock<PixelFormat::Y_8>();
+template typename PixelType<PixelFormat::RGB_8>::type *
+Image::Lock<PixelFormat::RGB_8>();
+template typename PixelType<PixelFormat::RGBA_8>::type *
+Image::Lock<PixelFormat::RGBA_8>();
+
+void Image::Unlock()
 {
-    return m_data->RetrieveTiles(tiles);
+    /* TODO: ensure we are actually unlocking something we locked */
+    ASSERT(m_data->m_pixels.HasKey((int)m_data->m_format));
 }
 
-Image::~Image()
+bool Image::RetrieveTiles(Array<ivec2, ivec2>& tiles) const
 {
-    m_data->Close();
-    delete m_data;
+    if (!m_data->m_codecdata)
+        return false;
+    return m_data->m_codecdata->RetrieveTiles(tiles);
 }
 
 } /* namespace lol */
diff --git a/src/lol/gpu/texture.h b/src/lol/gpu/texture.h
index 906f3a34..ec2a3fa0 100644
--- a/src/lol/gpu/texture.h
+++ b/src/lol/gpu/texture.h
@@ -19,38 +19,30 @@
 namespace lol
 {
 
-struct PixelFormat
+enum class PixelFormat
 {
     /* XXX: make sure to update texture.cpp when this changes */
-    enum Value
-    {
-        Unknown = 0,
-        RGB_8,
-        RGBA_8,
-        ARGB_8,
-        ABGR_8,
-        Y_8,
-    }
-    m_value;
-
-    inline PixelFormat() : m_value(Unknown) {}
-    inline PixelFormat(Value v) : m_value(v) {}
-    inline operator Value() { return m_value; }
+    Unknown = 0,
+    RGB_8,
+    RGBA_8,
+    ARGB_8,
+    ABGR_8,
+    Y_8,
+};
 
-    inline uint8_t BytesPerPixel()
+static inline uint8_t BytesPerPixel(PixelFormat format)
+{
+    switch (format)
     {
-        switch (m_value)
-        {
-        case Y_8:
-            return 1;
-        case RGB_8:
-            return 3;
-        case RGBA_8:
-        case ARGB_8:
-        case ABGR_8:
-        default:
-            return 4;
-        }
+    case PixelFormat::Y_8:
+        return 1;
+    case PixelFormat::RGB_8:
+        return 3;
+    case PixelFormat::RGBA_8:
+    case PixelFormat::ARGB_8:
+    case PixelFormat::ABGR_8:
+    default:
+        return 4;
     }
 };
 
diff --git a/src/lol/image/image.h b/src/lol/image/image.h
index 8da761e0..a3a59891 100644
--- a/src/lol/image/image.h
+++ b/src/lol/image/image.h
@@ -21,31 +21,37 @@
 namespace lol
 {
 
+/* FIXME: is it possible to avoid the cast to int here? */
+template <PixelFormat T> struct PixelType { typedef void type; };
+template<> struct PixelType<PixelFormat::Y_8> { typedef uint8_t type; };
+template<> struct PixelType<PixelFormat::RGB_8> { typedef u8vec3 type; };
+template<> struct PixelType<PixelFormat::RGBA_8> { typedef u8vec4 type; };
+
 class Image
 {
     friend class ImageBank;
+    friend class ImageCodec;
 
 public:
-    //Create/Load/Store image into bank.    THREAD: NOT SAFE
-    static Image*   Create(char const *path);
-    //Create/Load image into bank.          THREAD: SAFE
-    static Image*   Load(char const *path);
-    //Store image into bank.                THREAD: NOT SAFE
-    static bool     Store(Image *img);
+    Image();
+    ~Image();
+
+    static Image *Create(char const *path);
 
     bool Save(char const *path);
     void Destroy();
 
-    ivec2 GetSize() const;
     PixelFormat GetFormat() const;
-    uint8_t *GetData() const;
-    String GetPath() const;
+    void *LockGeneric();
+    template<PixelFormat T> typename PixelType<T>::type *Lock();
+    void Unlock();
+
+    ivec2 GetSize() const;
+    void SetSize(ivec2);
+
     bool RetrieveTiles(Array<ivec2, ivec2>& tiles) const;
 
 private:
-    Image(char const* path);
-    ~Image();
-
     class ImageData *m_data;
     String m_path;
 };
diff --git a/src/tileset.cpp b/src/tileset.cpp
index 319bcdf0..58a8cd2a 100644
--- a/src/tileset.cpp
+++ b/src/tileset.cpp
@@ -202,12 +202,13 @@ void TileSet::TickDraw(float seconds)
     else if (m_data->m_image)
     {
         PixelFormat format = m_data->m_image->GetFormat();
-        int planes = format.BytesPerPixel();
+        int planes = BytesPerPixel(format);
 
         int w = m_data->m_texture_size.x;
         int h = m_data->m_texture_size.y;
 
-        uint8_t *pixels = m_data->m_image->GetData();
+        uint8_t *pixels = (uint8_t *)m_data->m_image->LockGeneric();
+        bool resized = false;
         if (w != m_data->m_image_size.x || h != m_data->m_image_size.y)
         {
             uint8_t *tmp = new uint8_t[planes * w * h];
@@ -216,12 +217,13 @@ void TileSet::TickDraw(float seconds)
                        pixels + planes * m_data->m_image_size.x * line,
                        planes * m_data->m_image_size.x);
             pixels = tmp;
+            resized = false;
         }
 
         m_data->m_texture = new Texture(ivec2(w, h), format);
         m_data->m_texture->SetData(pixels);
 
-        if (pixels != m_data->m_image->GetData())
+        if (resized)
             delete[] pixels;
         m_data->m_image->Destroy();
         m_data->m_image = nullptr;
diff --git a/test/unit/image.cpp b/test/unit/image.cpp
index 37557133..14e3f245 100644
--- a/test/unit/image.cpp
+++ b/test/unit/image.cpp
@@ -30,17 +30,18 @@ LOLUNIT_FIXTURE(ImageTest)
         LOLUNIT_ASSERT_EQUAL(size.x, 256);
         LOLUNIT_ASSERT_EQUAL(size.y, 16);
 
-        uint8_t *data = image->GetData();
+        u8vec4 *data = image->Lock<PixelFormat::RGBA_8>();
         LOLUNIT_ASSERT(data);
 
-        LOLUNIT_ASSERT_EQUAL((int)data[0], 0x00);
-        LOLUNIT_ASSERT_EQUAL((int)data[1], 0x00);
-        LOLUNIT_ASSERT_EQUAL((int)data[2], 0x00);
+        LOLUNIT_ASSERT_EQUAL((int)data[0].r, 0x00);
+        LOLUNIT_ASSERT_EQUAL((int)data[0].g, 0x00);
+        LOLUNIT_ASSERT_EQUAL((int)data[0].b, 0x00);
 
-        LOLUNIT_ASSERT_EQUAL((int)data[255 * 4 + 0], 0xff);
-        LOLUNIT_ASSERT_EQUAL((int)data[255 * 4 + 1], 0xff);
-        LOLUNIT_ASSERT_EQUAL((int)data[255 * 4 + 2], 0xff);
+        LOLUNIT_ASSERT_EQUAL((int)data[255].r, 0xff);
+        LOLUNIT_ASSERT_EQUAL((int)data[255].g, 0xff);
+        LOLUNIT_ASSERT_EQUAL((int)data[255].b, 0xff);
 
+        image->Unlock();
         image->Destroy();
     }
 };