2006.08.29 20:44 "[Tiff] TiffReadEncodedStrip", by Richard Nolde

2006.08.30 22:01 "Re: [Tiff] TiffReadEncodedStrip", by Richard Nolde

From what I have read in the current man pages and programs in the

tools directory, I assume that I can simple compute the start of each row as an offset from the beginning of the buffer. For 1 bit per sample data with one sample per pixel, something like ImageWidth / 8 bytes would be returned into the read buffer for each row in the image and the offset would be (row * IMAGEWIDTH) / (8 /bps) * samples per pixel for contiguous planes. This seems to imply that the last byte of a scanline is padded out to fill that byte. Is this correct?

The last statement is correct, but the formulae before that is not. Scanlines are padded out with fillbits up to a byte boundary.

The correct expression to use goes something like this. A scanline length is (imagewidth*bitsperpixel+7)/8. Think about it, if imagewidth is 1 and bitsperpixel is 1, that'll round things up to 1 byte per row. If imagewidth is 8 and bitsperpixel is 1... still 1 byte per row.

So if you need an offset for (column,row), the byteoffset would be

row*((imagewidth*bitsperpixel+7)/8)+(column*bitsperpixel/8)

and if bitsperpixel is not a multiple of 8, you'll need a bit offset too...

((column*bitsperpixel)%8)

I would deduce from this that your row and column numbers are zero referenced as in C arrays. It has been a while since I have had a chance to write in C so I'm a little bit groggy on the bit shifts and masking as it applies to libtiff.

My usage, for reasons related to the code that defines the zones to be extracted, runs row from 1 to IMAGE_LENGTH and column from 1 to IMAGE_WIDTH so I have to subtract one in each case. The snippet below is the code to extract the data bytes from the input image and write them to an output image. My code now gets the right bytes, but I am confused by your comment on the bit offset. Assuming an image of 1658 pixels wide with 1 bit per sample, 1 sample per pixel, there will be 207 "full bytes" and two extra bits. Will these last bits be in the highorder bits or loworder bits, ie 128 and 64 or 2 and 1 and does the fill order matter when the image has been extracted by ReadEncodedStrip? I had assumed a continuous bit stream with the padding in the low order bits and planned on reading the trailing bits followed by zeros if it is the end of the line. Of course, when cropping from within the line, the remaining bits need to be masked off and converted to fill bits too.

for (i = 0; i < crop->zones; i++)
  {

    first_row = crop->zonelist[i].y1;
    last_row  = crop->zonelist[i].y2;
    first_col = crop->zonelist[i].x1;
    last_col  = crop->zonelist[i].x2;

    crop_width = last_col - first_col + 1;
    crop_length = last_row - first_row + 1;

    crop_size   = (crop_width * crop_length * spp) / (8 / bps);

    full_bytes = spp * (crop_width * bps) / 8;   /* number of COMPLETE bytes per row in crop area */

    padded_bytes = spp * (crop_width * bps + 7) / 8; /* total number of bytes per row in crop area */

trailing_bits = (crop_width * bps) % 8;

fprintf (stderr, "First row: %d, Last row; %d First column: %d, Last column: %d\n",

     first_row, last_row, first_col, last_col);
fprintf (stderr, "\nZone: %d, Crop width: %u, Crop length: %u, Compressed size in bytes: %u\n",
     i + 1, crop_width, crop_length, crop_size);
fprintf (stderr, "Complete bytes in cropped row: %d, %d trailing bits\n",
                  full_bytes, trailing_bits);

for (row = first_row; row <= last_row; row++)
  {
  /* Simple case first, all samples interleaved */
  if (planar == PLANARCONFIG_CONTIG)
    {
    /* Offset1 is the byte offset at which the first selected pixel of the region is found
       Shift1 is the bit offset within the byte at offeset1 */

        offset1 =  spp * ((row - 1) * ((img_width * bps + 7) / 8) + ((first_col - 1) * bps / 8));
        shift1  =  spp * (((first_col - 1) * bps) % 8);

/* Offset2 is the byte offset at which the last selected pixel of the region is found

     Shift2 is the bit offset within the byte at offeset2 */

        offset2 = spp * ((row - 1) * ((img_width * bps + 7) / 8) + ((last_col - 1) * bps / 8));

        shift2  = spp * (((last_col - 1) * bps) % 8);

        fprintf (stderr, "\nRow: %5d, Start byte: %6u,  Shift: %d\n            End byte:   %6u,  Shift: %d\n",

   row, offset1, shift1, offset2, shift2);

  bytebuff1 = bytebuff2 = 0;
  if (shift1 == 0) /* the region is byte and sample alligned */
    {
/* fprintf (stderr, "Processing %d unshifted bytes plus final partial byte\n", full_bytes); */
_TIFFmemcpy (crop_buff + dest_offset, read_buff + offset1, full_bytes);
    dest_offset += full_bytes;

    /* pick up the final bits */
    if (trailing_bits != 0)
      {
      for (k = 7; k >= 0; k--)
    sprintf (&bitbuffer[7 - k], "%s", ((unsigned char)read_buff[offset2] & (1 << k))? "1": "0");

  fprintf (stderr, "Final byte contains value %2x, %s\n",
       (unsigned char)read_buff[offset2], bitbuffer);

  bytebuff2 = read_buff[offset2] & ((unsigned char)255 << (8 - shift2));
      crop_buff[dest_offset] = bytebuff2;
      dest_offset++;
      }
    }
  else /* each destination byte will have to be built from two source bytes*/
    {
fprintf (stderr, "Processing %d bit shifted bytes\n", full_bytes);
    for (j = 0; j <= full_bytes; j++)
      {

        bytebuff1 = read_buff[offset1 + j] & ((unsigned char)255 >> (shift1));
        bytebuff2 = read_buff[offset1 + j + 1] & ((unsigned char)255 << (8 - shift1));

            crop_buff[dest_offset + j] = bytebuff1 | bytebuff2;

      }
    dest_offset += full_bytes;

    /* process the final bits of the cropped row */
/* calculate how many bits are left from shift1 and how many are needed from shift 2
         pad the scanline as needed */
    if (trailing_bits != 0)
      {
      for (k = 7; k >= 0; k--)
    sprintf (&bitbuffer[7 - k], "%s", ((unsigned char)read_buff[offset2] & (1 << k))? "1": "0");

    fprintf (stderr, "Final byte contains value %2x, %s\n",
         (unsigned char)read_buff[offset2], bitbuffer);
    if (shift2 > shift1)
          {
      bytebuff1 = read_buff[source_offset + full_bytes] & ((unsigned char)255 << (8 - shift2));
          bytebuff2 = bytebuff1 & ((unsigned char)255 << shift1);
          crop_buff[dest_offset + j] = bytebuff2;
          }
        else
          {
       if (shift2 < shift1)
            {
            bytebuff2 = ((unsigned char)255 << (8 - shift2));
            crop_buff[dest_offset + j] &= bytebuff2;
            }
      /* else
       fprintf (stderr, "Shift2 equals bit borrowed from final byte\n"); */
          }
    }
  }
}
  else
    {
/* PLANARCONFIG_SEPARATE */
fprintf (stderr, "Planar config separate data not yet supported.\n");
    return (-1);
}
  }

Here is a log for the first two images in the input file. Since I am not ready to write out the data, I can't tell if I have the bit shifting is correct.

Image 1, width: 1658, Image length: 2591, Xres 300.00, Yres: 300.00
Scale: 1.000000 Requested resunit 2, Image resunit 2

Margins: Top: 0 Left: 0 Bottom: 0 Right: 0

Crop region within margins: Requested Width:   1658  Length:   2591
Crop region within margins: Adjusted Width:    1658  Length:   2591

Zone 1, width: 1658, length:  323, x1:    1  x2: 1658  y1:    1  y2:  323

Image width: 1658, Image length: 2591, Image size in pixels: 4295878
Samples per Pixel: 1, Bits per Sample: 1, Planar config: 1
Image is not byte swapped and fill order is MSB to LSB
Number of strips: 1, Strip size: 538928, RowsPerStrip 2591, Scanline size: 208
Crop buffer: 535534 pixels, 67507 bytes
First row: 1, Last row; 323 First column: 1, Last column: 1658

Zone: 1, Crop width: 1658, Crop length: 323, Compressed size in bytes: 66941
Complete bytes in cropped row: 207, 2 trailing bits

Row:     1, Start byte:      0,  Shift: 0
            End byte:      207,  Shift: 1
Final byte contains value  0, 00000000

Row:     2, Start byte:    208,  Shift: 0
            End byte:      415,  Shift: 1
Final byte contains value  0, 00000000

Row:     3, Start byte:    416,  Shift: 0
            End byte:      623,  Shift: 1
Final byte contains value  0, 00000000

...

Image 2, width: 3449, Image length: 2604, Xres 300.00, Yres: 300.00
Scale: 1.000000 Requested resunit 2, Image resunit 2

Margins: Top: 0 Left: 0 Bottom: 0 Right: 0

Crop region within margins: Requested Width:   3449  Length:   2604
Crop region within margins: Adjusted Width:    3449  Length:   2604

Zone 1, width: 3449, length:  325, x1:    1  x2: 3449  y1:    1  y2:  325

Image width: 3449, Image length: 2604, Image size in pixels: 8981196
Samples per Pixel: 1, Bits per Sample: 1, Planar config: 1
Image is not byte swapped and fill order is MSB to LSB
Number of strips: 1, Strip size: 1124928, RowsPerStrip 2604, Scanline size: 432
Crop buffer: 2234952 pixels, 140400 bytes
First row: 1, Last row; 325 First column: 1, Last column: 3449

Zone: 1, Crop width: 3449, Crop length: 325, Compressed size in bytes: 140115
Complete bytes in cropped row: 431, 1 trailing bits

Row:     1, Start byte:      0,  Shift: 0
            End byte:      431,  Shift: 0
Final byte contains value  0, 00000000

Row:     2, Start byte:    432,  Shift: 0
            End byte:      863,  Shift: 0
Final byte contains value  0, 00000000

Richard Nolde