--- /dev/null
+#include <stdio.h>
+#include <setjmp.h>
+#include <jpeglib.h>
+#include "jpegloader.h"
+
+using namespace std;
+
+namespace {
+
+struct ErrorManager: jpeg_error_mgr
+{
+ jmp_buf jmp;
+ string message;
+};
+
+void error_exit(j_common_ptr jpeg)
+{
+ ErrorManager *err = reinterpret_cast<ErrorManager *>(jpeg->err);
+ char buf[JMSG_LENGTH_MAX];
+ err->format_message(jpeg, buf);
+ err->message = buf;
+ longjmp(err->jmp, 1);
+}
+
+void emit_message(j_common_ptr, int)
+{
+}
+
+
+struct SourceManager: jpeg_source_mgr
+{
+ Msp::IO::Seekable *io;
+ JOCTET buffer[4096];
+};
+
+void init_source(j_decompress_ptr jpeg)
+{
+ SourceManager *src = reinterpret_cast<SourceManager *>(jpeg->src);
+ src->next_input_byte = src->buffer;
+ src->bytes_in_buffer = 0;
+}
+
+int fill_input_buffer(j_decompress_ptr jpeg)
+{
+ SourceManager *src = reinterpret_cast<SourceManager *>(jpeg->src);
+ src->next_input_byte = src->buffer;
+ src->bytes_in_buffer = src->io->read(reinterpret_cast<char *>(src->buffer), sizeof(src->buffer));
+ return true;
+}
+
+void skip_input_data(j_decompress_ptr jpeg, long count)
+{
+ if(count<=0)
+ return;
+
+ SourceManager *src = reinterpret_cast<SourceManager *>(jpeg->src);
+ if(static_cast<unsigned long>(count)<src->bytes_in_buffer)
+ {
+ src->next_input_byte += count;
+ src->bytes_in_buffer -= count;
+ }
+ else
+ {
+ src->io->seek(count, Msp::IO::S_CUR);
+ src->bytes_in_buffer = 0;
+ }
+}
+
+void term_source(j_decompress_ptr)
+{
+}
+
+} // namespace
+
+
+namespace Msp {
+namespace Graphics {
+
+struct JpegLoader::Private
+{
+ jpeg_decompress_struct jpeg;
+ ErrorManager err_mgr;
+ SourceManager src_mgr;
+};
+
+
+ImageLoader::Register<JpegLoader> JpegLoader::reg;
+
+JpegLoader::JpegLoader(IO::Seekable &io):
+ priv(new Private)
+{
+ priv->jpeg.err = jpeg_std_error(&priv->err_mgr);
+ priv->err_mgr.error_exit = &error_exit;
+ priv->err_mgr.emit_message = &emit_message;
+
+ jpeg_create_decompress(&priv->jpeg);
+
+ priv->jpeg.src = &priv->src_mgr;
+ priv->src_mgr.init_source = &init_source;
+ priv->src_mgr.fill_input_buffer = &fill_input_buffer;
+ priv->src_mgr.skip_input_data = &skip_input_data;
+ priv->src_mgr.resync_to_restart = &jpeg_resync_to_restart;
+ priv->src_mgr.term_source = &term_source;
+ priv->src_mgr.io = &io;
+}
+
+JpegLoader::~JpegLoader()
+{
+ jpeg_destroy_decompress(&priv->jpeg);
+ delete priv;
+}
+
+bool JpegLoader::detect(const string &sig)
+{
+ static const char jpeg_sig[] = "\xFF\xD8\xFF";
+ if(sig.size()<sizeof(jpeg_sig))
+ return false;
+ return !sig.compare(0, 3, jpeg_sig);
+}
+
+void JpegLoader::load(Image::Data &data)
+{
+ if(setjmp(priv->err_mgr.jmp))
+ throw bad_image_data(priv->err_mgr.message);
+
+ jpeg_read_header(&priv->jpeg, true);
+ priv->jpeg.out_color_space = JCS_RGB;
+ jpeg_start_decompress(&priv->jpeg);
+
+ data.width = priv->jpeg.output_width;
+ data.height = priv->jpeg.output_height;
+ data.stride = priv->jpeg.output_width*priv->jpeg.output_components;
+ data.fmt = RGB;
+
+ data.data = new char[data.stride*data.height];
+ JSAMPROW rows[8];
+ while(priv->jpeg.output_scanline<data.height)
+ {
+ unsigned y = data.height-priv->jpeg.output_scanline;
+ unsigned count = min(y, 8U);
+ for(unsigned i=0; i<count; ++i)
+ rows[i] = reinterpret_cast<JSAMPROW>(data.data+(y-i-1)*data.stride);
+ jpeg_read_scanlines(&priv->jpeg, rows, count);
+ }
+
+ jpeg_finish_decompress(&priv->jpeg);
+}
+
+} // namespace Graphics
+} // namespace Msp