aboutsummaryrefslogtreecommitdiff
path: root/lib/pcxstream.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pcxstream.cc')
-rw-r--r--lib/pcxstream.cc320
1 files changed, 320 insertions, 0 deletions
diff --git a/lib/pcxstream.cc b/lib/pcxstream.cc
new file mode 100644
index 0000000..722e0b0
--- /dev/null
+++ b/lib/pcxstream.cc
@@ -0,0 +1,320 @@
+// pcxstream.cc -- image streams producing PCX files
+// Copyright (C) 2011 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 "pcxstream.hh"
+
+#include <ios>
+#include <cstring>
+
+namespace iscan
+{
+ pcxstream::pcxstream (FILE *fp, const string& name)
+ : _stream (fp),
+ _header (false),
+ _footer (false),
+ _bytesperline (0),
+ _row_buf (NULL),
+ _zbuf (NULL)
+ {
+ if (!_stream) throw std::invalid_argument ("invalid file handle");
+
+ memset (_pcx_header, 0, sizeof (_pcx_header));
+ _pcx_header [0] = 0x0a; // Manufacturer
+ _pcx_header [1] = 0x05; // Version
+ _pcx_header [2] = 1; // Encoding
+ }
+
+ pcxstream::~pcxstream (void)
+ {
+ fflush (_stream);
+ }
+
+ basic_imgstream&
+ pcxstream::flush (void)
+ {
+ if (!_footer)
+ {
+ if (mono == _cspc || grey == _cspc)
+ {
+ write_palette ();
+ }
+ delete [] _row_buf;
+ delete [] _zbuf;
+ _footer = true;
+ }
+
+ fflush (_stream);
+ return *this;
+ }
+
+ void
+ pcxstream::write_palette (void)
+ {
+ const size_type palette_size = 256 * 3 + 1;
+ byte_type palette [palette_size];
+ byte_type *ptr = palette;
+ *ptr = 0x0c; // set palette identifier
+ ++ptr;
+
+ for (int i=0; 256>i; ++i)
+ {
+ *ptr = *(ptr+1) = *(ptr+2) = i;
+ ptr += 3;
+ }
+ size_t rv = fwrite (palette, sizeof (*palette), palette_size, _stream);
+ if (palette_size != rv)
+ {
+ throw std::ios_base::failure ("write error");
+ }
+ }
+
+ basic_imgstream&
+ pcxstream::write (const byte_type *line, size_type n)
+ {
+ if (!line || 0 == n) return *this;
+ if (!_header)
+ {
+ size_type sz = 0;
+ write_header ();
+ if (mono == _cspc)
+ {
+ sz = n * 8;
+ pwrite = &pcxstream::write_mono;
+ }
+ else if (grey == _cspc)
+ {
+ sz = 1; // dummy
+ pwrite = &pcxstream::write_gray;
+ }
+ else if (RGB == _cspc)
+ {
+ sz = _h_sz * 3; // r + g + b
+ pwrite = &pcxstream::write_color;
+ }
+ _row_buf = new byte_type [sz];
+ _zbuf = new byte_type [_h_sz * 2 + 1]; // assume worst case
+ }
+
+ (this->*pwrite) (line, n);
+
+ return *this;
+ }
+
+ void
+ pcxstream::write_color (const byte_type *line, size_type n)
+ {
+ byte_type *r = _row_buf;
+ byte_type *g = r + _h_sz;
+ byte_type *b = g + _h_sz;
+
+ for (size_type i=0; _h_sz>i; ++i)
+ {
+ r [i] = *line; ++line;
+ g [i] = *line; ++line;
+ b [i] = *line; ++line;
+ }
+ write_row (r, _h_sz);
+ write_row (g, _h_sz);
+ write_row (b, _h_sz);
+ }
+
+ void
+ pcxstream::write_gray (const byte_type *line, size_type n)
+ {
+ write_row (line, n);
+ }
+
+ void
+ pcxstream::write_mono (const byte_type *line, size_type n)
+ {
+ for (size_type i=0; n>i; ++i)
+ {
+ for (size_type j=0; 8>j; ++j)
+ {
+ _row_buf [i*8+7-j] = (line[i] & (0x1 << j)) ? 0x0 : 0xff;
+ }
+ }
+ write_row (_row_buf, _h_sz);
+ }
+
+ void
+ pcxstream::write_row (const byte_type *line, size_type n)
+ {
+ size_type zlen = compress_row (line, n, _zbuf);
+
+ // 2 byte align
+ if (n < _bytesperline)
+ {
+ _zbuf [zlen] = 0x00; // add padding
+ ++zlen;
+ }
+
+ write_bytes (_zbuf, zlen);
+ }
+
+ void
+ pcxstream::write_bytes (const byte_type *bytes, size_type n)
+ {
+ size_type rv;
+ rv = fwrite (bytes, sizeof (*bytes), n, _stream);
+ if (n != rv)
+ {
+ throw std::ios_base::failure ("write error");
+ }
+ }
+
+ basic_imgstream::size_type
+ pcxstream::compress_row (const byte_type *line,
+ const size_type n,
+ byte_type *compressed)
+ {
+ byte_type cnt = 0;
+ byte_type *ptr = compressed;
+ byte_type saved = 0;
+
+ for (size_type i=0; i<n; ++i)
+ {
+ if (0 == cnt)
+ {
+ saved = line[i];
+ cnt = 1;
+ }
+ else if (63 > cnt && saved == line[i])
+ {
+ ++cnt;
+ }
+ else if (63 == cnt || saved != line[i])
+ {
+ if (1 < cnt || 0xc0 == (saved & 0xc0))
+ {
+ *ptr = 0xc0 | cnt;
+ ++ptr;
+ }
+ *ptr = saved;
+ ++ptr;
+ saved = line[i];
+ cnt = 1;
+ }
+ }
+ // put the rest
+ if (1 < cnt || 0xc0 == (saved & 0xc0))
+ {
+ *ptr = 0xc0 | cnt;
+ ++ptr;
+ }
+ *ptr = saved;
+ ++ptr;
+
+ return (ptr - compressed);
+ }
+
+ bool
+ pcxstream::is_usable (void)
+ {
+ return true;
+ }
+
+ basic_imgstream&
+ pcxstream::write_header (void)
+ {
+ check_consistency ();
+
+ // Even monochrome image, BitsPerPixel in PCX header is set to 8
+ // in accordance with the implementation of GIMP.
+ size_type bpp = 8;
+ _pcx_header [3] = bpp; // BitsPerPixel
+
+ set_value_le ((_h_sz-1), &_pcx_header [8], 2); // Window.Xmax
+ set_value_le ((_v_sz-1), &_pcx_header [10], 2); // Window.Ymax
+ set_value_le (_hres, &_pcx_header [12], 2); // HDpi
+ set_value_le (_vres, &_pcx_header [14], 2); // VDpi
+
+ size_type nplanes = 1;
+ size_type paletteinfo = 2;
+ if (RGB == _cspc)
+ {
+ nplanes = 3;
+ paletteinfo = 1;
+ }
+ _pcx_header [65] = nplanes; // NPlanes
+ set_value_le (paletteinfo, &_pcx_header [68], 2); // PaletteInfo
+
+ _bytesperline = (_h_sz * bpp + 7) / 8;
+ _bytesperline += (_bytesperline & 0x1); // 2 byte align
+ set_value_le (_bytesperline, &_pcx_header [66], 2); // BytesPerLine
+
+ write_bytes (_pcx_header, sizeof (_pcx_header));
+
+ _header = true;
+ return *this;
+ }
+
+ void
+ pcxstream::check_consistency (void) const
+ {
+ if (!(mono == _cspc || grey == _cspc || RGB == _cspc))
+ {
+ throw std::logic_error ("unsupported colour space");
+ }
+ if (!(_bits == 1 || _bits == 8))
+ {
+ throw std::logic_error ("unsupported bit depth");
+ }
+ size_type max = (1 << 16) - 1;
+ if (_h_sz > max || _v_sz > max)
+ {
+ throw std::logic_error ("maximum image size exceeded");
+ }
+ }
+
+ // set value to array with little endian
+ void
+ pcxstream::set_value_le (const size_type value,
+ byte_type *array,
+ const size_type array_size)
+ {
+ if (!array || 0 >= array_size)
+ {
+ throw std::invalid_argument ("invalid argment");
+ }
+
+ size_type v = value;
+
+ for (size_type i=0; array_size>i; ++i)
+ {
+ array[i] = v & 0xff;
+ v >>= 8;
+ }
+ }
+
+} // namespace iscan