Blame tools/cjb2.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 cjb2
Packit df99a1
Packit df99a1
    {\bf Synopsis}
Packit df99a1
    \begin{verbatim}
Packit df99a1
        cjb2 [options] <input-pbm-or-tiff>  <output-djvu>
Packit df99a1
    \end{verbatim}
Packit df99a1
Packit df99a1
    {\bf Description}
Packit df99a1
Packit df99a1
    File #"cjb2.cpp"# demonstrates a simple encoder for Bilevel DjVu Images.
Packit df99a1
    It is able to perform lossless encoding and limited lossy encoding.  Lots
Packit df99a1
    of lossy encoding refinements are missing from this simple implementation.
Packit df99a1
    Comments in the code suggest a few improvements.
Packit df99a1
Packit df99a1
    Options are:
Packit df99a1
    \begin{description}
Packit df99a1
    \item[-dpi xxx]     Specify image resolution (default 300).
Packit df99a1
    \item[-lossless]    Lossless compression (same as -losslevel 0, default).
Packit df99a1
    \item[-clean]       Quasi-lossless compression (same as -losslevel 1).
Packit df99a1
    \item[-lossy]       Lossy compression (same as -losslevel 100).
Packit df99a1
    \item[-losslevel n] Set loss level (0 to 200)
Packit df99a1
    \item[-verbose]     Display additional messages.
Packit df99a1
    \end{description}
Packit df99a1
    Encoding is lossless unless one or several lossy options are selected.
Packit df99a1
    The #dpi# argument mostly affects the cleaning thresholds.
Packit df99a1
Packit df99a1
    {\bf Bugs}
Packit df99a1
Packit df99a1
    This is not the full-fledged multipage DjVu compressor, but merely a free
Packit df99a1
    tool provided with the DjVu Reference Library as a demonstrative example.
Packit df99a1
Packit df99a1
    @memo
Packit df99a1
    Simple JB2 encoder.
Packit df99a1
    @author
Packit df99a1
    L\'eon Bottou <leonb@research.att.com>\\
Packit df99a1
    Paul Howard <pgh@research.att.com>\\
Packit df99a1
    Pascal Vincent <vincentp@iro.umontreal.ca>\\
Packit df99a1
    Ilya Mezhirov <ilya@mezhirov.mccme.ru>
Packit df99a1
*/
Packit df99a1
//@{
Packit df99a1
//@}
Packit df99a1
Packit df99a1
Packit df99a1
#include "DjVuGlobal.h"
Packit df99a1
#include "GException.h"
Packit df99a1
#include "GSmartPointer.h"
Packit df99a1
#include "GContainer.h"
Packit df99a1
#include "ByteStream.h"
Packit df99a1
#include "IFFByteStream.h"
Packit df99a1
#include "GRect.h"
Packit df99a1
#include "GBitmap.h"
Packit df99a1
#include "JB2Image.h"
Packit df99a1
#include "DjVuInfo.h"
Packit df99a1
#include "GOS.h"
Packit df99a1
#include "GURL.h"
Packit df99a1
#include "DjVuMessage.h"
Packit df99a1
#include "jb2tune.h"
Packit df99a1
#include "common.h"
Packit df99a1
#if HAVE_TIFF
Packit df99a1
#include <tiffio.h>
Packit df99a1
#endif
Packit df99a1
Packit df99a1
// --------------------------------------------------
Packit df99a1
// UTILITIES
Packit df99a1
// --------------------------------------------------
Packit df99a1
Packit df99a1
#ifdef MIN
Packit df99a1
#undef MIN
Packit df99a1
#endif
Packit df99a1
Packit df99a1
inline int 
Packit df99a1
MIN(int a, int b) 
Packit df99a1
{ 
Packit df99a1
  return ( a
Packit df99a1
}
Packit df99a1
Packit df99a1
#ifdef MAX
Packit df99a1
#undef MAX
Packit df99a1
#endif
Packit df99a1
Packit df99a1
inline int 
Packit df99a1
MAX(int a, int b) 
Packit df99a1
{ 
Packit df99a1
  return ( a>b ?a :b); 
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// --------------------------------------------------
Packit df99a1
// CONNECTED COMPONENT ANALYSIS AND CLEANING
Packit df99a1
// --------------------------------------------------
Packit df99a1
Packit df99a1
// -- A run of black pixels
Packit df99a1
struct Run    
Packit df99a1
{ 
Packit df99a1
  int y;         // vertical coordinate
Packit df99a1
  short x1;      // first horizontal coordinate
Packit df99a1
  short x2;      // last horizontal coordinate
Packit df99a1
  int ccid;      // component id
Packit df99a1
};
Packit df99a1
Packit df99a1
// -- A component descriptor
Packit df99a1
struct CC    
Packit df99a1
{
Packit df99a1
  GRect bb;      // bounding box
Packit df99a1
  int npix;      // number of black pixels
Packit df99a1
  int nrun;      // number of runs
Packit df99a1
  int frun;      // first run in cc ordered array of runs
Packit df99a1
};
Packit df99a1
Packit df99a1
Packit df99a1
// -- An image composed of runs
Packit df99a1
class CCImage 
Packit df99a1
{
Packit df99a1
public:
Packit df99a1
  int height;            // Height of the image in pixels
Packit df99a1
  int width;             // Width of the image in pixels
Packit df99a1
  GTArray<Run> runs;     // array of runs
Packit df99a1
  GTArray<CC>  ccs;      // Array of component descriptors
Packit df99a1
  int nregularccs;       // Number of regular ccs (set by merge_and_split_ccs)
Packit df99a1
  int largesize;         // CCs larger than that are special
Packit df99a1
  int smallsize;         // CCs smaller than that are special 
Packit df99a1
  int tinysize;          // CCs smaller than that may be removed 
Packit df99a1
  CCImage();
Packit df99a1
  void init(int width, int height, int dpi);
Packit df99a1
  void add_single_run(int y, int x1, int x2, int ccid=0);
Packit df99a1
  void add_bitmap_runs(const GBitmap &bm, int offx=0, int offy=0, int ccid=0);
Packit df99a1
  GP<GBitmap> get_bitmap_for_cc(int ccid) const;
Packit df99a1
  GP<JB2Image> get_jb2image() const;
Packit df99a1
  void make_ccids_by_analysis();
Packit df99a1
  void make_ccs_from_ccids();
Packit df99a1
  void erase_tiny_ccs();
Packit df99a1
  void merge_and_split_ccs();
Packit df99a1
  void sort_in_reading_order(); 
Packit df99a1
};
Packit df99a1
Packit df99a1
Packit df99a1
// -- Compares runs
Packit df99a1
static inline bool
Packit df99a1
operator <= (const Run &a, const Run &b)
Packit df99a1
{
Packit df99a1
  return (a.y
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Constructs CCImage and provide defaults
Packit df99a1
CCImage::CCImage()
Packit df99a1
  : height(0), width(0), nregularccs(0)
Packit df99a1
{
Packit df99a1
}
Packit df99a1
Packit df99a1
void
Packit df99a1
CCImage::init(int w, int h, int dpi)
Packit df99a1
{
Packit df99a1
  runs.empty();
Packit df99a1
  ccs.empty();
Packit df99a1
  height = h;
Packit df99a1
  width = w;
Packit df99a1
  nregularccs = 0;
Packit df99a1
  dpi = MAX(200, MIN(900, dpi));
Packit df99a1
  largesize = MIN( 500, MAX(64, dpi));
Packit df99a1
  smallsize = MAX(2, dpi/150);
Packit df99a1
  tinysize = MAX(0, dpi*dpi/20000 - 1);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Adds a run to the CCImage
Packit df99a1
inline void 
Packit df99a1
CCImage::add_single_run(int y, int x1, int x2, int ccid)
Packit df99a1
{
Packit df99a1
  int index = runs.hbound();
Packit df99a1
  runs.touch(++index);
Packit df99a1
  Run& run = runs[index];
Packit df99a1
  run.y = y;
Packit df99a1
  run.x1 = x1;
Packit df99a1
  run.x2 = x2;
Packit df99a1
  run.ccid = ccid;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Adds runs extracted from a bitmap
Packit df99a1
void 
Packit df99a1
CCImage::add_bitmap_runs(const GBitmap &bm, int offx, int offy, int ccid)
Packit df99a1
{
Packit df99a1
  // Iterate over rows
Packit df99a1
  for (unsigned int y=0; y
Packit df99a1
    {
Packit df99a1
      const unsigned char *row = bm[y];
Packit df99a1
      int w = bm.columns();
Packit df99a1
      int x = 0;
Packit df99a1
      // Iterate over runs
Packit df99a1
      while (x < w)
Packit df99a1
        {
Packit df99a1
          while (x < w  && !row[x]) x++;
Packit df99a1
          if (x < w)
Packit df99a1
            {
Packit df99a1
              int x1 = x;
Packit df99a1
              while (x < w && row[x]) x++;
Packit df99a1
              add_single_run(offy+y, offx+x1, offx+x-1, ccid);
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Performs connected component analysis
Packit df99a1
void
Packit df99a1
CCImage::make_ccids_by_analysis()
Packit df99a1
{
Packit df99a1
  // Sort runs
Packit df99a1
  runs.sort();
Packit df99a1
  // Single Pass Connected Component Analysis (with unodes)
Packit df99a1
  int n;
Packit df99a1
  int p=0;
Packit df99a1
  GTArray<int> umap;
Packit df99a1
  for (n=0; n<=runs.hbound(); n++)
Packit df99a1
    {
Packit df99a1
      int y = runs[n].y;
Packit df99a1
      int x1 = runs[n].x1 - 1;
Packit df99a1
      int x2 = runs[n].x2 + 1;
Packit df99a1
      int id = (umap.hbound() + 1);
Packit df99a1
      // iterate over previous line runs
Packit df99a1
      for(;runs[p].y < y-1;p++);
Packit df99a1
      for(;(runs[p].y < y) && (runs[p].x1 <= x2);p++ )
Packit df99a1
        {
Packit df99a1
          if ( runs[p].x2 >= x1 )
Packit df99a1
            {
Packit df99a1
              // previous run touches current run
Packit df99a1
              int oid = runs[p].ccid;
Packit df99a1
              while (umap[oid] < oid)
Packit df99a1
                oid = umap[oid];
Packit df99a1
              if ((int)id > umap.hbound()) {
Packit df99a1
                id = oid;
Packit df99a1
              } else if (id < oid) {
Packit df99a1
                umap[oid] = id;
Packit df99a1
              } else {
Packit df99a1
                umap[id] = oid;
Packit df99a1
                id = oid;
Packit df99a1
              }
Packit df99a1
              // freshen previous run id
Packit df99a1
              runs[p].ccid = id;
Packit df99a1
              // stop if previous run goes past current run
Packit df99a1
              if (runs[p].x2 >= x2)
Packit df99a1
                break;
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
      // create new entry in umap
Packit df99a1
      runs[n].ccid = id;
Packit df99a1
      if (id > umap.hbound())
Packit df99a1
        {
Packit df99a1
          umap.touch(id);
Packit df99a1
          umap[id] = id;
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  // Update umap and ccid
Packit df99a1
  for (n=0; n<=runs.hbound(); n++)
Packit df99a1
    {
Packit df99a1
      Run &run = runs[n];
Packit df99a1
      int ccid = run.ccid;
Packit df99a1
      while (umap[ccid] < ccid)
Packit df99a1
      {
Packit df99a1
        ccid = umap[ccid];
Packit df99a1
      }
Packit df99a1
      umap[run.ccid] = ccid;
Packit df99a1
      run.ccid = ccid;
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Constructs the ``ccs'' array from run's ccids.
Packit df99a1
void
Packit df99a1
CCImage::make_ccs_from_ccids()
Packit df99a1
{
Packit df99a1
  int n;
Packit df99a1
  Run *pruns = runs;
Packit df99a1
  // Find maximal ccid
Packit df99a1
  int maxccid = nregularccs-1;
Packit df99a1
  for (n=0; n<=runs.hbound(); n++)
Packit df99a1
    if (pruns[n].ccid > maxccid)
Packit df99a1
      maxccid = runs[n].ccid;
Packit df99a1
  // Renumber ccs 
Packit df99a1
  GTArray<int> armap(0,maxccid);
Packit df99a1
  int *rmap = armap;
Packit df99a1
  for (n=0; n<=maxccid; n++)
Packit df99a1
    armap[n] = -1;
Packit df99a1
  for (n=0; n<=runs.hbound(); n++)
Packit df99a1
    if (pruns[n].ccid >= 0)
Packit df99a1
      rmap[ pruns[n].ccid ] = 1;
Packit df99a1
  int nid = 0;
Packit df99a1
  for (n=0; n<=maxccid; n++)
Packit df99a1
    if (rmap[n] > 0)
Packit df99a1
      rmap[n] = nid++;
Packit df99a1
  
Packit df99a1
  // Adjust nregularccs (since ccs are renumbered)
Packit df99a1
  while (nregularccs>0 && rmap[nregularccs-1]<0)
Packit df99a1
    nregularccs -= 1;
Packit df99a1
  if (nregularccs>0)
Packit df99a1
    nregularccs = 1 + rmap[nregularccs-1];
Packit df99a1
  
Packit df99a1
  // Prepare cc descriptors
Packit df99a1
  ccs.resize(0,nid-1);
Packit df99a1
  for (n=0; n
Packit df99a1
    ccs[n].nrun = 0;
Packit df99a1
  
Packit df99a1
  // Relabel runs
Packit df99a1
  for (n=0; n<=runs.hbound(); n++)
Packit df99a1
    {
Packit df99a1
      Run &run = pruns[n];
Packit df99a1
      if (run.ccid < 0) continue;  // runs with negative ccids are destroyed
Packit df99a1
      int oldccid = run.ccid;
Packit df99a1
      int newccid = rmap[oldccid];
Packit df99a1
      CC &cc = ccs[newccid];
Packit df99a1
      run.ccid = newccid;
Packit df99a1
      cc.nrun += 1;
Packit df99a1
    }
Packit df99a1
  
Packit df99a1
  // Compute positions for runs of cc
Packit df99a1
  int frun = 0;
Packit df99a1
  for (n=0; n
Packit df99a1
    {
Packit df99a1
      ccs[n].frun = rmap[n] = frun;
Packit df99a1
      frun += ccs[n].nrun;
Packit df99a1
    }
Packit df99a1
Packit df99a1
  // Copy runs
Packit df99a1
  GTArray<Run> rtmp;
Packit df99a1
  rtmp.steal(runs);
Packit df99a1
  Run *ptmp = rtmp;
Packit df99a1
  runs.resize(0,frun-1);
Packit df99a1
  pruns = runs;
Packit df99a1
  for (n=0; n<=rtmp.hbound(); n++)
Packit df99a1
    {
Packit df99a1
      int id = ptmp[n].ccid;
Packit df99a1
      if (id < 0) continue;
Packit df99a1
      int pos = rmap[id]++;
Packit df99a1
      pruns[pos] = ptmp[n];
Packit df99a1
    }
Packit df99a1
Packit df99a1
  // Finalize ccs
Packit df99a1
  for (n=0; n
Packit df99a1
    {
Packit df99a1
      CC &cc = ccs[n];
Packit df99a1
      int npix = 0;
Packit df99a1
      runs.sort(cc.frun, cc.frun+cc.nrun-1);
Packit df99a1
      Run *run = &runs[cc.frun];
Packit df99a1
      int xmin = run->x1;
Packit df99a1
      int xmax = run->x2;
Packit df99a1
      int ymin = run->y;
Packit df99a1
      int ymax = run->y;
Packit df99a1
      for (int i=0; i
Packit df99a1
        {
Packit df99a1
          if (run->x1 < xmin)  xmin = run->x1;
Packit df99a1
          if (run->x2 > xmax)  xmax = run->x2;
Packit df99a1
          if (run->y  < ymin)  ymin = run->y;
Packit df99a1
          if (run->y  > ymax)  ymax = run->y;
Packit df99a1
          npix += run->x2 - run->x1 + 1;
Packit df99a1
        }
Packit df99a1
      cc.npix = npix;
Packit df99a1
      cc.bb.xmin = xmin;
Packit df99a1
      cc.bb.ymin = ymin;
Packit df99a1
      cc.bb.xmax = xmax + 1;
Packit df99a1
      cc.bb.ymax = ymax + 1;
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// Removes ccs which are too small.
Packit df99a1
void
Packit df99a1
CCImage::erase_tiny_ccs()
Packit df99a1
{
Packit df99a1
  // ISSUE: HALFTONE DETECTION
Packit df99a1
  // We should not remove tiny ccs if they are part of a halftone pattern...
Packit df99a1
  for (int i=0; i
Packit df99a1
    {
Packit df99a1
      CC& cc = ccs[i];
Packit df99a1
      if (cc.npix <= tinysize)
Packit df99a1
        {
Packit df99a1
          // Mark cc to be erased
Packit df99a1
          Run *r = &runs[cc.frun];
Packit df99a1
          int nr = cc.nrun;
Packit df99a1
          cc.nrun = 0;
Packit df99a1
          cc.npix = 0;
Packit df99a1
          while (--nr >= 0)
Packit df99a1
            (r++)->ccid = -1;
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
}
Packit df99a1
 
Packit df99a1
Packit df99a1
// -- Merges small ccs and split large ccs
Packit df99a1
void
Packit df99a1
CCImage::merge_and_split_ccs()
Packit df99a1
{
Packit df99a1
  int ncc = ccs.size();
Packit df99a1
  int nruns = runs.size();
Packit df99a1
  int splitsize = largesize;
Packit df99a1
  if (ncc <= 0) return;
Packit df99a1
  // Grid of special components
Packit df99a1
  int gridwidth = (width+splitsize-1)/splitsize;
Packit df99a1
  nregularccs = ncc;
Packit df99a1
  // Set the correct ccids for the runs
Packit df99a1
  for (int ccid=0; ccid
Packit df99a1
    {
Packit df99a1
      CC* cc = &ccs[ccid];
Packit df99a1
      if (cc->nrun <= 0) continue;
Packit df99a1
      int ccheight = cc->bb.height();
Packit df99a1
      int ccwidth = cc->bb.width();
Packit df99a1
      if (ccheight<=smallsize && ccwidth<=smallsize)
Packit df99a1
        {
Packit df99a1
          int gridi = (cc->bb.ymin+cc->bb.ymax)/splitsize/2;
Packit df99a1
          int gridj = (cc->bb.xmin+cc->bb.xmax)/splitsize/2;
Packit df99a1
          int newccid = ncc + gridi*gridwidth + gridj;
Packit df99a1
          for(int runid=cc->frun; runid<cc->frun+cc->nrun; runid++)
Packit df99a1
            runs[runid].ccid = newccid;
Packit df99a1
        }
Packit df99a1
      else if (ccheight>=largesize || ccwidth>=largesize)
Packit df99a1
        {
Packit df99a1
          for(int runid=cc->frun; runid<cc->frun+cc->nrun; runid++)
Packit df99a1
            {
Packit df99a1
              Run& r = runs[runid];
Packit df99a1
              int y = r.y;
Packit df99a1
              int x_start = r.x1;
Packit df99a1
              int x_end = r.x2;
Packit df99a1
              int gridi = y/splitsize;
Packit df99a1
              int gridj_start = x_start/splitsize;
Packit df99a1
              int gridj_end = x_end/splitsize;
Packit df99a1
              int gridj_span = gridj_end-gridj_start;
Packit df99a1
              int newccid = ncc + gridi*gridwidth + gridj_start;
Packit df99a1
              if (! gridj_span)
Packit df99a1
                {
Packit df99a1
                  r.ccid = newccid;
Packit df99a1
                }
Packit df99a1
              else // gridj_span>0
Packit df99a1
                {
Packit df99a1
                  // truncate the current run 
Packit df99a1
                  r.ccid = newccid++;
Packit df99a1
                  int x = (gridj_start+1)*splitsize;
Packit df99a1
                  r.x2 = x-1;
Packit df99a1
                  runs.touch(nruns+gridj_span-1);
Packit df99a1
                  // append additional runs to the runs array
Packit df99a1
                  for(int gridj=gridj_start+1; gridj
Packit df99a1
                    {
Packit df99a1
                      Run& newrun = runs[nruns++];
Packit df99a1
                      newrun.y = y;
Packit df99a1
                      newrun.x1 = x;
Packit df99a1
                      x += splitsize;
Packit df99a1
                      newrun.x2 = x-1;
Packit df99a1
                      newrun.ccid = newccid++;
Packit df99a1
                    }
Packit df99a1
                  // append last run to the run array
Packit df99a1
                  Run& newrun = runs[nruns++];
Packit df99a1
                  newrun.y = y;
Packit df99a1
                  newrun.x1 = x;
Packit df99a1
                  newrun.x2 = x_end;
Packit df99a1
                  newrun.ccid = newccid++;                      
Packit df99a1
                }
Packit df99a1
            }
Packit df99a1
        }
Packit df99a1
    }
Packit df99a1
  // Recompute cc descriptors
Packit df99a1
  make_ccs_from_ccids();
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Helps sorting cc
Packit df99a1
static int 
Packit df99a1
top_edges_descending (const void *pa, const void *pb)
Packit df99a1
{
Packit df99a1
  if (((CC*) pa)->bb.ymax != ((CC*) pb)->bb.ymax)
Packit df99a1
    return (((CC*) pb)->bb.ymax - ((CC*) pa)->bb.ymax);
Packit df99a1
  if (((CC*) pa)->bb.xmin != ((CC*) pb)->bb.xmin)
Packit df99a1
    return (((CC*) pa)->bb.xmin - ((CC*) pb)->bb.xmin);
Packit df99a1
  return (((CC*) pa)->frun - ((CC*) pb)->frun);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Helps sorting cc
Packit df99a1
static int 
Packit df99a1
left_edges_ascending (const void *pa, const void *pb)
Packit df99a1
{
Packit df99a1
  if (((CC*) pa)->bb.xmin != ((CC*) pb)->bb.xmin)
Packit df99a1
    return (((CC*) pa)->bb.xmin - ((CC*) pb)->bb.xmin);
Packit df99a1
  if (((CC*) pb)->bb.ymax != ((CC*) pa)->bb.ymax)
Packit df99a1
    return (((CC*) pb)->bb.ymax - ((CC*) pa)->bb.ymax);
Packit df99a1
  return (((CC*) pa)->frun - ((CC*) pb)->frun);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Helps sorting cc
Packit df99a1
static int 
Packit df99a1
integer_ascending (const void *pa, const void *pb)
Packit df99a1
{
Packit df99a1
  return ( *(int*)pb - *(int*)pa );
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Sort ccs in approximate reading order
Packit df99a1
void 
Packit df99a1
CCImage::sort_in_reading_order()
Packit df99a1
{
Packit df99a1
  if (nregularccs<2) return;
Packit df99a1
  CC *ccarray = new CC[nregularccs];
Packit df99a1
  // Copy existing ccarray (but segregate special ccs)
Packit df99a1
  int ccid;
Packit df99a1
  for(ccid=0; ccid
Packit df99a1
    ccarray[ccid] = ccs[ccid];
Packit df99a1
  // Sort the ccarray list into top-to-bottom order.
Packit df99a1
  qsort (ccarray, nregularccs, sizeof(CC), top_edges_descending);
Packit df99a1
  // Subdivide the ccarray list roughly into text lines [LYB]
Packit df99a1
  // - Determine maximal top deviation
Packit df99a1
  int maxtopchange = width / 40;
Packit df99a1
  if (maxtopchange < 32) 
Packit df99a1
    maxtopchange = 32;
Packit df99a1
  // - Loop until processing all ccs
Packit df99a1
  int ccno = 0;
Packit df99a1
  int *bottoms = new int[nregularccs];
Packit df99a1
  while (ccno < nregularccs)
Packit df99a1
    {
Packit df99a1
      // - Gather first line approximation
Packit df99a1
      int nccno;
Packit df99a1
      int sublist_top = ccarray[ccno].bb.ymax-1;
Packit df99a1
      int sublist_bottom = ccarray[ccno].bb.ymin;
Packit df99a1
      for (nccno=ccno; nccno < nregularccs; nccno++)
Packit df99a1
        {
Packit df99a1
          if (ccarray[nccno].bb.ymax-1 < sublist_bottom) break;
Packit df99a1
          if (ccarray[nccno].bb.ymax-1 < sublist_top - maxtopchange) break;
Packit df99a1
          int bottom = ccarray[nccno].bb.ymin;
Packit df99a1
          bottoms[nccno-ccno] = bottom;
Packit df99a1
          if (bottom < sublist_bottom)
Packit df99a1
            sublist_bottom = bottom;
Packit df99a1
        }
Packit df99a1
      // - If more than one candidate cc for the line
Packit df99a1
      if (nccno > ccno + 1)
Packit df99a1
        {
Packit df99a1
          // - Compute median bottom
Packit df99a1
          qsort(bottoms, nccno-ccno, sizeof(int), integer_ascending);
Packit df99a1
          int bottom = bottoms[ (nccno-ccno-1)/2 ];
Packit df99a1
          // - Compose final line
Packit df99a1
          for (nccno=ccno; nccno < nregularccs; nccno++)
Packit df99a1
            if (ccarray[nccno].bb.ymax-1 < bottom)
Packit df99a1
              break;
Packit df99a1
          // - Sort final line
Packit df99a1
          qsort (ccarray+ccno, nccno-ccno, sizeof(CC), left_edges_ascending);
Packit df99a1
        }
Packit df99a1
      // - Next line
Packit df99a1
      ccno = nccno;
Packit df99a1
    }
Packit df99a1
  // Copy ccarray back and renumber the runs
Packit df99a1
  for(ccid=0; ccid
Packit df99a1
    {
Packit df99a1
      CC& cc = ccarray[ccid];
Packit df99a1
      ccs[ccid] = cc;
Packit df99a1
      for(int r=cc.frun; r
Packit df99a1
        runs[r].ccid = ccid;
Packit df99a1
    }
Packit df99a1
  // Free memory
Packit df99a1
  delete [] bottoms;
Packit df99a1
  delete[] ccarray;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Creates a bitmap for a particular component
Packit df99a1
GP<GBitmap>   
Packit df99a1
CCImage::get_bitmap_for_cc(const int ccid) const
Packit df99a1
{
Packit df99a1
  const CC &cc = ccs[ccid];
Packit df99a1
  const GRect &bb = cc.bb;
Packit df99a1
  GP<GBitmap> bits = GBitmap::create(bb.height(), bb.width());
Packit df99a1
  const Run *prun = & runs[(int)cc.frun];
Packit df99a1
  for (int i=0; i
Packit df99a1
    {
Packit df99a1
      if (prun->y<bb.ymin || prun->y>=bb.ymax)
Packit df99a1
        G_THROW("Internal error (y bounds)");
Packit df99a1
      if (prun->x1<bb.xmin || prun->x2>=bb.xmax)
Packit df99a1
        G_THROW("Internal error (x bounds)");
Packit df99a1
      unsigned char *row = (*bits)[prun->y - bb.ymin];
Packit df99a1
      for (int x=prun->x1; x<=prun->x2; x++)
Packit df99a1
        row[x - bb.xmin] = 1;
Packit df99a1
    }
Packit df99a1
  return bits;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
// -- Creates a JB2Image with the remaining components
Packit df99a1
GP<JB2Image> 
Packit df99a1
CCImage::get_jb2image() const
Packit df99a1
{
Packit df99a1
  GP<JB2Image> jimg = JB2Image::create();
Packit df99a1
  jimg->set_dimension(width, height);
Packit df99a1
  if (runs.hbound() < 0)
Packit df99a1
    return jimg;
Packit df99a1
  if (ccs.hbound() < 0)
Packit df99a1
    G_THROW("Must first perform a cc analysis");
Packit df99a1
  // Iterate over CCs
Packit df99a1
  for (int ccid=0; ccid<=ccs.hbound(); ccid++)
Packit df99a1
    {
Packit df99a1
      JB2Shape shape;
Packit df99a1
      JB2Blit  blit;
Packit df99a1
      shape.parent = -1;
Packit df99a1
      shape.bits = get_bitmap_for_cc(ccid);
Packit df99a1
      shape.userdata = 0;
Packit df99a1
      if (ccid >= nregularccs)
Packit df99a1
        shape.userdata |= JB2SHAPE_SPECIAL;
Packit df99a1
      blit.shapeno = jimg->add_shape(shape);
Packit df99a1
      blit.left = ccs[ccid].bb.xmin;
Packit df99a1
      blit.bottom = ccs[ccid].bb.ymin;
Packit df99a1
      jimg->add_blit(blit);
Packit df99a1
      shape.bits->compress();
Packit df99a1
    }
Packit df99a1
  // Return
Packit df99a1
  return jimg;
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// --------------------------------------------------
Packit df99a1
// COMPLETE COMPRESSION ROUTINE
Packit df99a1
// --------------------------------------------------
Packit df99a1
Packit df99a1
struct cjb2opts {
Packit df99a1
  int  dpi;
Packit df99a1
  int  forcedpi;
Packit df99a1
  int  losslevel;
Packit df99a1
  bool verbose;
Packit df99a1
};
Packit df99a1
Packit df99a1
#if HAVE_TIFF
Packit df99a1
Packit df99a1
static int 
Packit df99a1
is_tiff(ByteStream *ref) 
Packit df99a1
{
Packit df99a1
  char magic[2];
Packit df99a1
  magic[0] = magic[1] = 0;
Packit df99a1
  ref->readall((void*)magic, sizeof(magic));
Packit df99a1
  ref->seek(0);
Packit df99a1
  if(magic[0] == 'I' && magic[1] == 'I')
Packit df99a1
    return 1;
Packit df99a1
  if(magic[0] == 'M' && magic[1] == 'M')
Packit df99a1
    return 1;
Packit df99a1
  return 0;
Packit df99a1
}
Packit df99a1
Packit df99a1
static tsize_t readproc(thandle_t h, tdata_t p, tsize_t s) { 
Packit df99a1
  ByteStream *bs = (ByteStream*)h;
Packit df99a1
  return (tsize_t) bs->readall((void*)p, (size_t)s);
Packit df99a1
}
Packit df99a1
Packit df99a1
static tsize_t writeproc(thandle_t, tdata_t, tsize_t) { 
Packit df99a1
  return -1; 
Packit df99a1
}
Packit df99a1
Packit df99a1
static toff_t seekproc(thandle_t h, toff_t offset, int mode) { 
Packit df99a1
  ByteStream *bs = (ByteStream*)h;
Packit df99a1
  bs->seek((long)offset, mode);
Packit df99a1
  return (toff_t)bs->tell(); 
Packit df99a1
}
Packit df99a1
Packit df99a1
static int closeproc(thandle_t) { 
Packit df99a1
  return 0; 
Packit df99a1
}
Packit df99a1
Packit df99a1
static toff_t sizeproc(thandle_t h) { 
Packit df99a1
  ByteStream *bs = (ByteStream*)h;
Packit df99a1
  return (toff_t) bs->size(); 
Packit df99a1
}
Packit df99a1
Packit df99a1
static int mapproc(thandle_t, tdata_t*, toff_t*) { 
Packit df99a1
  return -1; 
Packit df99a1
}
Packit df99a1
Packit df99a1
static void unmapproc(thandle_t, tdata_t, toff_t) { 
Packit df99a1
}
Packit df99a1
Packit df99a1
static void
Packit df99a1
read_tiff(CCImage &rimg, ByteStream *bs, cjb2opts &opts)
Packit df99a1
{
Packit df99a1
  TIFF *tiff = TIFFClientOpen("libtiff", "rm", (thandle_t)bs,
Packit df99a1
                              readproc, writeproc, seekproc,
Packit df99a1
                              closeproc, sizeproc, 
Packit df99a1
                              mapproc, unmapproc );
Packit df99a1
  // bitonal
Packit df99a1
  uint16 bps = 0, spp = 0;
Packit df99a1
  TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &bps;;
Packit df99a1
  TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &spp;;
Packit df99a1
  if (bps != 1 || spp != 1)
Packit df99a1
    G_THROW("Tiff image is not bitonal");
Packit df99a1
  // photometric
Packit df99a1
  uint16 photo = PHOTOMETRIC_MINISWHITE;
Packit df99a1
  TIFFGetFieldDefaulted(tiff, TIFFTAG_PHOTOMETRIC, &photo);
Packit df99a1
  // image size
Packit df99a1
  uint32 w, h;
Packit df99a1
  if (!TIFFGetFieldDefaulted(tiff, TIFFTAG_IMAGEWIDTH, &w) ||
Packit df99a1
      !TIFFGetFieldDefaulted(tiff, TIFFTAG_IMAGELENGTH, &h) )
Packit df99a1
    G_THROW("Tiff image size is not defined");
Packit df99a1
  // resolution
Packit df99a1
  float xres, yres;
Packit df99a1
  if (TIFFGetFieldDefaulted(tiff, TIFFTAG_XRESOLUTION, &xres) &&
Packit df99a1
      TIFFGetFieldDefaulted(tiff, TIFFTAG_YRESOLUTION, &yres) ) 
Packit df99a1
    {
Packit df99a1
      if (xres != yres)
Packit df99a1
        DjVuPrintErrorUTF8( "cjb2: X- and Y-resolution do not match\n");
Packit df99a1
      if (! opts.forcedpi)
Packit df99a1
        opts.dpi = (int) (xres + yres) / 2;
Packit df99a1
    }
Packit df99a1
  // init rimg
Packit df99a1
  rimg.init(w, h, opts.dpi);
Packit df99a1
  // allocate scanline
Packit df99a1
  tsize_t scanlinesize = TIFFScanlineSize(tiff);
Packit df99a1
  scanlinesize = MAX(scanlinesize,1);
Packit df99a1
  unsigned char *scanline = 0;
Packit df99a1
  GPBuffer<unsigned char> gscanline(scanline, scanlinesize);
Packit df99a1
  // iterate on rows
Packit df99a1
  for (int y=0; y<(int)h; y++)
Packit df99a1
    {
Packit df99a1
      int yy = h - y - 1;
Packit df99a1
      if (TIFFReadScanline(tiff, (tdata_t)scanline, y) < 0)
Packit df99a1
        G_THROW("Tiff file is corrupted (TIFFReadScanline)");
Packit df99a1
      if (photo != PHOTOMETRIC_MINISWHITE)
Packit df99a1
        for (int i=0; i<(int)scanlinesize; i++)
Packit df99a1
          scanline[i] ^= 0xff;
Packit df99a1
      int lastx=0, off=0;
Packit df99a1
      unsigned char mask=0, c=0, b=0;
Packit df99a1
      for (int x=0; x<(int)w; x++)
Packit df99a1
        {
Packit df99a1
          if (! mask) 
Packit df99a1
            {
Packit df99a1
              b = scanline[off++];
Packit df99a1
              while (b==c && x+8<(int)w )
Packit df99a1
                {
Packit df99a1
                  x = x + 8;  // speedup
Packit df99a1
                  b = scanline[off++];
Packit df99a1
                }
Packit df99a1
              mask = 0x80;
Packit df99a1
            }
Packit df99a1
          if ( (b ^ c) & mask ) 
Packit df99a1
            {
Packit df99a1
              c ^= 0xff;
Packit df99a1
              if (c)
Packit df99a1
                lastx = x;
Packit df99a1
              else
Packit df99a1
                rimg.add_single_run(yy, lastx, x-1);
Packit df99a1
            }
Packit df99a1
          mask >>= 1;
Packit df99a1
        }
Packit df99a1
      if (c)
Packit df99a1
        rimg.add_single_run(yy, lastx, w-1);
Packit df99a1
    }
Packit df99a1
  // close
Packit df99a1
  TIFFClose(tiff);
Packit df99a1
}
Packit df99a1
Packit df99a1
#endif // HAVE_TIFF
Packit df99a1
Packit df99a1
Packit df99a1
void 
Packit df99a1
cjb2(const GURL &urlin, const GURL &urlout, cjb2opts &opts)
Packit df99a1
{
Packit df99a1
  GP<ByteStream> ibs=ByteStream::create(urlin, "rb");
Packit df99a1
  CCImage rimg;
Packit df99a1
Packit df99a1
#if HAVE_TIFF
Packit df99a1
  if (is_tiff(ibs))
Packit df99a1
    read_tiff(rimg, ibs, opts);
Packit df99a1
  else
Packit df99a1
#endif
Packit df99a1
    {
Packit df99a1
      GP<GBitmap> input=GBitmap::create(*ibs);
Packit df99a1
      rimg.init(input->columns(), input->rows(), opts.dpi);
Packit df99a1
      rimg.add_bitmap_runs(*input); 
Packit df99a1
    }
Packit df99a1
  if (opts.verbose)
Packit df99a1
    DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("cjb2.runs"), 
Packit df99a1
                         rimg.runs.size() );
Packit df99a1
  
Packit df99a1
  // Component analysis
Packit df99a1
  rimg.make_ccids_by_analysis(); // obtain ccids
Packit df99a1
  rimg.make_ccs_from_ccids();    // compute cc descriptors
Packit df99a1
  if (opts.verbose)
Packit df99a1
    DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("cjb2.ccs_before"), 
Packit df99a1
                         rimg.ccs.size());
Packit df99a1
  if (opts.losslevel > 0) 
Packit df99a1
    rimg.erase_tiny_ccs();       // clean
Packit df99a1
  rimg.merge_and_split_ccs();    // reorganize weird ccs
Packit df99a1
  rimg.sort_in_reading_order();  // sort cc descriptors
Packit df99a1
  if (opts.verbose)
Packit df99a1
    DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("cjb2.ccs_after"), 
Packit df99a1
                         rimg.ccs.size());
Packit df99a1
  
Packit df99a1
  // Pattern matching
Packit df99a1
  GP<JB2Image> jimg = rimg.get_jb2image();          // get ``raw'' jb2image
Packit df99a1
  rimg.runs.empty();                                // save memory
Packit df99a1
  rimg.ccs.empty();                                 // save memory
Packit df99a1
  if (opts.losslevel>1)
Packit df99a1
    tune_jb2image_lossy(jimg, opts.dpi, opts.losslevel);
Packit df99a1
  else
Packit df99a1
    tune_jb2image_lossless(jimg);
Packit df99a1
  if (opts.verbose)
Packit df99a1
    {
Packit df99a1
      int nshape=0, nrefine=0;
Packit df99a1
      for (int i=0; i<jimg->get_shape_count(); i++) {
Packit df99a1
        if (!jimg->get_shape(i).bits) continue;
Packit df99a1
        if (jimg->get_shape(i).parent >= 0) nrefine++; 
Packit df99a1
        nshape++; 
Packit df99a1
      }
Packit df99a1
      DjVuFormatErrorUTF8( "%s\t%d\t%d", ERR_MSG("cjb2.shapes"), 
Packit df99a1
                           nshape, nrefine);
Packit df99a1
    }
Packit df99a1
  
Packit df99a1
  // Code
Packit df99a1
  GP<ByteStream> obs=ByteStream::create(urlout, "wb");
Packit df99a1
  GP<IFFByteStream> giff=IFFByteStream::create(obs);
Packit df99a1
  IFFByteStream &iff=*giff;
Packit df99a1
  // -- main composite chunk
Packit df99a1
  iff.put_chunk("FORM:DJVU", 1);
Packit df99a1
  // -- ``INFO'' chunk
Packit df99a1
  GP<DjVuInfo> ginfo=DjVuInfo::create();
Packit df99a1
  DjVuInfo &info=*ginfo;
Packit df99a1
  info.height = rimg.height;
Packit df99a1
  info.width = rimg.width;
Packit df99a1
  info.dpi = opts.dpi;
Packit df99a1
  iff.put_chunk("INFO");
Packit df99a1
  info.encode(*iff.get_bytestream());
Packit df99a1
  iff.close_chunk();
Packit df99a1
  // -- ``Sjbz'' chunk
Packit df99a1
  iff.put_chunk("Sjbz");
Packit df99a1
  jimg->encode(iff.get_bytestream());
Packit df99a1
  iff.close_chunk();
Packit df99a1
  // -- terminate main composite chunk
Packit df99a1
  iff.close_chunk();
Packit df99a1
  // Finished!
Packit df99a1
}
Packit df99a1
      
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
Packit df99a1
// --------------------------------------------------
Packit df99a1
// MAIN
Packit df99a1
// --------------------------------------------------
Packit df99a1
Packit df99a1
void
Packit df99a1
usage()
Packit df99a1
{
Packit df99a1
  DjVuPrintErrorUTF8(
Packit df99a1
#ifdef DJVULIBRE_VERSION
Packit df99a1
         "CJB2 --- DjVuLibre-" DJVULIBRE_VERSION "\n"
Packit df99a1
#endif
Packit df99a1
         "Simple DjVuBitonal encoder\n\n"
Packit df99a1
         "Usage: cjb2 [options] <input-pbm-or-tiff> <output-djvu>\n"
Packit df99a1
         "Options are:\n"
Packit df99a1
         " -verbose        Display additional messages.\n"
Packit df99a1
         " -dpi <n>        Specify image resolution (default 300).\n"
Packit df99a1
         " -clean          Cleanup image by removing small flyspecks.\n"
Packit df99a1
         " -lossy          Lossy compression (implies -clean as well)\n"
Packit df99a1
         " -losslevel <n>  Loss factor (implies -lossy, default 100)\n"
Packit df99a1
         "Encoding is lossless unless a lossy options is selected.\n" );
Packit df99a1
  exit(10);
Packit df99a1
}
Packit df99a1
Packit df99a1
Packit df99a1
int 
Packit df99a1
main(int argc, const 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
      GURL inputpbmurl;
Packit df99a1
      GURL outputdjvuurl;
Packit df99a1
      cjb2opts opts;
Packit df99a1
      // Defaults
Packit df99a1
      opts.forcedpi = 0;
Packit df99a1
      opts.dpi = 300;
Packit df99a1
      opts.losslevel = 0;
Packit df99a1
      opts.verbose = false;
Packit df99a1
      // Parse options
Packit df99a1
      for (int i=1; i
Packit df99a1
        {
Packit df99a1
          GUTF8String arg = dargv[i];
Packit df99a1
          if (arg == "-dpi" && i+1
Packit df99a1
            {
Packit df99a1
              char *end;
Packit df99a1
              opts.dpi = opts.forcedpi = strtol(dargv[++i], &end, 10);
Packit df99a1
              if (*end || opts.dpi<25 || opts.dpi>1200)
Packit df99a1
                usage();
Packit df99a1
            }
Packit df99a1
          else if (arg == "-losslevel")
Packit df99a1
            {
Packit df99a1
              char *end;
Packit df99a1
              opts.losslevel = strtol(dargv[++i], &end, 10);
Packit df99a1
              if (*end || opts.losslevel<0 || opts.losslevel>200)
Packit df99a1
                usage();
Packit df99a1
            }
Packit df99a1
          else if (arg == "-lossless")
Packit df99a1
            opts.losslevel = 0;
Packit df99a1
          else if (arg == "-lossy")
Packit df99a1
            opts.losslevel = 100;
Packit df99a1
          else if (arg == "-clean") // almost deprecated
Packit df99a1
            opts.losslevel = 1;
Packit df99a1
          else if (arg == "-verbose" || arg == "-v")
Packit df99a1
            opts.verbose = true;
Packit df99a1
          else if (arg[0] == '-' && arg[1])
Packit df99a1
            usage();
Packit df99a1
          else if (inputpbmurl.is_empty())
Packit df99a1
            inputpbmurl = GURL::Filename::UTF8(arg);
Packit df99a1
          else if (outputdjvuurl.is_empty())
Packit df99a1
            outputdjvuurl = GURL::Filename::UTF8(arg);
Packit df99a1
          else
Packit df99a1
            usage();
Packit df99a1
        }
Packit df99a1
      if (inputpbmurl.is_empty() || outputdjvuurl.is_empty())
Packit df99a1
        usage();
Packit df99a1
      // Execute
Packit df99a1
      cjb2(inputpbmurl, outputdjvuurl, opts);
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
}
Packit df99a1