Blame tools/c44.cpp

Packit df99a1
//C-  -*- C++ -*-
Packit df99a1
//C- -------------------------------------------------------------------
Packit df99a1
//C- DjVuLibre-3.5
Packit df99a1
//C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
Packit df99a1
//C- Copyright (c) 2001  AT&T
Packit df99a1
//C-
Packit df99a1
//C- This software is subject to, and may be distributed under, the
Packit df99a1
//C- GNU General Public License, either Version 2 of the license,
Packit df99a1
//C- or (at your option) any later version. The license should have
Packit df99a1
//C- accompanied the software or you may obtain a copy of the license
Packit df99a1
//C- from the Free Software Foundation at http://www.fsf.org .
Packit df99a1
//C-
Packit df99a1
//C- This program is distributed in the hope that it will be useful,
Packit df99a1
//C- but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit df99a1
//C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit df99a1
//C- GNU General Public License for more details.
Packit df99a1
//C- 
Packit df99a1
//C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
Packit df99a1
//C- Lizardtech Software.  Lizardtech Software has authorized us to
Packit df99a1
//C- replace the original DjVu(r) Reference Library notice by the following
Packit df99a1
//C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
Packit df99a1
//C-
Packit df99a1
//C-  ------------------------------------------------------------------
Packit df99a1
//C- | DjVu (r) Reference Library (v. 3.5)
Packit df99a1
//C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
Packit df99a1
//C- | The DjVu Reference Library is protected by U.S. Pat. No.
Packit df99a1
//C- | 6,058,214 and patents pending.
Packit df99a1
//C- |
Packit df99a1
//C- | This software is subject to, and may be distributed under, the
Packit df99a1
//C- | GNU General Public License, either Version 2 of the license,
Packit df99a1
//C- | or (at your option) any later version. The license should have
Packit df99a1
//C- | accompanied the software or you may obtain a copy of the license
Packit df99a1
//C- | from the Free Software Foundation at http://www.fsf.org .
Packit df99a1
//C- |
Packit df99a1
//C- | The computer code originally released by LizardTech under this
Packit df99a1
//C- | license and unmodified by other parties is deemed "the LIZARDTECH
Packit df99a1
//C- | ORIGINAL CODE."  Subject to any third party intellectual property
Packit df99a1
//C- | claims, LizardTech grants recipient a worldwide, royalty-free, 
Packit df99a1
//C- | non-exclusive license to make, use, sell, or otherwise dispose of 
Packit df99a1
//C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the 
Packit df99a1
//C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU 
Packit df99a1
//C- | General Public License.   This grant only confers the right to 
Packit df99a1
//C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to 
Packit df99a1
//C- | the extent such infringement is reasonably necessary to enable 
Packit df99a1
//C- | recipient to make, have made, practice, sell, or otherwise dispose 
Packit df99a1
//C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to 
Packit df99a1
//C- | any greater extent that may be necessary to utilize further 
Packit df99a1
//C- | modifications or combinations.
Packit df99a1
//C- |
Packit df99a1
//C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
Packit df99a1
//C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
Packit df99a1
//C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
Packit df99a1
//C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Packit df99a1
//C- +------------------------------------------------------------------
Packit df99a1
Packit df99a1
#ifdef HAVE_CONFIG_H
Packit df99a1
# include "config.h"
Packit df99a1
#endif
Packit df99a1
#if NEED_GNUG_PRAGMAS
Packit df99a1
# pragma implementation
Packit df99a1
#endif
Packit df99a1
Packit df99a1
/** @name c44
Packit df99a1
Packit df99a1
    {\bf Synopsis}\\
Packit df99a1
    \begin{verbatim}
Packit df99a1
        c44 [options] pnmfile [djvufile]
Packit df99a1
        c44 [options] jpegfile [djvufile]
Packit df99a1
    \end{verbatim}
Packit df99a1
Packit df99a1
    {\bf Description} ---
Packit df99a1
    File #"c44.cpp"# illustrates the use of classes \Ref{IWBitmap} and
Packit df99a1
    \Ref{IWPixmap} for compressing and encoding a color image or a gray level
Packit df99a1
    image using the DjVu IW44 wavelets.  This is the preferred mode for
Packit df99a1
    creating a DjVu image which does not require separate layers for encoding
Packit df99a1
    the text and the background images.  The files created by #c44# are
Packit df99a1
    legal Photo DjVu files recognized by the DjVu decoder (see program \Ref{ddjvu}).
Packit df99a1
Packit df99a1
    {\bf Arguments} ---
Packit df99a1
    Argument #pnmfile# or #jpegfile# is the name of the input file.  PGM files
Packit df99a1
    are recognized for gray level images; PPM files are recognized for color
Packit df99a1
    images.  Popular file formats can be converted to PGM or PPM using the
Packit df99a1
    NetPBM package (\URL{http://www.arc.umn.edu/GVL/Software/netpbm.html})
Packit df99a1
    or the ImageMagick package (\URL{http://www.wizards.dupont.com/cristy/}).
Packit df99a1
Packit df99a1
    Optional argument #djvufile# is the name of the output file.  It is
Packit df99a1
    customary to use either suffix #".djvu"#, #".djv"#, #".iw44"# or #".iw4"#.
Packit df99a1
    Suffix #".djvu"# emphasizes the fact that IW44 files is seamlessly
Packit df99a1
    recognized by the current DjVu decoder.  Suffix #".iw4"# however was
Packit df99a1
    required by older versions of the DjVu plugin.  If this argument is
Packit df99a1
    omitted, a filename is generated by replacing the suffix of #pnmfile# 
Packit df99a1
    or #jpegfile# with suffix #".djvu"#.
Packit df99a1
Packit df99a1
    {\bf Quality Specification}---
Packit df99a1
    Files produced by the DjVu IW44 Wavelet Encoder are IFF files composed of
Packit df99a1
    an arbitrary number of chunks (see \Ref{showiff} and \Ref{IWImage.h})
Packit df99a1
    containing successive refinements of the encoded image.  Each chunk is
Packit df99a1
    composed of several slices.  A typical file contains a total of 100 slices
Packit df99a1
    split between three or four chunks.  Various options provide quality
Packit df99a1
    targets (#-decibel#), slicing targets (#-slice#) or file size targets
Packit df99a1
    (#-bpp# or #-size#) for each of these refinements.  Chunks are generated
Packit df99a1
    until meeting either the decibel target, the file size target, or the
Packit df99a1
    slicing target for each chunk.
Packit df99a1
    \begin{description}
Packit df99a1
    \item[-bpp n,..,n] 
Packit df99a1
    Selects a increasing sequence of bitrates for building 
Packit df99a1
    progressive IW44 file (in bits per pixel).
Packit df99a1
    \item[-size n,..,n] 
Packit df99a1
    Selects a increasing sequence of minimal sizes for building 
Packit df99a1
    progressive IW44 file (in bytes).
Packit df99a1
    \item[-decibel n,..,n]
Packit df99a1
    Selects an increasing sequence of luminance error expressed as decibels
Packit df99a1
    ranging from 16 (very low quality) to 48 (very high quality).  This
Packit df99a1
    criterion should not be used when recoding an image which was already
Packit df99a1
    compressed with a lossy compression scheme (such as Wavelets or JPEG)
Packit df99a1
    because successive losses of quality accumulate.    
Packit df99a1
    \item[-slice n+...+n]
Packit df99a1
    Selects an increasing sequence of data slices expressed as integers 
Packit df99a1
    ranging from 1 to 140. 
Packit df99a1
    \end{description}
Packit df99a1
    These options take a target specification list expressed either as a comma
Packit df99a1
    separated list of increasing numbers or as a list of numbers separated by
Packit df99a1
    character #'+'#.  Both commands below for instance are equivalent:
Packit df99a1
    \begin{verbatim}
Packit df99a1
    c44 -bpp 0.1,0.2,0.5 inputfile.ppm  outputfile.djvu
Packit df99a1
    c44 -bpp 0.1+0.1+0.3 inputfile.ppm  outputfile.djvu
Packit df99a1
    \end{verbatim}
Packit df99a1
    Both these commands generate a file whose first chunk encodes the image
Packit df99a1
    with 0.1 bits per pixel, whose second chunk refines the image to 0.2 bits
Packit df99a1
    per pixel and whose third chunk refines the image to 0.5 bits per pixel.
Packit df99a1
    In other words, the second chunk provides an extra 0.1 bits per pixel and
Packit df99a1
    the third chunk provides an extra 0.3 bits per pixels.
Packit df99a1
Packit df99a1
    When no quality specification is provided, program #c44# usually generates
Packit df99a1
    a file composed of three progressive refinement chunks whose quality
Packit df99a1
    should be acceptable.  The best results however are achieved by precisely
Packit df99a1
    tuning the image quality.  As a rule of thumb, #c44# generates an
Packit df99a1
    acceptable quality when you specify a size equal to 50% to 75% of the size
Packit df99a1
    of a comparable JPEG image.
Packit df99a1
Packit df99a1
    {\bf Color Processing Specification} ---
Packit df99a1
    Five options control the encoding of the chrominance information of color
Packit df99a1
    images.  These options are of course meaningless for processing a gray
Packit df99a1
    level image.
Packit df99a1
    \begin{description}
Packit df99a1
    \item[-crcbnormal]
Packit df99a1
    Selects normal chrominance encoding (default).  Chrominance information is
Packit df99a1
    encoded at the same resolution as the luminance. 
Packit df99a1
    \item[-crcbhalf]
Packit df99a1
    Selects half resolution chrominance encoding.  Chrominance information is
Packit df99a1
    encoded at half the luminance resolution. 
Packit df99a1
    \item[-crcbdelay n]
Packit df99a1
    This option can be used with #-crcbnormal# and #-crcbhalf# for specifying
Packit df99a1
    an encoding delay which reduces the bitrate associated with the chrominance. The
Packit df99a1
    default chrominance encoding delay is 10 slices.
Packit df99a1
    \item[-crcbfull]
Packit df99a1
    Selects the highest possible quality for encoding the chrominance information. This
Packit df99a1
    is equivalent to specifying #-crcbnormal# and #-crcbdelay 0#.
Packit df99a1
    \item[-crcbnone]
Packit df99a1
    Disables encoding of the chrominance.  Only the luminance information will
Packit df99a1
    be encoded. The resulting image will show in shades of gray.
Packit df99a1
    \end{description}
Packit df99a1
Packit df99a1
    {\bf Advanced Options} ---
Packit df99a1
    Program #c44# also recognizes the following options:
Packit df99a1
    \begin{description}
Packit df99a1
    \item[-dbfrac f]
Packit df99a1
    This option alters the meaning of the -decibel option.  The decibel target then
Packit df99a1
    addresses only the average error of the specified fraction of the most
Packit df99a1
    misrepresented 32x32 pixel blocks.
Packit df99a1
    \item[-mask pbmfile] 
Packit df99a1
    This option can be used when we know that certain pixels of a background
Packit df99a1
    image are going to be covered by foreground objects like text or drawings.
Packit df99a1
    File #pbmfile# must be a PBM file whose size matches the size of the input
Packit df99a1
    file.  Each black pixel in #pbmfile# means that the value of the corresponding
Packit df99a1
    pixel in the input file is irrelevant.  The DjVu IW44 Encoder will replace
Packit df99a1
    the masked pixels by a color value whose coding cost is minimal (see
Packit df99a1
    \URL{http://www.research.att.com/~leonb/DJVU/mask}).
Packit df99a1
    \end{description}
Packit df99a1
Packit df99a1
    {\bf Photo DjVu options} ---
Packit df99a1
    Photo DjVu images have the additional capability to store the resolution
Packit df99a1
    and gamma correction information.
Packit df99a1
    \begin{description}
Packit df99a1
    \item[-dpi n]  Sets the resolution information for a Photo DjVu image.
Packit df99a1
    \item[-gamma n] Sets the gamma correction information for a Photo DjVu image.
Packit df99a1
    \end{description}
Packit df99a1
Packit df99a1
    {\bf Performance} ---
Packit df99a1
    The main design objective for the DjVu wavelets consisted of allowing
Packit df99a1
    progressive rendering and smooth scrolling of large images with limited
Packit df99a1
    memory requirements.  Decoding functions process the compressed data and
Packit df99a1
    update a memory efficient representation of the wavelet coefficients.
Packit df99a1
    Imaging function then can quickly render an arbitrary segment of the image
Packit df99a1
    using the available data.  Both process can be carried out in two threads
Packit df99a1
    of execution.  This design plays an important role in the DjVu system.
Packit df99a1
Packit df99a1
    We have investigated various state-of-the-art wavelet compression schemes:
Packit df99a1
    although these schemes may achieve slightly smaller file sizes, the
Packit df99a1
    decoding functions did not even approach our requirements.  The IW44
Packit df99a1
    wavelets reach these requirements today and may in the future implement
Packit df99a1
    more modern refinements (such as trellis quantization, bitrate
Packit df99a1
    allocation, etc.) if (and only if) these refinements can be implemented
Packit df99a1
    within our constraints.
Packit df99a1
Packit df99a1
    @memo
Packit df99a1
    DjVu IW44 wavelet encoder.
Packit df99a1
    @author
Packit df99a1
    L\'eon Bottou <leonb@research.att.com>
Packit df99a1
*/
Packit df99a1
//@{
Packit df99a1
//@}
Packit df99a1
Packit df99a1
#include "GString.h"
Packit df99a1
#include "GException.h"
Packit df99a1
#include "IW44Image.h"
Packit df99a1
#include "DjVuInfo.h"
Packit df99a1
#include "IFFByteStream.h"
Packit df99a1
#include "GOS.h"
Packit df99a1
#include "GBitmap.h"
Packit df99a1
#include "GPixmap.h"
Packit df99a1
#include "GURL.h"
Packit df99a1
#include "DjVuMessage.h"
Packit df99a1
#include "JPEGDecoder.h"
Packit df99a1
#include "common.h"
Packit df99a1
Packit df99a1
// command line data
Packit df99a1
Packit df99a1
int flag_mask = 0;
Packit df99a1
int flag_bpp = 0;
Packit df99a1
int flag_size = 0;
Packit df99a1
int flag_percent = 0;
Packit df99a1
int flag_slice = 0;
Packit df99a1
int flag_decibel = 0;
Packit df99a1
int flag_crcbdelay = -1;
Packit df99a1
int flag_crcbmode = -1;  
Packit df99a1
double flag_dbfrac = -1;
Packit df99a1
int flag_dpi = -1;
Packit df99a1
double flag_gamma = -1;
Packit df99a1
int argc_bpp = 0;
Packit df99a1
int argc_size = 0;
Packit df99a1
int argc_slice = 0;
Packit df99a1
int argc_decibel = 0;
Packit df99a1
IW44Image::CRCBMode arg_crcbmode = IW44Image::CRCBnormal;
Packit df99a1
Packit df99a1
#define MAXCHUNKS 64
Packit df99a1
float argv_bpp[MAXCHUNKS];
Packit df99a1
int   argv_size[MAXCHUNKS];
Packit df99a1
int   argv_slice[MAXCHUNKS];
Packit df99a1
float argv_decibel[MAXCHUNKS];
Packit df99a1
Packit df99a1
struct C44Global 
Packit df99a1
{
Packit df99a1
  // Globals that need static initialization
Packit df99a1
  // are grouped here to work around broken compilers.
Packit df99a1
  GURL pnmurl;
Packit df99a1
  GURL iw4url;
Packit df99a1
  GURL mskurl;
Packit df99a1
  IWEncoderParms parms[MAXCHUNKS];
Packit df99a1
}; 
Packit df99a1
Packit df99a1
static C44Global& g(void)
Packit df99a1
{ 
Packit df99a1
  static C44Global g; 
Packit df99a1
  return g;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// parse arguments
Packit df99a1
Packit df99a1
void 
Packit df99a1
usage()
Packit df99a1
{
Packit df99a1
  DjVuPrintErrorUTF8(
Packit df99a1
#ifdef DJVULIBRE_VERSION
Packit df99a1
         "C44 --- DjVuLibre-" DJVULIBRE_VERSION "\n"
Packit df99a1
#endif
Packit df99a1
         "Image compression utility using IW44 wavelets\n\n"
Packit df99a1
         "Usage: c44 [options] pnm-or-jpeg-file [djvufile]\n"
Packit df99a1
         "Options:\n"
Packit df99a1
         "    -slice n+...+n   -- select an increasing sequence of data slices\n"
Packit df99a1
         "                        expressed as integers ranging from 1 to 140.\n"
Packit df99a1
         "    -bpp n,..,n      -- select a increasing sequence of bitrates\n"
Packit df99a1
         "                        for building progressive file (in bits per pixel).\n"
Packit df99a1
         "    -size n,..,n     -- select an increasing sequence of minimal sizes\n"
Packit df99a1
         "                        for building progressive files (expressed in bytes).\n"
Packit df99a1
         "    -percent n,..,n  -- selects the percentage of original file size\n"
Packit df99a1
         "                        for building progressive file.\n"
Packit df99a1
         "    -decibel n,..,n  -- select an increasing sequence of luminance error\n"
Packit df99a1
         "                        expressed as decibels (ranging from 16 to 50).\n"
Packit df99a1
         "    -dbfrac frac     -- restrict decibel estimation to a fraction of\n"
Packit df99a1
         "                        the most misrepresented 32x32 blocks\n"
Packit df99a1
         "    -mask pbmfile    -- select bitmask specifying image zone to encode\n"
Packit df99a1
         "                        with minimal bitrate. (default none)\n"
Packit df99a1
         "    -dpi n           -- sets the image resolution\n"
Packit df99a1
         "    -gamma n         -- sets the image gamma correction\n"
Packit df99a1
         "    -crcbfull        -- encode chrominance with highest quality\n"
Packit df99a1
         "    -crcbnormal      -- encode chrominance with normal resolution (default)\n"
Packit df99a1
         "    -crcbhalf        -- encode chrominance with half resolution\n"
Packit df99a1
         "    -crcbnone        -- do not encode chrominance at all\n"
Packit df99a1
         "    -crcbdelay n     -- select chrominance coding delay (default 10)\n"
Packit df99a1
         "                        for -crcbnormal and -crcbhalf modes\n"
Packit df99a1
         "\n");
Packit df99a1
  exit(1);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
void 
Packit df99a1
parse_bpp(const char *q)
Packit df99a1
{
Packit df99a1
  flag_bpp = 1;
Packit df99a1
  argc_bpp = 0;
Packit df99a1
  double lastx = 0;
Packit df99a1
  while (*q)
Packit df99a1
    {
Packit df99a1
      char *ptr; 
Packit df99a1
      double x = strtod(q, &ptr);
Packit df99a1
      if (ptr == q)
Packit df99a1
        G_THROW( ERR_MSG("c44.bitrate_not_number") );
Packit df99a1
      if (lastx>0 && q[-1]=='+')
Packit df99a1
        x += lastx;
Packit df99a1
      if (x<=0 || x>24 || x
Packit df99a1
        G_THROW( ERR_MSG("c44.bitrate_out_of_range") );
Packit df99a1
      lastx = x;
Packit df99a1
      if (*ptr && *ptr!='+' && *ptr!=',')
Packit df99a1
        G_THROW( ERR_MSG("c44.bitrate_comma_expected") );
Packit df99a1
      q = (*ptr ? ptr+1 : ptr);
Packit df99a1
      argv_bpp[argc_bpp++] = (float)x;
Packit df99a1
      if (argc_bpp>MAXCHUNKS)
Packit df99a1
        G_THROW( ERR_MSG("c44.bitrate_too_many") );
Packit df99a1
    }
Packit df99a1
  if (argc_bpp < 1)
Packit df99a1
    G_THROW( ERR_MSG("c44.bitrate_no_chunks") );
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
void 
Packit df99a1
parse_size(const char *q)
Packit df99a1
{
Packit df99a1
  flag_size = 1;
Packit df99a1
  argc_size = 0;
Packit df99a1
  int lastx = 0;
Packit df99a1
  while (*q)
Packit df99a1
    {
Packit df99a1
      char *ptr; 
Packit df99a1
      int x = strtol(q, &ptr, 10);
Packit df99a1
      if (ptr == q)
Packit df99a1
        G_THROW( ERR_MSG("c44.size_not_number") );
Packit df99a1
      if (lastx>0 && q[-1]=='+')
Packit df99a1
        x += lastx;
Packit df99a1
      if (x
Packit df99a1
        G_THROW( ERR_MSG("c44.size_out_of_range") );
Packit df99a1
      lastx = x;
Packit df99a1
      if (*ptr && *ptr!='+' && *ptr!=',')
Packit df99a1
        G_THROW( ERR_MSG("c44.size_comma_expected") );
Packit df99a1
      q = (*ptr ? ptr+1 : ptr);
Packit df99a1
      argv_size[argc_size++] = x;
Packit df99a1
      if (argc_size>=MAXCHUNKS)
Packit df99a1
        G_THROW( ERR_MSG("c44.size_too_many") );
Packit df99a1
    }
Packit df99a1
  if (argc_size < 1)
Packit df99a1
    G_THROW( ERR_MSG("c44.size_no_chunks") );
Packit df99a1
}
Packit df99a1
Packit df99a1
void 
Packit df99a1
parse_slice(const char *q)
Packit df99a1
{
Packit df99a1
  flag_slice = 1;
Packit df99a1
  argc_slice = 0;
Packit df99a1
  int lastx = 0;
Packit df99a1
  while (*q)
Packit df99a1
    {
Packit df99a1
      char *ptr; 
Packit df99a1
      int x = strtol(q, &ptr, 10);
Packit df99a1
      if (ptr == q)
Packit df99a1
        G_THROW( ERR_MSG("c44.slice_not_number") );
Packit df99a1
      if (lastx>0 && q[-1]=='+')
Packit df99a1
        x += lastx;
Packit df99a1
      if (x<1 || x>1000 || x
Packit df99a1
        G_THROW( ERR_MSG("c44.slice_out_of_range") );
Packit df99a1
      lastx = x;
Packit df99a1
      if (*ptr && *ptr!='+' && *ptr!=',')
Packit df99a1
        G_THROW( ERR_MSG("c44.slice_comma_expected") );
Packit df99a1
      q = (*ptr ? ptr+1 : ptr);
Packit df99a1
      argv_slice[argc_slice++] = x;
Packit df99a1
      if (argc_slice>=MAXCHUNKS)
Packit df99a1
        G_THROW( ERR_MSG("c44.slice_too_many") );
Packit df99a1
    }
Packit df99a1
  if (argc_slice < 1)
Packit df99a1
    G_THROW( ERR_MSG("c44.slice_no_chunks") );
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
void 
Packit df99a1
parse_decibel(const char *q)
Packit df99a1
{
Packit df99a1
  flag_decibel = 1;
Packit df99a1
  argc_decibel = 0;
Packit df99a1
  double lastx = 0;
Packit df99a1
  while (*q)
Packit df99a1
    {
Packit df99a1
      char *ptr; 
Packit df99a1
      double x = strtod(q, &ptr);
Packit df99a1
      if (ptr == q)
Packit df99a1
        G_THROW( ERR_MSG("c44.decibel_not_number") );
Packit df99a1
      if (lastx>0 && q[-1]=='+')
Packit df99a1
        x += lastx;
Packit df99a1
      if (x<16 || x>50 || x
Packit df99a1
        G_THROW( ERR_MSG("c44.decibel_out_of_range") );
Packit df99a1
      lastx = x;
Packit df99a1
      if (*ptr && *ptr!='+' && *ptr!=',')
Packit df99a1
        G_THROW( ERR_MSG("c44.decibel_comma_expected") );
Packit df99a1
      q = (*ptr ? ptr+1 : ptr);
Packit df99a1
      argv_decibel[argc_decibel++] = (float)x;
Packit df99a1
      if (argc_decibel>=MAXCHUNKS)
Packit df99a1
        G_THROW( ERR_MSG("c44.decibel_too_many") );
Packit df99a1
    }
Packit df99a1
  if (argc_decibel < 1)
Packit df99a1
    G_THROW( ERR_MSG("c44.decibel_no_chunks") );
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
int 
Packit df99a1
resolve_quality(int npix)
Packit df99a1
{
Packit df99a1
  // Convert ratio specification into size specification
Packit df99a1
  if (flag_bpp)
Packit df99a1
    {
Packit df99a1
      if (flag_size)
Packit df99a1
        G_THROW( ERR_MSG("c44.exclusive") );
Packit df99a1
      flag_size = flag_bpp;
Packit df99a1
      argc_size = argc_bpp;
Packit df99a1
      for (int i=0; i
Packit df99a1
        argv_size[i] = (int)(npix*argv_bpp[i]/8.0+0.5);
Packit df99a1
    }
Packit df99a1
  // Compute number of chunks
Packit df99a1
  int nchunk = 0;
Packit df99a1
  if (flag_slice && nchunk
Packit df99a1
    nchunk = argc_slice;
Packit df99a1
  if (flag_size && nchunk
Packit df99a1
    nchunk = argc_size;
Packit df99a1
  if (flag_decibel && nchunk
Packit df99a1
    nchunk = argc_decibel;
Packit df99a1
  // Force default values
Packit df99a1
  if (nchunk == 0)
Packit df99a1
    {
Packit df99a1
#ifdef DECIBELS_25_30_34
Packit df99a1
      nchunk = 3;
Packit df99a1
      flag_decibel = 1;
Packit df99a1
      argc_decibel = 3;
Packit df99a1
      argv_decibel[0]=25;
Packit df99a1
      argv_decibel[1]=30;
Packit df99a1
      argv_decibel[2]=34;
Packit df99a1
#else
Packit df99a1
      nchunk = 3;
Packit df99a1
      flag_slice = 1;
Packit df99a1
      argc_slice = 3;
Packit df99a1
      argv_slice[0]=74;
Packit df99a1
      argv_slice[1]=89;
Packit df99a1
      argv_slice[2]=99;
Packit df99a1
#endif
Packit df99a1
    }
Packit df99a1
  // Complete short specifications
Packit df99a1
  while (argc_size < nchunk)
Packit df99a1
    argv_size[argc_size++] = 0;
Packit df99a1
  while (argc_slice < nchunk)
Packit df99a1
    argv_slice[argc_slice++] = 0;
Packit df99a1
  while (argc_decibel < nchunk)
Packit df99a1
    argv_decibel[argc_decibel++] = 0.0;
Packit df99a1
  // Fill parm structure
Packit df99a1
  for(int i=0; i
Packit df99a1
    {
Packit df99a1
      g().parms[i].bytes = argv_size[i];
Packit df99a1
      g().parms[i].slices = argv_slice[i];
Packit df99a1
      g().parms[i].decibels = argv_decibel[i];
Packit df99a1
    }
Packit df99a1
  // Return number of chunks
Packit df99a1
  return nchunk;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
void
Packit df99a1
parse(GArray<GUTF8String> &argv)
Packit df99a1
{
Packit df99a1
  const int argc=argv.hbound()+1;
Packit df99a1
  for (int i=1; i
Packit df99a1
    {
Packit df99a1
      if (argv[i][0] == '-')
Packit df99a1
        {
Packit df99a1
          if (argv[i] == "-percent")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_bpp_arg") );
Packit df99a1
              if (flag_bpp || flag_size)
Packit df99a1
                G_THROW( ERR_MSG("c44.multiple_bitrate") );
Packit df99a1
              parse_size(argv[i]);
Packit df99a1
              flag_percent = 1;
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-bpp")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_bpp_arg") );
Packit df99a1
              if (flag_bpp || flag_size)
Packit df99a1
                G_THROW( ERR_MSG("c44.multiple_bitrate") );
Packit df99a1
              parse_bpp(argv[i]);
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-size")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_size_arg") );
Packit df99a1
              if (flag_bpp || flag_size)
Packit df99a1
                G_THROW( ERR_MSG("c44.multiple_size") );
Packit df99a1
              parse_size(argv[i]);
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-decibel")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_decibel_arg") );
Packit df99a1
              if (flag_decibel)
Packit df99a1
                G_THROW( ERR_MSG("c44.multiple_decibel") );
Packit df99a1
              parse_decibel(argv[i]);
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-slice")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_slice_arg") );
Packit df99a1
              if (flag_slice)
Packit df99a1
                G_THROW( ERR_MSG("c44.multiple_slice") );
Packit df99a1
              parse_slice(argv[i]);
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-mask")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_mask_arg") );
Packit df99a1
              if (! g().mskurl.is_empty())
Packit df99a1
                G_THROW( ERR_MSG("c44.multiple_mask") );
Packit df99a1
              g().mskurl = GURL::Filename::UTF8(argv[i]);
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-dbfrac")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_dbfrac_arg") );
Packit df99a1
              if (flag_dbfrac>0)
Packit df99a1
                G_THROW( ERR_MSG("c44.multiple_dbfrac") );
Packit df99a1
              char *ptr;
Packit df99a1
              flag_dbfrac = strtod(argv[i], &ptr);
Packit df99a1
              if (flag_dbfrac<=0 || flag_dbfrac>1 || *ptr)
Packit df99a1
                G_THROW( ERR_MSG("c44.illegal_dbfrac") );
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-crcbnone")
Packit df99a1
            {
Packit df99a1
              if (flag_crcbmode>=0 || flag_crcbdelay>=0)
Packit df99a1
                G_THROW( ERR_MSG("c44.incompatable_chrominance") );
Packit df99a1
              flag_crcbdelay = flag_crcbmode = 0;
Packit df99a1
              arg_crcbmode = IW44Image::CRCBnone;
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-crcbhalf")
Packit df99a1
            {
Packit df99a1
              if (flag_crcbmode>=0)
Packit df99a1
                G_THROW( ERR_MSG("c44.incompatable_chrominance") );
Packit df99a1
              flag_crcbmode = 0;
Packit df99a1
              arg_crcbmode = IW44Image::CRCBhalf;
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-crcbnormal")
Packit df99a1
            {
Packit df99a1
              if (flag_crcbmode>=0)
Packit df99a1
                G_THROW( ERR_MSG("c44.incompatable_chrominance") );
Packit df99a1
              flag_crcbmode = 0;
Packit df99a1
              arg_crcbmode = IW44Image::CRCBnormal;
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-crcbfull")
Packit df99a1
            {
Packit df99a1
              if (flag_crcbmode>=0 || flag_crcbdelay>=0)
Packit df99a1
                G_THROW( ERR_MSG("c44.incompatable_chrominance") );
Packit df99a1
              flag_crcbdelay = flag_crcbmode = 0;
Packit df99a1
              arg_crcbmode = IW44Image::CRCBfull;
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-crcbdelay")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_crcbdelay_arg") );
Packit df99a1
              if (flag_crcbdelay>=0)
Packit df99a1
                G_THROW( ERR_MSG("c44.incompatable_chrominance") );
Packit df99a1
              char *ptr; 
Packit df99a1
              flag_crcbdelay = strtol(argv[i], &ptr, 10);
Packit df99a1
              if (*ptr || flag_crcbdelay<0 || flag_crcbdelay>=100)
Packit df99a1
                G_THROW( ERR_MSG("c44.illegal_crcbdelay") );
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-dpi")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_dpi_arg") );
Packit df99a1
              if (flag_dpi>0)
Packit df99a1
                G_THROW( ERR_MSG("c44.duplicate_dpi") );
Packit df99a1
              char *ptr; 
Packit df99a1
              flag_dpi = strtol(argv[i], &ptr, 10);
Packit df99a1
              if (*ptr || flag_dpi<25 || flag_dpi>4800)
Packit df99a1
                G_THROW( ERR_MSG("c44.illegal_dpi") );
Packit df99a1
            }
Packit df99a1
          else if (argv[i] == "-gamma")
Packit df99a1
            {
Packit df99a1
              if (++i >= argc)
Packit df99a1
                G_THROW( ERR_MSG("c44.no_gamma_arg") );
Packit df99a1
              if (flag_gamma > 0)
Packit df99a1
                G_THROW( ERR_MSG("c44.duplicate_gamma") );
Packit df99a1
              char *ptr; 
Packit df99a1
              flag_gamma = strtod(argv[i], &ptr);
Packit df99a1
              if (*ptr || flag_gamma<=0.25 || flag_gamma>=5)
Packit df99a1
                G_THROW( ERR_MSG("c44.illegal_gamma") );
Packit df99a1
            }
Packit df99a1
          else
Packit df99a1
            usage();
Packit df99a1
        }
Packit df99a1
      else if (g().pnmurl.is_empty())
Packit df99a1
        g().pnmurl = GURL::Filename::UTF8(argv[i]);
Packit df99a1
      else if (g().iw4url.is_empty())
Packit df99a1
        g().iw4url = GURL::Filename::UTF8(argv[i]);
Packit df99a1
      else
Packit df99a1
        usage();
Packit df99a1
    }
Packit df99a1
  if (g().pnmurl.is_empty())
Packit df99a1
    usage();
Packit df99a1
  if (g().iw4url.is_empty())
Packit df99a1
    {
Packit df99a1
      GURL codebase=g().pnmurl.base();
Packit df99a1
      GUTF8String base = g().pnmurl.fname();
Packit df99a1
      int dot = base.rsearch('.');
Packit df99a1
      if (dot >= 1)
Packit df99a1
        base = base.substr(0,dot);
Packit df99a1
      const char *ext=".djvu";
Packit df99a1
      g().iw4url = GURL::UTF8(base+ext,codebase);
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
GP<GBitmap>
Packit df99a1
getmask(int w, int h)
Packit df99a1
{
Packit df99a1
  GP<GBitmap> msk8;
Packit df99a1
  if (! g().mskurl.is_empty())
Packit df99a1
    {
Packit df99a1
      GP<ByteStream> mbs=ByteStream::create(g().mskurl,"rb");
Packit df99a1
      msk8 = GBitmap::create(*mbs);
Packit df99a1
      if (msk8->columns() != (unsigned int)w || 
Packit df99a1
          msk8->rows()    != (unsigned int)h  )
Packit df99a1
        G_THROW( ERR_MSG("c44.different_size") );
Packit df99a1
    }
Packit df99a1
  return msk8;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
static void 
Packit df99a1
create_photo_djvu_file(IW44Image &iw, int w, int h,
Packit df99a1
                       IFFByteStream &iff, int nchunks, IWEncoderParms xparms[])
Packit df99a1
{
Packit df99a1
  // Prepare info chunk
Packit df99a1
  GP<DjVuInfo> ginfo=DjVuInfo::create();
Packit df99a1
  DjVuInfo &info=*ginfo;
Packit df99a1
  info.width = w;
Packit df99a1
  info.height = h;
Packit df99a1
  info.dpi = (flag_dpi>0 ? flag_dpi : 100);
Packit df99a1
  info.gamma = (flag_gamma>0 ? flag_gamma : 2.2);
Packit df99a1
  // Write djvu header and info chunk
Packit df99a1
  iff.put_chunk("FORM:DJVU", 1);
Packit df99a1
  iff.put_chunk("INFO");
Packit df99a1
  info.encode(*iff.get_bytestream());
Packit df99a1
  iff.close_chunk();
Packit df99a1
  // Write all chunks
Packit df99a1
  int flag = 1;
Packit df99a1
  for (int i=0; flag && i
Packit df99a1
    {
Packit df99a1
      iff.put_chunk("BG44");
Packit df99a1
      flag = iw.encode_chunk(iff.get_bytestream(), xparms[i]);
Packit df99a1
      iff.close_chunk();
Packit df99a1
    }
Packit df99a1
  // Close djvu chunk
Packit df99a1
  iff.close_chunk();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
int
Packit df99a1
main(int argc, char **argv)
Packit df99a1
{
Packit df99a1
  DJVU_LOCALE;
Packit df99a1
  GArray<GUTF8String> dargv(0,argc-1);
Packit df99a1
  for(int i=0;i
Packit df99a1
    dargv[i]=GNativeString(argv[i]);
Packit df99a1
  G_TRY
Packit df99a1
    {
Packit df99a1
      // Parse arguments
Packit df99a1
      parse(dargv);
Packit df99a1
      // Check input file
Packit df99a1
      GP<ByteStream> gibs=ByteStream::create(g().pnmurl,"rb");
Packit df99a1
      ByteStream &ibs=*gibs;
Packit df99a1
      char prefix[16];
Packit df99a1
      memset(prefix, 0, sizeof(prefix));
Packit df99a1
      if (ibs.readall((void*)prefix, sizeof(prefix)) < sizeof(prefix))
Packit df99a1
        G_THROW( ERR_MSG("c44.failed_pnm_header") );
Packit df99a1
#ifdef DEFAULT_JPEG_TO_HALF_SIZE
Packit df99a1
      // Default specification for jpeg files
Packit df99a1
      // This is disabled because
Packit df99a1
      // -1- jpeg detection is unreliable.
Packit df99a1
      // -2- quality is very difficult to predict.
Packit df99a1
      if(prefix[0]!='P' &&prefix[0]!='A' && prefix[0]!='F' && 
Packit df99a1
	 !flag_mask && !flag_bpp && !flag_size && 
Packit df99a1
	 !flag_slice && !flag_decibel)
Packit df99a1
        {
Packit df99a1
          parse_size("10,20,30,50");
Packit df99a1
	  flag_size = flag_percent = 1;
Packit df99a1
        }
Packit df99a1
#endif
Packit df99a1
      // Change percent specification into size specification
Packit df99a1
      if (flag_size && flag_percent)
Packit df99a1
	for (int i=0; i
Packit df99a1
	  argv_size[i] = (argv_size[i]*gibs->size())/ 100;
Packit df99a1
      flag_percent = 0;
Packit df99a1
      // Load images
Packit df99a1
      int w = 0;
Packit df99a1
      int h = 0;
Packit df99a1
      ibs.seek(0);
Packit df99a1
      GP<IW44Image> iw;
Packit df99a1
      // Check color vs gray
Packit df99a1
      if (prefix[0]=='P' && (prefix[1]=='2' || prefix[1]=='5'))
Packit df99a1
        {
Packit df99a1
          // gray file
Packit df99a1
          GP<GBitmap> gibm=GBitmap::create(ibs);
Packit df99a1
          GBitmap &ibm=*gibm;
Packit df99a1
          w = ibm.columns();
Packit df99a1
          h = ibm.rows();
Packit df99a1
          iw = IW44Image::create_encode(ibm, getmask(w,h));
Packit df99a1
        }
Packit df99a1
      else if (!GStringRep::cmp(prefix,"AT&TFORM",8) || 
Packit df99a1
	       !GStringRep::cmp(prefix,"FORM",4))
Packit df99a1
        {
Packit df99a1
          char *s = (prefix[0]=='F' ? prefix+8 : prefix+12);
Packit df99a1
          GP<IFFByteStream> giff=IFFByteStream::create(gibs);
Packit df99a1
          IFFByteStream &iff=*giff;
Packit df99a1
          const bool color=!GStringRep::cmp(s,"PM44",4);
Packit df99a1
          if (color || !GStringRep::cmp(s,"BM44",4))
Packit df99a1
            {
Packit df99a1
              iw = IW44Image::create_encode(IW44Image::COLOR);
Packit df99a1
              iw->decode_iff(iff);
Packit df99a1
              w = iw->get_width();
Packit df99a1
              h = iw->get_height();
Packit df99a1
            }
Packit df99a1
          else
Packit df99a1
            G_THROW( ERR_MSG("c44.unrecognized") );
Packit df99a1
          // Check that no mask has been specified.
Packit df99a1
          if (! g().mskurl.is_empty())
Packit df99a1
            G_THROW( ERR_MSG("c44.failed_mask") );
Packit df99a1
        }
Packit df99a1
      else  // just for kicks, try jpeg.
Packit df99a1
        {
Packit df99a1
          // color file
Packit df99a1
          const GP<GPixmap> gipm(GPixmap::create(ibs));
Packit df99a1
          GPixmap &ipm=*gipm;
Packit df99a1
          w = ipm.columns();
Packit df99a1
          h = ipm.rows();
Packit df99a1
          iw = IW44Image::create_encode(ipm, getmask(w,h), arg_crcbmode);
Packit df99a1
        }
Packit df99a1
      // Call destructor on input file
Packit df99a1
      gibs=0;
Packit df99a1
              
Packit df99a1
      // Perform compression PM44 or BM44 as required
Packit df99a1
      if (iw)
Packit df99a1
        {
Packit df99a1
          g().iw4url.deletefile();
Packit df99a1
          GP<IFFByteStream> iff =
Packit df99a1
	    IFFByteStream::create(ByteStream::create(g().iw4url,"wb"));
Packit df99a1
          if (flag_crcbdelay >= 0)
Packit df99a1
            iw->parm_crcbdelay(flag_crcbdelay);
Packit df99a1
          if (flag_dbfrac > 0)
Packit df99a1
            iw->parm_dbfrac((float)flag_dbfrac);
Packit df99a1
          int nchunk = resolve_quality(w*h);
Packit df99a1
          // Create djvu file
Packit df99a1
          create_photo_djvu_file(*iw, w, h, *iff, nchunk, g().parms);
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  G_CATCH(ex)
Packit df99a1
    {
Packit df99a1
      ex.perror();
Packit df99a1
      exit(1);
Packit df99a1
    }
Packit df99a1
  G_ENDCATCH;
Packit df99a1
  return 0;
Packit df99a1
}