{
source "source/graphics";
source "source/input";
+ if_feature "libpng"
+ {
+ source "source/graphics/png";
+ };
+ if_feature "devil"
+ {
+ source "source/graphics/devil";
+ };
if_arch "windows"
{
overlay "windows";
--- /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
--- /dev/null
+#ifndef MSP_GRAPHICS_DEVILLOADER_H_
+#define MSP_GRAPHICS_DEVILLOADER_H_
+
+#include <msp/graphics/imageloader.h>
+
+namespace Msp {
+namespace Graphics {
+
+class DevilLoader: public ImageLoader
+{
+private:
+ IO::Base &io;
+ std::string signature;
+ unsigned id;
+
+public:
+ DevilLoader(IO::Base &, const std::string &);
+ virtual ~DevilLoader();
+
+ static bool detect(const std::string &);
+
+ virtual void load(Image::Data &);
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
-#ifdef WITH_DEVIL
-#include <IL/il.h>
-#endif
+#include <msp/core/refptr.h>
#include <msp/fs/utils.h>
#include <msp/io/file.h>
#include <msp/io/memory.h>
#include "image.h"
-#include "image_devil.h"
-#include "image_png.h"
-#include "image_private.h"
+#include "imageloader.h"
using namespace std;
namespace Msp {
namespace Graphics {
-Image::Private::Private()
-{
-#ifdef WITH_DEVIL
- id = 0;
-#endif
- fmt = RGB;
- width = 0;
- height = 0;
- data = 0;
-}
+Image::Data::Data():
+ fmt(RGB),
+ width(0),
+ height(0),
+ data(0)
+{ }
-
-Image::Image():
- priv(new Private)
+Image::Data::~Data()
{
-#if !defined(WITH_DEVIL) && !defined(WITH_LIBPNG)
- throw runtime_error("no image support");
-#endif
+ delete[] data;
}
-Image::~Image()
-{
-#ifdef WITH_DEVIL
- if(priv->id)
- ilDeleteImages(1, &priv->id);
- else
-#endif
- delete[] priv->data;
- delete priv;
-}
void Image::load_file(const string &fn)
{
- string ext = FS::extpart(fn);
-#ifdef WITH_LIBPNG
- if(ext==".png")
- {
- IO::BufferedFile file(fn);
- load_png(file, *priv, 0, 0);
- }
- else
-#endif
- {
-#ifdef WITH_DEVIL
- load_devil_file(fn, *priv);
-#else
- throw unsupported_image_format("DevIL needed for non-PNG images");
-#endif
- }
- (void)fn;
+ RefPtr<ImageLoader> loader = ImageLoader::open_file(fn);
+ load(*loader);
}
void Image::load_io(IO::Base &io)
{
- char sig_buf[8];
- unsigned sig_len = io.read(sig_buf, sizeof(sig_buf));
-#ifdef WITH_LIBPNG
- if(sig_len==sizeof(sig_buf) && is_png(sig_buf, sig_len))
- load_png(io, *priv, sig_buf, sig_len);
- else
-#endif
- {
-#ifdef WITH_DEVIL
- load_devil_io(io, *priv, sig_buf, sig_len);
-#else
- throw unsupported_image_format("DevIL needed for non-PNG images");
-#endif
- }
-}
-
-PixelFormat Image::get_format() const
-{
- return priv->fmt;
-}
-
-unsigned Image::get_width() const
-{
- return priv->width;
-}
-
-unsigned Image::get_height() const
-{
- return priv->height;
+ RefPtr<ImageLoader> loader = ImageLoader::open_io(io);
+ load(*loader);
}
-const void *Image::get_data() const
+void Image::load(ImageLoader &loader)
{
- return priv->data;
+ loader.load(data);
}
} // namespace Graphics
namespace Msp {
namespace Graphics {
-class unsupported_image_format: public std::runtime_error
-{
-public:
- unsupported_image_format(const std::string &w): std::runtime_error(w) { }
- virtual ~unsupported_image_format() throw() { }
-};
-
-class bad_image_data: public std::runtime_error
-{
-public:
- bad_image_data(const std::string &w): std::runtime_error(w) { }
- virtual ~bad_image_data() throw() { }
-};
-
+class ImageLoader;
class Image
{
public:
- struct Private;
+ struct Data
+ {
+ PixelFormat fmt;
+ unsigned width;
+ unsigned height;
+ char *data;
+
+ Data();
+ ~Data();
+ };
private:
- Private *priv;
+ Data data;
public:
- Image();
- ~Image();
-
void load_file(const std::string &);
void load_io(IO::Base &);
- PixelFormat get_format() const;
- unsigned get_width() const;
- unsigned get_height() const;
- const void *get_data() const;
+ void load(ImageLoader &);
+
+ PixelFormat get_format() const { return data.fmt; }
+ unsigned get_width() const { return data.width; }
+ unsigned get_height() const { return data.height; }
+ const void *get_data() const { return data.data; }
};
} // namespace Graphics
+++ /dev/null
-#ifdef WITH_DEVIL
-#include <IL/il.h>
-#include "image_devil.h"
-#include "image_private.h"
-
-using namespace std;
-
-namespace {
-
-using namespace Msp::Graphics;
-
-void init_load(Image::Private &priv)
-{
- static bool il_init_done = false;
-
- if(!il_init_done)
- {
- ilInit();
- ilEnable(IL_ORIGIN_SET);
- ilOriginFunc(IL_ORIGIN_LOWER_LEFT);
- il_init_done = true;
- }
-
- if(!priv.id)
- ilGenImages(1, &priv.id);
- ilBindImage(priv.id);
-}
-
-void finish_load(Image::Private &priv)
-{
- switch(ilGetInteger(IL_IMAGE_FORMAT))
- {
- case IL_COLOR_INDEX: priv.fmt = COLOR_INDEX; break;
- case IL_LUMINANCE: priv.fmt = LUMINANCE; break;
- case IL_LUMINANCE_ALPHA: priv.fmt = LUMINANCE_ALPHA; break;
- case IL_RGB: priv.fmt = RGB; break;
- case IL_RGBA: priv.fmt = RGBA; break;
- case IL_BGR: priv.fmt = BGR; break;
- case IL_BGRA: priv.fmt = BGRA; break;
- default: throw unsupported_image_format("unknown pixel format");
- }
-
- priv.width = ilGetInteger(IL_IMAGE_WIDTH);
- priv.height = ilGetInteger(IL_IMAGE_HEIGHT);
- priv.data = reinterpret_cast<char *>(ilGetData());
-
- ilBindImage(0);
-}
-
-}
-
-
-namespace Msp {
-namespace Graphics {
-
-void load_devil_file(const string &fn, Image::Private &priv)
-{
- init_load(priv);
- if(!ilLoadImage(const_cast<char *>(fn.c_str())))
- throw bad_image_data("IL error");
- finish_load(priv);
-}
-
-void load_devil_io(IO::Base &io, Image::Private &priv, const char *sig_buf, unsigned sig_len)
-{
- vector<char> data(sig_buf, sig_buf+sig_len);
- while(1)
- {
- unsigned pos = data.size();
- data.resize(pos+16384);
- unsigned len = io.read(&data[pos], 16384);
- data.resize(pos+len);
- if(len==0)
- break;
- }
-
- init_load(priv);
- if(!ilLoadL(IL_TYPE_UNKNOWN, &data.front(), data.size()))
- throw bad_image_data("IL error");
- finish_load(priv);
-}
-
-} // namespace Graphics
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef MSP_GRAPHICS_IMAGE_DEVIL_H_
-#define MSP_GRAPHICS_IMAGE_DEVIL_H_
-
-#include "image.h"
-
-namespace Msp {
-namespace Graphics {
-
-#ifdef WITH_DEVIL
-void load_devil_file(const std::string &, Image::Private &);
-void load_devil_io(IO::Base &, Image::Private &, const char *, unsigned);
-#endif
-
-} // namespace Graphics
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifdef WITH_LIBPNG
-#include <png.h>
-#include "image_png.h"
-#include "image_private.h"
-
-using namespace std;
-
-namespace {
-
-struct ErrorInfo
-{
- string msg;
-};
-
-void read(png_struct *png, png_byte *data, png_size_t size)
-{
- Msp::IO::Base *in = reinterpret_cast<Msp::IO::Base *>(png_get_io_ptr(png));
- in->read(reinterpret_cast<char *>(data), size);
-}
-
-void error(png_struct *png, const char *msg)
-{
- ErrorInfo *error_info = reinterpret_cast<ErrorInfo *>(png_get_error_ptr(png));
- error_info->msg = msg;
- longjmp(png_jmpbuf(png), 1);
-}
-
-}
-
-
-namespace Msp {
-namespace Graphics {
-
-bool is_png(const char *buf, unsigned len)
-{
- return !png_sig_cmp(reinterpret_cast<png_byte *>(const_cast<char*>(buf)), 0, len);
-}
-
-void load_png(IO::Base &in, Image::Private &priv, const char *, unsigned sig_len)
-{
- png_struct *png = 0;
- png_info *info = 0;
- priv.data = 0;
-
- try
- {
- ErrorInfo error_info;
- png = png_create_read_struct(PNG_LIBPNG_VER_STRING, &error_info, error, 0);
- info = png_create_info_struct(png);
-
- if(setjmp(png_jmpbuf(png)))
- throw bad_image_data(error_info.msg);
-
- png_set_read_fn(png, &in, read);
- png_set_sig_bytes(png, sig_len);
- png_read_info(png, info);
- png_uint_32 width;
- png_uint_32 height;
- int depth;
- int color;
- png_get_IHDR(png, info, &width, &height, &depth, &color, 0, 0, 0);
- priv.width = width;
- priv.height = height;
- if(depth!=8)
- throw unsupported_image_format("depth!=8");
- switch(color)
- {
- case PNG_COLOR_TYPE_PALETTE: priv.fmt = COLOR_INDEX; break;
- case PNG_COLOR_TYPE_GRAY: priv.fmt = LUMINANCE; break;
- case PNG_COLOR_TYPE_GRAY_ALPHA: priv.fmt = LUMINANCE_ALPHA; break;
- case PNG_COLOR_TYPE_RGB: priv.fmt = RGB; break;
- case PNG_COLOR_TYPE_RGB_ALPHA: priv.fmt = RGBA; break;
- default: throw unsupported_image_format("unknown color type");
- }
-
- unsigned nchans = png_get_channels(png, info);
- if(nchans==4 && priv.fmt==RGB)
- png_set_strip_alpha(png);
-
- unsigned rowstride = priv.width*nchans;
- priv.data = new char[rowstride*priv.height];
- for(unsigned y=0; y<priv.height; ++y)
- png_read_row(png, reinterpret_cast<png_byte *>(priv.data+rowstride*(priv.height-1-y)), 0);
-
- png_read_end(png, 0);
- png_destroy_read_struct(&png, &info, 0);
- }
- catch(...)
- {
- png_destroy_read_struct(&png, &info, 0);
- delete[] priv.data;
- throw;
- }
-}
-
-} // namespace Graphics
-} // namespace Msp
-#endif
+++ /dev/null
-#ifndef MSP_GRAPHICS_IMAGE_PNG_H_
-#define MSP_GRAPHICS_IMAGE_PNG_H_
-
-#include <msp/io/base.h>
-#include "image.h"
-
-namespace Msp {
-namespace Graphics {
-
-#ifdef WITH_LIBPNG
-bool is_png(const char *, unsigned);
-void load_png(IO::Base &, Image::Private &, const char *, unsigned);
-#endif
-
-} // namespace Graphics
-} // namespace Msp
-
-#endif
+++ /dev/null
-#ifndef MSP_GRAPHICS_IMAGE_PRIVATE_H_
-#define MSP_GRAPHICS_IMAGE_PRIVATE_H_
-
-#include "image.h"
-
-namespace Msp {
-namespace Graphics {
-
-struct Image::Private
-{
-#ifdef WITH_DEVIL
- unsigned id;
-#endif
- PixelFormat fmt;
- unsigned width;
- unsigned height;
- char *data;
-
- Private();
-};
-
-} // namespace Graphics
-} // namespace Msp
-
-#endif
--- /dev/null
+#include <msp/core/refptr.h>
+#include <msp/io/file.h>
+#include <msp/strings/format.h>
+#include "imageloader.h"
+#ifdef WITH_LIBPNG
+#include "png/pngloader.h"
+#endif
+#ifdef WITH_DEVIL
+#include "devil/devilloader.h"
+#endif
+
+using namespace std;
+
+namespace Msp {
+namespace Graphics {
+
+ImageLoader::ImageLoader():
+ source(0)
+{ }
+
+ImageLoader::~ImageLoader()
+{
+ delete source;
+}
+
+ImageLoader *ImageLoader::open_file(const string &fn)
+{
+ try
+ {
+ RefPtr<IO::File> file = new IO::BufferedFile(fn);
+ ImageLoader *loader = open_io(*file);
+ loader->source = file.release();
+ return loader;
+ }
+ catch(const unsupported_image_format &e)
+ {
+ throw unsupported_image_format(format("%s: %s", fn, e.what()));
+ }
+}
+
+ImageLoader *ImageLoader::open_io(IO::Base &io)
+{
+ char sig_buf[8];
+ unsigned sig_len = io.read(sig_buf, sizeof(sig_buf));
+ string sig(sig_buf, sig_len);
+
+ // TODO register loader classes automatically
+ ImageLoader *loader = 0;
+ if(0)
+ ;
+#ifdef WITH_LIBPNG
+ else if(PngLoader::detect(sig))
+ loader = new PngLoader(io, sig);
+#endif
+#ifdef WITH_DEVIL
+ else if(DevilLoader::detect(sig))
+ loader = new DevilLoader(io, sig);
+#endif
+ else
+ {
+ string sig_hex;
+ for(unsigned i=0; i<sig_len; ++i)
+ {
+ if(i)
+ sig_hex += ' ';
+ sig_hex += format("%02X", static_cast<unsigned char>(sig_buf[i]));
+ }
+ throw unsupported_image_format(sig_hex);
+ }
+
+ return loader;
+}
+
+} // namespace Graphics
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GRAPHICS_IMAGELOADER_H_
+#define MSP_GRAPHICS_IMAGELOADER_H_
+
+#include "image.h"
+
+namespace Msp {
+namespace Graphics {
+
+class unsupported_image_format: public std::runtime_error
+{
+public:
+ unsupported_image_format(const std::string &w): std::runtime_error(w) { }
+ virtual ~unsupported_image_format() throw() { }
+};
+
+class bad_image_data: public std::runtime_error
+{
+public:
+ bad_image_data(const std::string &w): std::runtime_error(w) { }
+ virtual ~bad_image_data() throw() { }
+};
+
+
+class ImageLoader
+{
+private:
+ IO::Base *source;
+protected:
+
+ ImageLoader();
+public:
+ virtual ~ImageLoader();
+
+ static ImageLoader *open_file(const std::string &);
+ static ImageLoader *open_io(IO::Base &);
+
+ virtual void load(Image::Data &) = 0;
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <png.h>
+#include "pngloader.h"
+
+using namespace std;
+
+namespace {
+
+void read(png_struct *png, png_byte *data, png_size_t size)
+{
+ Msp::IO::Base *in = reinterpret_cast<Msp::IO::Base *>(png_get_io_ptr(png));
+ in->read(reinterpret_cast<char *>(data), size);
+}
+
+void error(png_struct *png, const char *msg)
+{
+ string *message = reinterpret_cast<string *>(png_get_error_ptr(png));
+ *message = msg;
+ longjmp(png_jmpbuf(png), 1);
+}
+
+} // namespace
+
+
+namespace Msp {
+namespace Graphics {
+
+struct PngLoader::Private
+{
+ std::string message;
+ png_struct *png;
+ png_info *info;
+};
+
+PngLoader::PngLoader(IO::Base &io, const string &sig):
+ priv(new Private)
+{
+ priv->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, &priv->message, error, 0);
+ priv->info = png_create_info_struct(priv->png);
+
+ // These probably won't give any errors
+ png_set_read_fn(priv->png, &io, read);
+ png_set_sig_bytes(priv->png, sig.size());
+}
+
+PngLoader::~PngLoader()
+{
+ png_destroy_read_struct(&priv->png, &priv->info, 0);
+ delete priv;
+}
+
+bool PngLoader::detect(const std::string &sig)
+{
+ return !png_sig_cmp(reinterpret_cast<png_byte *>(const_cast<char*>(sig.data())), 0, sig.size());
+}
+
+void PngLoader::load(Image::Data &data)
+{
+ if(setjmp(png_jmpbuf(priv->png)))
+ throw bad_image_data(priv->message);
+
+ png_read_info(priv->png, priv->info);
+ png_uint_32 width;
+ png_uint_32 height;
+ int depth;
+ int color;
+ png_get_IHDR(priv->png, priv->info, &width, &height, &depth, &color, 0, 0, 0);
+ data.width = width;
+ data.height = height;
+ if(depth!=8)
+ throw unsupported_image_format("depth!=8");
+ switch(color)
+ {
+ case PNG_COLOR_TYPE_PALETTE: data.fmt = COLOR_INDEX; break;
+ case PNG_COLOR_TYPE_GRAY: data.fmt = LUMINANCE; break;
+ case PNG_COLOR_TYPE_GRAY_ALPHA: data.fmt = LUMINANCE_ALPHA; break;
+ case PNG_COLOR_TYPE_RGB: data.fmt = RGB; break;
+ case PNG_COLOR_TYPE_RGB_ALPHA: data.fmt = RGBA; break;
+ default: throw unsupported_image_format("unknown color type");
+ }
+
+ unsigned nchans = png_get_channels(priv->png, priv->info);
+ if(nchans==4 && data.fmt==RGB)
+ png_set_strip_alpha(priv->png);
+
+ unsigned rowstride = data.width*nchans;
+ data.data = new char[rowstride*data.height];
+ for(unsigned y=0; y<data.height; ++y)
+ png_read_row(priv->png, reinterpret_cast<png_byte *>(data.data+rowstride*(data.height-1-y)), 0);
+
+ png_read_end(priv->png, 0);
+}
+
+} // namespace Graphics
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GRAPHICS_PNGLOADER_H_
+#define MSP_GRAPHICS_PNGLOADER_H_
+
+#include <msp/graphics/imageloader.h>
+
+namespace Msp {
+namespace Graphics {
+
+class PngLoader: public ImageLoader
+{
+private:
+ struct Private;
+
+ Private *priv;
+
+public:
+ PngLoader(IO::Base &, const std::string &);
+ virtual ~PngLoader();
+
+ static bool detect(const std::string &);
+
+ virtual void load(Image::Data &);
+};
+
+} // namespace Graphics
+} // namespace Msp
+
+#endif