aboutsummaryrefslogtreecommitdiff
path: root/lib/jpegstream.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/jpegstream.cc')
-rw-r--r--lib/jpegstream.cc270
1 files changed, 270 insertions, 0 deletions
diff --git a/lib/jpegstream.cc b/lib/jpegstream.cc
new file mode 100644
index 0000000..66bf92d
--- /dev/null
+++ b/lib/jpegstream.cc
@@ -0,0 +1,270 @@
+// jpegstream.cc -- image streams producing JPEG files
+// Copyright (C) 2008, 2009 SEIKO EPSON CORPORATION
+//
+// This file is part of the 'iscan' program.
+//
+// The 'iscan' program is free-ish software.
+// You can redistribute it and/or modify it under the terms of the GNU
+// General Public License as published by the Free Software Foundation;
+// either version 2 of the License or at your option any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of FITNESS
+// FOR A PARTICULAR PURPOSE or MERCHANTABILITY.
+// See the GNU General Public License for more details.
+//
+// You should have received a verbatim copy of the GNU General Public
+// License along with this program; if not, write to:
+//
+// Free Software Foundation, Inc.
+// 59 Temple Place, Suite 330
+// Boston, MA 02111-1307 USA
+//
+// As a special exception, the copyright holders give permission
+// to link the code of this program with the esmod library and
+// distribute linked combinations including the two. You must obey
+// the GNU General Public License in all respects for all of the
+// code used other then esmod.
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "jpegstream.hh"
+
+#include <cstdlib>
+#include <ios>
+
+namespace iscan
+{
+ jpegstream::jpegstream (FILE *fp, const string& name)
+ : _stream (fp), _header (false), _scanline (NULL)
+ {
+ if (!_stream) throw std::invalid_argument ("invalid file handle");
+#if HAVE_JPEGLIB_H
+ init ();
+#endif
+ }
+
+ jpegstream::~jpegstream (void)
+ {
+ delete [] _scanline;
+#if HAVE_JPEGLIB_H
+ if (_header)
+ {
+ if (0 == _v_sz) lib->finish_compress (&_info);
+ lib->destroy_compress (&_info);
+ }
+#endif
+ fflush (_stream);
+ }
+
+ basic_imgstream&
+ jpegstream::write (const byte_type *line, size_type n)
+ {
+ if (!line || 0 == n) return *this;
+#if HAVE_JPEGLIB_H
+ if (!_header)
+ {
+ write_header ();
+ }
+ if (!_scanline)
+ {
+ lib->write_scanlines (&_info, (JSAMPLE **) &line, 1);
+ if (0 < _info.err->msg_code)
+ throw std::ios_base::failure ("write error");
+ }
+ else
+ {
+ // FIXME: assumes that _bits == 1, whereas the condition for
+ // _scanline to be true, see write_init (), requires
+ // only that _bits != 8.
+ for (unsigned int i = 0; i < _h_sz; ++i)
+ {
+ div_t index = div (i, 8 * sizeof (JSAMPLE));
+ int offset = 8 * sizeof (JSAMPLE) - 1 - index.rem;
+ _scanline[i] = ((line[index.quot] & (1 << offset))
+ ? 0 : ~0);
+ }
+ lib->write_scanlines (&_info, (JSAMPLE **) &_scanline, 1);
+ if (0 < _info.err->msg_code)
+ throw std::ios_base::failure ("write error");
+ }
+ --_v_sz;
+#endif /* HAVE_JPEGLIB_H */
+ return *this;
+ }
+
+ bool
+ jpegstream::is_usable (void)
+ {
+ if (lib)
+ {
+ return lib->is_usable;
+ }
+
+ lib = new (std::nothrow) jpeg_lib_handle ();
+ if (!lib)
+ {
+ return false;
+ }
+
+ lib->is_usable = false;
+ lib->message = string ();
+ lib->lib = NULL;
+#if HAVE_JPEGLIB_H
+ try
+ {
+ basic_imgstream::dlopen ("libjpeg", validate);
+ }
+ catch (std::runtime_error& e)
+ {
+ lib->message = e.what ();
+ return lib->is_usable;
+ }
+#endif /* HAVE_JPEGLIB_H */
+
+ return lib->is_usable;
+ }
+
+#if HAVE_JPEGLIB_H
+#define funcsym(name) \
+ lib->name \
+ = ((jpegstream::jpeg_lib_handle::name##_f) \
+ basic_imgstream::dlsym (lib->lib, "jpeg_"#name));
+#endif
+
+ bool
+ jpegstream::validate (lt_dlhandle h)
+ {
+ if (!h) return false;
+
+#if HAVE_JPEGLIB_H
+ lib->lib = h;
+
+# ifndef jpeg_create_compress
+ funcsym (create_compress);
+# else
+ funcsym (CreateCompress);
+# endif
+ funcsym (finish_compress);
+ funcsym (destroy_compress);
+ funcsym (destroy);
+ funcsym (stdio_dest);
+ funcsym (std_error);
+ funcsym (write_scanlines);
+ funcsym (set_defaults);
+ funcsym (start_compress);
+ funcsym (default_qtables);
+
+ // restrict usage of libjpeg to the version range it was compiled against;
+ // either before version 7.0 or 7.0 and later
+ bool is_version_consistent =
+ ((JPEG_LIB_VERSION < 70 && !lib->default_qtables) ||
+ (JPEG_LIB_VERSION >= 70 && lib->default_qtables));
+
+ lib->is_usable = (
+# ifndef jpeg_create_compress
+ lib->create_compress
+# else
+ lib->CreateCompress
+# endif
+ && lib->finish_compress
+ && lib->destroy_compress
+ && lib->destroy
+ && lib->stdio_dest
+ && lib->std_error
+ && lib->write_scanlines
+ && lib->set_defaults
+ && lib->start_compress
+ && is_version_consistent);
+#endif /* HAVE_JPEGLIB_H */
+
+ return lib->is_usable;
+ }
+
+#if HAVE_JPEGLIB_H
+#undef funcsym
+#endif
+
+ basic_imgstream&
+ jpegstream::write_header (void)
+ {
+ check_consistency ();
+
+#if HAVE_JPEGLIB_H
+
+ _info.image_width = _h_sz;
+ _info.image_height = _v_sz;
+
+ _info.in_color_space = (RGB == _cspc ? JCS_RGB : JCS_GRAYSCALE);
+ _info.input_components = (RGB == _cspc ? 3 : 1);
+
+ lib->set_defaults (&_info);
+
+ size_type density_max = (1 << sizeof (_info.X_density) * 8) - 1;
+ _info.density_unit = 1;
+ _info.X_density = (_hres <= density_max) ? _hres : density_max;
+ _info.Y_density = (_vres <= density_max) ? _vres : density_max;
+
+ lib->start_compress (&_info, true);
+
+ if (mono == _cspc && 8 != _bits)
+ {
+ _scanline = new byte_type[_h_sz];
+ }
+ else
+ {
+ _scanline = NULL;
+ }
+#endif /* HAVE_JPEGLIB_H */
+
+ _header = true;
+ return *this;
+ }
+
+ void
+ jpegstream::check_consistency (void) const
+ {
+ if (!(mono == _cspc || grey == _cspc || RGB == _cspc))
+ {
+ throw std::logic_error ("unsupported colour space");
+ }
+ }
+
+ void
+ jpegstream::init (void)
+ {
+ if (!is_usable ())
+ {
+ throw std::runtime_error (lib->message);
+ }
+
+#if HAVE_JPEGLIB_H
+ // set up JPEG library default error handlers first, then override
+ // error handling for fatal errors (because the default would just
+ // end up calling exit())
+ _info.err = lib->std_error (&_err);
+ _err.error_exit = error_exit;
+# ifndef jpeg_create_compress
+ lib->create_compress (&_info);
+# else
+ lib->CreateCompress (&_info, JPEG_LIB_VERSION,
+ (size_t) sizeof (struct jpeg_compress_struct));
+# endif
+ lib->stdio_dest (&_info, _stream);
+#endif /* HAVE_JPEGLIB_H */
+ }
+
+ jpegstream::jpeg_lib_handle *jpegstream::lib = NULL;
+
+#if HAVE_JPEGLIB_H
+ void
+ jpegstream::error_exit (jpeg_common_struct *info)
+ {
+ jpegstream::lib->destroy (info);
+ }
+#endif
+
+} // namespace iscan