|
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 |
|