void cjb2(const GURL &urlin, const GURL &urlout, cjb2opts &opts) { GP<ByteStream> ibs=ByteStream::create(urlin, "rb"); CCImage rimg; #if HAVE_TIFF if (is_tiff(ibs)) read_tiff(rimg, ibs, opts); else #endif { GP<GBitmap> input=GBitmap::create(*ibs); rimg.init(input->columns(), input->rows(), opts.dpi); rimg.add_bitmap_runs(*input); } if (opts.verbose) DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("cjb2.runs"), rimg.runs.size() ); // Component analysis rimg.make_ccids_by_analysis(); // obtain ccids rimg.make_ccs_from_ccids(); // compute cc descriptors if (opts.verbose) DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("cjb2.ccs_before"), rimg.ccs.size()); if (opts.losslevel > 0) rimg.erase_tiny_ccs(); // clean rimg.merge_and_split_ccs(); // reorganize weird ccs rimg.sort_in_reading_order(); // sort cc descriptors if (opts.verbose) DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("cjb2.ccs_after"), rimg.ccs.size()); // Pattern matching GP<JB2Image> jimg = rimg.get_jb2image(); // get ``raw'' jb2image rimg.runs.empty(); // save memory rimg.ccs.empty(); // save memory if (opts.losslevel>1) tune_jb2image_lossy(jimg, opts.dpi, opts.losslevel); else tune_jb2image_lossless(jimg); if (opts.verbose) { int nshape=0, nrefine=0; for (int i=0; i<jimg->get_shape_count(); i++) { if (!jimg->get_shape(i).bits) continue; if (jimg->get_shape(i).parent >= 0) nrefine++; nshape++; } DjVuFormatErrorUTF8( "%s\t%d\t%d", ERR_MSG("cjb2.shapes"), nshape, nrefine); } // Code GP<ByteStream> obs=ByteStream::create(urlout, "wb"); GP<IFFByteStream> giff=IFFByteStream::create(obs); IFFByteStream &iff=*giff; // -- main composite chunk iff.put_chunk("FORM:DJVU", 1); // -- ``INFO'' chunk GP<DjVuInfo> ginfo=DjVuInfo::create(); DjVuInfo &info=*ginfo; info.height = rimg.height; info.width = rimg.width; info.dpi = opts.dpi; iff.put_chunk("INFO"); info.encode(*iff.get_bytestream()); iff.close_chunk(); // -- ``Sjbz'' chunk iff.put_chunk("Sjbz"); jimg->encode(iff.get_bytestream()); iff.close_chunk(); // -- terminate main composite chunk iff.close_chunk(); // Finished! }
static void read_tiff(CCImage &rimg, ByteStream *bs, cjb2opts &opts) { TIFF *tiff = TIFFClientOpen("libtiff", "rm", (thandle_t)bs, readproc, writeproc, seekproc, closeproc, sizeproc, mapproc, unmapproc ); // bitonal uint16 bps = 0, spp = 0; TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &bps); TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &spp); if (bps != 1 || spp != 1) G_THROW("Tiff image is not bitonal"); // photometric uint16 photo = PHOTOMETRIC_MINISWHITE; TIFFGetFieldDefaulted(tiff, TIFFTAG_PHOTOMETRIC, &photo); // image size uint32 w, h; if (!TIFFGetFieldDefaulted(tiff, TIFFTAG_IMAGEWIDTH, &w) || !TIFFGetFieldDefaulted(tiff, TIFFTAG_IMAGELENGTH, &h) ) G_THROW("Tiff image size is not defined"); // resolution float xres, yres; if (TIFFGetFieldDefaulted(tiff, TIFFTAG_XRESOLUTION, &xres) && TIFFGetFieldDefaulted(tiff, TIFFTAG_YRESOLUTION, &yres) ) { if (xres != yres) DjVuPrintErrorUTF8( "cjb2: X- and Y-resolution do not match\n"); if (! opts.forcedpi) opts.dpi = (int) (xres + yres) / 2; } // init rimg rimg.init(w, h, opts.dpi); // allocate scanline tsize_t scanlinesize = TIFFScanlineSize(tiff); scanlinesize = MAX(scanlinesize,1); unsigned char *scanline = 0; GPBuffer<unsigned char> gscanline(scanline, scanlinesize); // iterate on rows for (int y=0; y<(int)h; y++) { int yy = h - y - 1; if (TIFFReadScanline(tiff, (tdata_t)scanline, y) < 0) G_THROW("Tiff file is corrupted (TIFFReadScanline)"); if (photo != PHOTOMETRIC_MINISWHITE) for (int i=0; i<(int)scanlinesize; i++) scanline[i] ^= 0xff; int lastx=0, off=0; unsigned char mask=0, c=0, b=0; for (int x=0; x<(int)w; x++) { if (! mask) { b = scanline[off++]; while (b==c && x+8<(int)w ) { x = x + 8; // speedup b = scanline[off++]; } mask = 0x80; } if ( (b ^ c) & mask ) { c ^= 0xff; if (c) lastx = x; else rimg.add_single_run(yy, lastx, x-1); } mask >>= 1; } if (c) rimg.add_single_run(yy, lastx, w-1); } // close TIFFClose(tiff); }