--- /dev/null
+#include <algorithm>
+#include <IL/il.h>
+#include "devilloader.h"
+
+using namespace std;
+
+namespace {
+
+struct IOContext
+{
+ Msp::IO::Base *io;
+ char *buffer;
+ unsigned buf_fill;
+ unsigned buf_max;
+ unsigned position;
+};
+
+unsigned char eof(void *handle)
+{
+ return reinterpret_cast<IOContext *>(handle)->io->eof();
+}
+
+int get(void *handle)
+{
+ IOContext *ctx = reinterpret_cast<IOContext *>(handle);
+ if(ctx->position<ctx->buf_fill)
+ return ctx->buffer[ctx->position++];
+ else
+ {
+ char c = ctx->io->get();
+ if(ctx->position<ctx->buf_max)
+ ctx->buffer[ctx->position] = c;
+ ++ctx->position;
+ return c;
+ }
+}
+
+int read(void *buf, unsigned size, unsigned count, void *handle)
+{
+ IOContext *ctx = reinterpret_cast<IOContext *>(handle);
+ char *cbuf = reinterpret_cast<char *>(buf);
+ unsigned len = size*count;
+ unsigned ret = 0;
+
+ if(ctx->position<ctx->buf_fill)
+ {
+ unsigned copy_len = min(ctx->buf_fill-ctx->position, len);
+ copy(ctx->buffer+ctx->position, ctx->buffer+ctx->position+copy_len, cbuf);
+ ctx->position += copy_len;
+ ret += copy_len;
+ cbuf += copy_len;
+ len -= copy_len;
+ }
+
+ if(len)
+ {
+ unsigned read_len = ctx->io->read(cbuf, len);
+ if(ctx->position<ctx->buf_max)
+ {
+ unsigned copy_len = min(ctx->buf_max-ctx->position, read_len);
+ copy(cbuf, cbuf+copy_len, ctx->buffer+ctx->position);
+ ctx->buf_fill += copy_len;
+ }
+ ret += read_len;
+ ctx->position += read_len;
+ }
+
+ return ret;
+}
+
+int seek(void *handle, int offset, int type)
+{
+ IOContext *ctx = reinterpret_cast<IOContext *>(handle);
+
+ unsigned new_pos = ctx->position;
+ if(type==IL_SEEK_SET)
+ new_pos = offset;
+ else if(type==IL_SEEK_CUR)
+ new_pos += offset;
+ else
+ return -1;
+
+ if(new_pos>ctx->buf_fill)
+ new_pos = ctx->buf_fill;
+ ctx->position = new_pos;
+
+ return 0;
+}
+
+int tell(void *handle)
+{
+ return reinterpret_cast<IOContext *>(handle)->position;
+}
+
+} // namespace
+
+
+namespace Msp {
+namespace Graphics {
+
+DevilLoader::DevilLoader(IO::Base &i, const string &s):
+ io(i),
+ signature(s)
+{
+ static bool il_init_done = false;
+
+ if(!il_init_done)
+ {
+ ilInit();
+ ilEnable(IL_ORIGIN_SET);
+ ilOriginFunc(IL_ORIGIN_LOWER_LEFT);
+ il_init_done = true;
+ }
+
+ ilGenImages(1, &id);
+}
+
+DevilLoader::~DevilLoader()
+{
+ ilDeleteImages(1, &id);
+}
+
+bool DevilLoader::detect(const string &sig)
+{
+ unsigned type = ilDetermineTypeL(sig.data(), sig.size());
+ return type!=IL_TYPE_UNKNOWN;
+}
+
+void DevilLoader::load(Image::Data &data)
+{
+ IOContext ctx;
+ ctx.io = &io;
+ char buffer[4096];
+ ctx.buffer = buffer;
+ ctx.buf_max = sizeof(buffer);
+ copy(signature.begin(), signature.end(), buffer);
+ ctx.buf_fill = signature.size();
+ ctx.position = 0;
+
+ ilSetRead(0, 0, eof, get, read, seek, tell);
+ ilBindImage(id);
+ ilLoadF(IL_TYPE_UNKNOWN, &ctx);
+
+ switch(ilGetInteger(IL_IMAGE_FORMAT))
+ {
+ case IL_COLOR_INDEX: data.fmt = COLOR_INDEX; break;
+ case IL_LUMINANCE: data.fmt = LUMINANCE; break;
+ case IL_LUMINANCE_ALPHA: data.fmt = LUMINANCE_ALPHA; break;
+ case IL_RGB: data.fmt = RGB; break;
+ case IL_RGBA: data.fmt = RGBA; break;
+ case IL_BGR: data.fmt = BGR; break;
+ case IL_BGRA: data.fmt = BGRA; break;
+ default: throw unsupported_image_format("unknown pixel format");
+ }
+
+ data.width = ilGetInteger(IL_IMAGE_WIDTH);
+ data.height = ilGetInteger(IL_IMAGE_HEIGHT);
+ unsigned data_size = data.width*data.height*ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL);
+ data.data = new char[data_size];
+ ILubyte *il_data = ilGetData();
+ copy(il_data, il_data+data_size, data.data);
+
+ ilBindImage(0);
+ ilResetRead();
+}
+
+} // namespace Graphics
+} // namespace Msp