コード例 #1
0
ファイル: djvumake.cpp プロジェクト: smyts/tengwa-djvu
void 
usage()
{
  DjVuPrintErrorUTF8(
#ifdef DJVULIBRE_VERSION
         "DJVUMAKE --- DjVuLibre-" DJVULIBRE_VERSION "\n"
#endif
         "Utility for manually assembling DjVu files\n\n"
         "Usage: djvumake djvufile ...arguments...\n"
         "\n"
         "The arguments describe the successive chunks of the DJVU file.\n"
         "Possible arguments are:\n"
         "\n"
         "   INFO=w[,[h[,[dpi]]]]     --  Create the initial information chunk\n"
         "   Sjbz=jb2file             --  Create a JB2 mask chunk\n"
         "   Djbz=jb2file             --  Create a JB2 shape dictionary\n"
         "   Smmr=mmrfile             --  Create a MMR mask chunk\n"
         "   BG44=[iw4file][:nchunks] --  Create one or more IW44 background chunks\n"
         "   BGjp=jpegfile            --  Create a JPEG background chunk\n"
         "   BG2k=jpeg2000file        --  Create a JP2K background chunk\n"
         "   FG44=iw4file             --  Create an IW44 foreground chunk\n"
         "   FGbz=bzzfile             --  Create a foreground color chunk from a file\n"
         "   FGbz={#color:x,y,w,h}    --  Create a foreground color chunk from zones\n"
         "   FGjp=jpegfile            --  Create a JPEG foreground image chunk\n"
         "   FG2k=jpeg2000file        --  Create a JP2K foreground image chunk\n"
         "   INCL=fileid              --  Create an INCL chunk\n"
         "   chunk=rawdatafile        --  Create the specified chunk from the raw data file\n"
         "   PPM=ppmfile              --  Create IW44 foreground and background chunks\n"
         "                                by masking and subsampling a PPM file.\n"
         "\n"
         "You may omit the specification of the information chunk. An information\n"
         "chunk will be created using the image size of the first mask chunk\n"
         "This program is sometimes able  to issue a warning when you are building an\n"
         "incorrect djvu file.\n"
         "\n");
  exit(1);
}
コード例 #2
0
ファイル: djvuxmlparser.cpp プロジェクト: traycold/djvulibre
static void
nofile(char *s)
{
    DjVuPrintErrorUTF8("Error: File '%s' does not exist.\n",s);
    exit(1);
}
コード例 #3
0
ファイル: djvuxmlparser.cpp プロジェクト: traycold/djvulibre
static void
usage(char *argv0)
{
    DjVuPrintErrorUTF8("Usage: %s [-o <djvufile>] <xmlfile> ...\n", argv0);
    exit(1);
}
コード例 #4
0
ファイル: djvumake.cpp プロジェクト: smyts/tengwa-djvu
int
main(int argc, char **argv)
{
  setlocale(LC_ALL,"");
  setlocale(LC_NUMERIC,"C");
  djvu_programname(argv[0]);
  GArray<GUTF8String> dargv(0,argc-1);
  for(int i=0;i<argc;++i)
    dargv[i]=GNativeString(argv[i]);
  G_TRY
    {
      // Print usage when called without enough arguments
      if (argc <= 2)
        usage();
      // Open djvu file
      remove(dargv[1]);
      GP<IFFByteStream> giff = 
        IFFByteStream::create(ByteStream::create(GURL::Filename::UTF8(dargv[1]),"wb"));
      IFFByteStream &iff=*giff;
      // Create header
      iff.put_chunk("FORM:DJVU", 1);
      // Check if shared dicts are present
      check_for_shared_dict(dargv);
      // Create information chunk
      create_info_chunk(iff, dargv);
      // Parse all arguments
      for (int i=2; i<argc; i++)
        {
          if (!dargv[i].cmp("INFO=",5))
            {
              if (i>2)
                DjVuPrintErrorUTF8("%s","djvumake: 'INFO' chunk should appear first (ignored)\n");
            }
          else if (!dargv[i].cmp("Sjbz=",5))
            {
              if (flag_contains_stencil)
                DjVuPrintErrorUTF8("%s","djvumake: duplicate stencil chunk\n");
              create_jb2_chunk(iff, "Sjbz", GURL::Filename::UTF8(5+(const char *)dargv[i]));
              flag_contains_stencil = 1;
              if (flag_fg_needs_palette && blit_count >= 0)
                create_fgbz_chunk(iff);
              flag_fg_needs_palette = 0;
            }
          else if (!dargv[i].cmp("Smmr=",5))
            {
              create_mmr_chunk(iff, "Smmr", 
			       GURL::Filename::UTF8(5+(const char *)dargv[i]));
              if (flag_contains_stencil)
                DjVuPrintErrorUTF8("%s","djvumake: duplicate stencil chunk\n");
              flag_contains_stencil = 1;
            }
          else if (!dargv[i].cmp("FGbz=",5))
            {
              const char *c = 5 + (const char*)dargv[i];
              if (flag_contains_fg)
                DjVuPrintErrorUTF8("%s","djvumake: duplicate 'FGxx' chunk\n");
              if (c[0] != '#')
                {
                  create_raw_chunk(iff, "FGbz", GURL::Filename::UTF8(c));
                }
              else
                {
                  parse_color_zones(c);
                  if (flag_contains_stencil && blit_count >= 0)
                    create_fgbz_chunk(iff);
                  else
                    flag_fg_needs_palette = 1;
                }
              flag_contains_fg = 1;
            }
          else if (!dargv[i].cmp("FG44=",5))
            {
              if (flag_contains_fg)
                DjVuPrintErrorUTF8("%s","djvumake: duplicate 'FGxx' chunk\n");
              create_fg44_chunk(iff, "FG44", 
				GURL::Filename::UTF8(5+(const char *)dargv[i]));
            }
          else if (!dargv[i].cmp("BG44=",5))
            {
              create_bg44_chunk(iff, "BG44", 5+(const char *)dargv[i]);
            }
          else if (!dargv[i].cmp("BGjp=",5) ||
                   !dargv[i].cmp("BG2k=",5)  )
            {
              if (flag_contains_bg)
                DjVuPrintErrorUTF8("%s","djvumake: Duplicate BGxx chunk\n");
              GUTF8String chkid = dargv[i].substr(0,4);
              create_raw_chunk(iff, chkid, GURL::Filename::UTF8(5+(const char *)dargv[i]));
              flag_contains_bg = 1;
            }
          else if (!dargv[i].cmp("FGjp=",5) ||  !dargv[i].cmp("FG2k=",5))
            {
              if (flag_contains_fg)
                DjVuPrintErrorUTF8("%s","djvumake: duplicate 'FGxx' chunk\n");
              GUTF8String chkid = dargv[i].substr(0,4);
              create_raw_chunk(iff, chkid, GURL::Filename::UTF8(5+(const char *)dargv[i]));
              flag_contains_fg = 1;
            }
          else if (!dargv[i].cmp("INCL=",5))
            {
              create_incl_chunk(iff, "INCL", GURL::Filename::UTF8(5+(const char *)dargv[i]).fname());
              flag_contains_incl = 1;
            }
          else if (!dargv[i].cmp("PPM=",4))
            {
              if (flag_contains_bg || flag_contains_fg)
                DjVuPrintErrorUTF8("%s","djvumake: Duplicate 'FGxx' or 'BGxx' chunk\n");
              create_masksub_chunks(iff, GURL::Filename::UTF8(4+(const char *)dargv[i]));
              flag_contains_bg = 1;
              flag_contains_fg = 1;
            }
          else if (dargv[i].length() > 4 && dargv[i][4] == '=')
            {
              GNativeString chkid = dargv[i].substr(0,4);
              if (chkid != "TXTz" && chkid != "TXTa" 
                  && chkid != "ANTz" && chkid != "ANTa"
                  && chkid != "Djbz" )
                DjVuPrintErrorUTF8("djvumake: creating chunk of unknown type ``%s''.\n",
                                   (const char*)chkid);
              create_raw_chunk(iff, chkid, GURL::Filename::UTF8(5+(const char *)dargv[i]));
            }
          else 
            {
              DjVuPrintErrorUTF8("djvumake: illegal argument : ``%s'' (ignored)\n", 
                                 (const char *)dargv[i]);
            }
        }
      // Common cases for missing chunks
      if (flag_contains_stencil)
        {
          if (flag_contains_bg && ! flag_contains_fg)
            {  
              DjVuPrintErrorUTF8("%s","djvumake: generating black FGbz chunk\n");
              g().colorzones.empty();
              g().colorpalette = ByteStream::create();
              char rgb[3] = {0,0,0};
              g().colorpalette->writall(rgb, 3);
              create_fgbz_chunk(iff);
              flag_contains_fg = 1;
            }
          if (flag_contains_fg && !flag_contains_bg)
            {
              DjVuPrintErrorUTF8("%s","djvumake: generating white BG44 chunk\n");
              GPixel bgcolor = GPixel::WHITE;
              GP<GPixmap> inputsub=GPixmap::create((h+11)/12, (w+11)/12, &bgcolor);
              GP<IW44Image> iw = IW44Image::create_encode(*inputsub, 0, IW44Image::CRCBnone);
              IWEncoderParms iwparms;
              iff.put_chunk("BG44");
              iwparms.slices = 97;
              iw->encode_chunk(iff.get_bytestream(), iwparms);
              iff.close_chunk();
              flag_contains_bg = 1;
            }
        }
      // Close
      iff.close_chunk();
      // Sanity checks
      if (flag_contains_stencil)
        {
          // Compound or Bilevel
          if (flag_contains_bg && ! flag_contains_fg)
            DjVuPrintErrorUTF8("%s","djvumake: djvu file contains a BGxx chunk but no FGxx chunk\n");
          if (flag_contains_fg && ! flag_contains_bg)
            DjVuPrintErrorUTF8("%s","djvumake: djvu file contains a FGxx chunk but no BGxx chunk\n");
        }
      else if (flag_contains_bg)
        {
          // Photo DjVu Image
          if (flag_contains_bg!=1)
            DjVuPrintErrorUTF8("%s","djvumake: photo djvu image has subsampled BGxx chunk\n"); 
          if (flag_fg_needs_palette)
            DjVuPrintErrorUTF8("%s","djvumake: could not generate FGbz chunk, as stencil is not available\n");
          else if (flag_contains_fg)
            DjVuPrintErrorUTF8("%s","djvumake: photo djvu file contains FGxx chunk\n");            
        }
      else
        DjVuPrintErrorUTF8("%s","djvumake: djvu file contains neither Sxxx nor BGxx chunks\n");
    }
  G_CATCH(ex)
    {
      remove(dargv[1]);
      ex.perror();
      exit(1);
    }
  G_ENDCATCH;
  return 0;
}
コード例 #5
0
ファイル: djvumake.cpp プロジェクト: smyts/tengwa-djvu
void 
create_bg44_chunk(IFFByteStream &iff, const char *ckid, GUTF8String filespec)
{
  static GP<IFFByteStream> bg44iff;
  if (! bg44iff)
    {
      if (flag_contains_bg)
        DjVuPrintErrorUTF8("%s","djvumake: Duplicate BGxx chunk\n");
      int i=filespec.rsearch(':');
      for (int j=i+1; i>0 && j<(int)filespec.length(); j++)
        if (filespec[j] < '0' || filespec[j] > '9')
          i = -1;
      if (!i)
        G_THROW("djvumake: no filename specified in first BG44 specification");
      GUTF8String filename=(i<0)?filespec:GUTF8String(filespec, i);
      const GURL::Filename::UTF8 url(filename);
      const GP<ByteStream> gbs(ByteStream::create(url,"rb"));
      if(!gbs)
      {
        G_THROW("djvumake: no such file as"+filename);
      }
      bg44iff = IFFByteStream::create(gbs);
      GUTF8String chkid;
      bg44iff->get_chunk(chkid);
      if (chkid != "FORM:PM44" && chkid != "FORM:BM44")
        G_THROW("djvumake: BG44 file has incorrect format (wrong IFF header)");        
      if (i>=0)
        filespec = i+1+(const char *)filespec;
      else 
        filespec = "99";
    }
  else
    {
      if (filespec.length() && filespec[0]!=':')
        G_THROW("djvumake: filename specified in BG44 refinement");
      filespec = 1+(const char *)filespec;
    }
  const char *s=filespec;
  int nchunks = strtol((char *)s, (char **)&s, 10);
  if (nchunks<1 || nchunks>99)
    G_THROW("djvumake: invalid number of chunks in BG44 specification");    
  if (*s)
    G_THROW("djvumake: invalid BG44 specification (syntax error)");
  
  int flag = (nchunks>=99);
  GUTF8String chkid;
  while (nchunks-->0 && bg44iff->get_chunk(chkid))
    {
      if (chkid!="PM44" && chkid!="BM44")
        {
          DjVuPrintErrorUTF8("%s","djvumake: BG44 file contains unrecognized chunks (fixed)\n");
          nchunks += 1;
          bg44iff->close_chunk();
          continue;
        }
      GP<ByteStream> gmbs=ByteStream::create();
      ByteStream &mbs=*gmbs;
      mbs.copy(*(bg44iff->get_bytestream()));
      bg44iff->close_chunk();  
      mbs.seek(0);
      if (mbs.readall((void*)&primary, sizeof(primary)) != sizeof(primary))
        G_THROW("djvumake: BG44 file is corrupted (cannot read primary header)\n");    
      if (primary.serial == 0)
        {
          if (mbs.readall((void*)&secondary, sizeof(secondary)) != sizeof(secondary))
            G_THROW("djvumake: BG44 file is corrupted (cannot read secondary header)\n");    
          int iw = (secondary.xhi<<8) + secondary.xlo;
          int ih = (secondary.yhi<<8) + secondary.ylo;
          int red;
          for (red=1; red<=12; red++)
            if (iw==(w+red-1)/red && ih==(h+red-1)/red)
              break;
          flag_contains_bg = red;
          if (red>12)
            DjVuPrintErrorUTF8("%s","djvumake: BG44 subsampling is not in [1..12] range\n");
        }
      mbs.seek(0);
      iff.put_chunk(ckid);
      iff.copy(mbs);
      iff.close_chunk();
      flag = 1;
    }
  if (!flag)
    DjVuPrintErrorUTF8("%s","djvumake: no more chunks in BG44 file\n");
}
コード例 #6
0
ファイル: cpaldjvu.cpp プロジェクト: smyts/tengwa-djvu
// -- Compresses low color pixmap.
void 
cpaldjvu(ByteStream *ibs, GURL &urlout, const cpaldjvuopts &opts)
{
  GP<GPixmap> ginput=GPixmap::create(*ibs);
  int w = ginput->columns();
  int h = ginput->rows();
  int dpi = MAX(200, MIN(900, opts.dpi));
  int largesize = MIN(500, MAX(64, dpi));
  int smallsize = MAX(2, dpi/150);

  // Compute optimal palette and quantize input pixmap
  GP<DjVuPalette> gpal=DjVuPalette::create();
  DjVuPalette &pal=*gpal;
  GPixel bgcolor;
  int bgindex = -1;
  if (! opts.bgwhite)
    {
      bgindex = pal.compute_pixmap_palette(*ginput, opts.ncolors);
      pal.index_to_color(bgindex, bgcolor);
    }
  else
    {
      bgcolor = GPixel::WHITE;
      pal.histogram_clear();
      for (int j=0; j<h; j++)
        {
          const GPixel *p = (*ginput)[j];
          for (int i=0; i<w; i++)
            if (p[i] != GPixel::WHITE)
              pal.histogram_add(p[i], 1);
        }
      pal.compute_palette(opts.ncolors);
    }
  if (opts.verbose)
    DjVuFormatErrorUTF8( "%s\t%d\t%d\t%d",
                         ERR_MSG("cpaldjvu.quantizied"), 
                         w, h, pal.size());
  if (opts.verbose && !opts.bgwhite)
    DjVuPrintErrorUTF8("cpaldjvu: background color is #%02x%02x%02x.\n", 
                       bgcolor.r, bgcolor.g, bgcolor.b);
  
  // Fill CCImage with color runs
  int xruncount=0,yruncount=0;
  CCImage rimg(w, h);
  int *line;
  GPBuffer<int> gline(line,w);
  int *prevline;
  GPBuffer<int> gprevline(prevline,w);
  for (int x=0;x<w;x++)
  {
    prevline[x]=bgindex;
  }
  for (int y=0; y<h; y++)
    {
      int x;
      const GPixel *row = (*ginput)[y];
      for(x=0;x<w;x++)
        {
          line[x] = pal.color_to_index(row[x]);
          if (opts.bgwhite && row[x]==GPixel::WHITE)
            line[x] = bgindex;
        }
      for(x=0;x<w;)
        {
          int x1 = x;
          int index = line[x++];
          while (x<w && line[x]==index) { x++; }
          if (index != bgindex)
            {
              xruncount++;
              rimg.add_single_run(y, x1, x-1, index);
            }
        }
      for(x=0;x<w;x++)
        if(prevline[x] != line[x]) yruncount++;
      gprevline.swap(gline);
    }
  ginput = 0; //save memory
  if (opts.verbose)
    DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("cpaldjvu.color_runs"), 
                         rimg.runs.size());

  // Perform Color Connected 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("cpaldjvu.ccs_before"), 
                         rimg.ccs.size());
  rimg.merge_and_split_ccs(smallsize,largesize);  // Eliminates gross ccs
  if (opts.verbose)
    DjVuFormatErrorUTF8( "%s\t%d", ERR_MSG("cpaldjvu.ccs_after"), 
                         rimg.ccs.size());
  rimg.sort_in_reading_order();                   // Sort cc descriptors
  
  // Create JB2Image and fill colordata
  GP<JB2Image> gjimg=JB2Image::create();
  JB2Image &jimg=*gjimg;
  jimg.set_dimension(w, h);
  int nccs = rimg.ccs.size();
  for (int ccid=0; ccid<nccs; ccid++)
    {
      JB2Shape shape;
      JB2Blit  blit;
      shape.parent = -1;
      shape.userdata = 0;
      if (ccid >= rimg.nregularccs)
        shape.userdata |= JB2SHAPE_SPECIAL;
      shape.bits = rimg.get_bitmap_for_cc(ccid);
      shape.bits->compress();
      CC& cc = rimg.ccs[ccid];
      blit.shapeno = jimg.add_shape(shape);
      blit.left = cc.bb.xmin;
      blit.bottom = cc.bb.ymin;
      int blitno = jimg.add_blit(blit);
      pal.colordata.touch(blitno);
      pal.colordata[blitno] = cc.color;
    }
  
  // Organize JB2Image
  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("cpaldjvu.cross_code"), 
                       nshape, nrefine);
    }
  
  // Create background image
#ifdef BACKGROUND_SUBSAMPLING_FACTOR
  // -- we may create the background by masking and subsampling
  GP<GPixmap> ginputsub=GPixmap::create();
  GPixmap &inputsub=*ginputsub;
  GP<GBitmap> mask = jimg.get_bitmap(BACKGROUND_SUBSAMPLING_FACTOR);
  inputsub.downsample(&input, BACKGROUND_SUBSAMPLING_FACTOR);
  GP<IW44Image> iwimage=IW44Image::create(inputsub, mask);
#else
  // -- but who cares since the background is uniform.
  GP<GPixmap> ginputsub=GPixmap::create((h+11)/12, (w+11)/12, &bgcolor);
  GPixmap &inputsub=*ginputsub;
  GP<IW44Image> iwimage=IW44Image::create_encode(inputsub);
#endif

  // Assemble DJVU file
  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
  iff.put_chunk("INFO");
  GP<DjVuInfo> ginfo=DjVuInfo::create();
  DjVuInfo info=*ginfo;
  info.height = h;
  info.width = w;
  info.dpi = opts.dpi;
  info.encode(*iff.get_bytestream());
  iff.close_chunk();
  // -- ``Sjbz'' chunk
  iff.put_chunk("Sjbz");
  jimg.encode(iff.get_bytestream());
  iff.close_chunk();
  // -- ``FGbz'' chunk
  iff.put_chunk("FGbz");
  pal.encode(iff.get_bytestream());
  iff.close_chunk();
  // -- ``BG44'' chunk
  IWEncoderParms iwparms;
#ifdef PROGRESSIVE_BACKGROUND
  // ----- we may use several chunks to enable progressive rendering ...
  iff.put_chunk("BG44");
  iwparms.slices = 74;
  iwimage->encode_chunk(iff, iwparms);
  iff.close_chunk();
  iff.put_chunk("BG44");
  iwparms.slices = 87;
  iwimage->encode_chunk(iff, iwparms);
  iff.close_chunk();
#endif
  // ----- but who cares when the background is so small.
  iff.put_chunk("BG44");
  iwparms.slices = 97;
  iwimage->encode_chunk(iff.get_bytestream(), iwparms);
  iff.close_chunk();
  // -- terminate main composite chunk
  iff.close_chunk();
  // Finished!
}  
コード例 #7
0
void
DjVuMessageLite::perror( const GUTF8String & MessageList )
{
  DjVuPrintErrorUTF8("%s\n",(const char *)DjVuMessageLite::LookUpUTF8(MessageList));
}
コード例 #8
0
ファイル: cjb2.cpp プロジェクト: tau0/djvulibre
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);
}