/// <summary> /// 8-bit packed YCbCr samples w/ 4,4 subsampling => RGB /// </summary> private static void putcontig8bitYCbCr44tile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int rasterOffset1 = rasterOffset + width + rasterShift; int rasterOffset2 = rasterOffset1 + width + rasterShift; int rasterOffset3 = rasterOffset2 + width + rasterShift; int incr = 3 * width + 4 * rasterShift; // adjust bufferShift bufferShift = (bufferShift * 18) / 4; if ((height & 3) == 0 && (width & 3) == 0) { for (; height >= 4; height -= 4) { x = width >> 2; do { int Cb = buffer[offset + 16]; int Cr = buffer[offset + 17]; img.YCbCrtoRGB(out raster[rasterOffset], buffer[offset + 0], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset1 + 0], buffer[offset + 4], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset1 + 1], buffer[offset + 5], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset1 + 2], buffer[offset + 6], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset1 + 3], buffer[offset + 7], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 8], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 9], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 2], buffer[offset + 10], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 3], buffer[offset + 11], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset3 + 0], buffer[offset + 12], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset3 + 1], buffer[offset + 13], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset3 + 2], buffer[offset + 14], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset3 + 3], buffer[offset + 15], Cb, Cr); rasterOffset += 4; rasterOffset1 += 4; rasterOffset2 += 4; rasterOffset3 += 4; offset += 18; } while (--x != 0); rasterOffset += incr; rasterOffset1 += incr; rasterOffset2 += incr; rasterOffset3 += incr; offset += bufferShift; } } else { while (height > 0) { for (x = width; x > 0; ) { int Cb = buffer[offset + 16]; int Cr = buffer[offset + 17]; bool h_goOn = false; bool x_goOn = false; // order of if's is important if (x < 1 || x > 3) { // order of if's is important h_goOn = false; if (height < 1 || height > 3) { img.YCbCrtoRGB(out raster[rasterOffset3 + 3], buffer[offset + 15], Cb, Cr); h_goOn = true; } if (height == 3 || h_goOn) { img.YCbCrtoRGB(out raster[rasterOffset2 + 3], buffer[offset + 11], Cb, Cr); h_goOn = true; } if (height == 2 || h_goOn) { img.YCbCrtoRGB(out raster[rasterOffset1 + 3], buffer[offset + 7], Cb, Cr); h_goOn = true; } if (height == 1 || h_goOn) img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); x_goOn = true; } if (x == 3 || x_goOn) { // order of if's is important h_goOn = false; if (height < 1 || height > 3) { img.YCbCrtoRGB(out raster[rasterOffset3 + 2], buffer[offset + 14], Cb, Cr); h_goOn = true; } if (height == 3 || h_goOn) { img.YCbCrtoRGB(out raster[rasterOffset2 + 2], buffer[offset + 10], Cb, Cr); h_goOn = true; } if (height == 2 || h_goOn) { img.YCbCrtoRGB(out raster[rasterOffset1 + 2], buffer[offset + 6], Cb, Cr); h_goOn = true; } if (height == 1 || h_goOn) img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); x_goOn = true; } if (x == 2 || x_goOn) { // order of if's is important h_goOn = false; if (height < 1 || height > 3) { img.YCbCrtoRGB(out raster[rasterOffset3 + 1], buffer[offset + 13], Cb, Cr); h_goOn = true; } if (height == 3 || h_goOn) { img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 9], Cb, Cr); h_goOn = true; } if (height == 2 || h_goOn) { img.YCbCrtoRGB(out raster[rasterOffset1 + 1], buffer[offset + 5], Cb, Cr); h_goOn = true; } if (height == 1 || h_goOn) img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); } if (x == 1 || x_goOn) { // order of if's is important h_goOn = false; if (height < 1 || height > 3) { img.YCbCrtoRGB(out raster[rasterOffset3 + 0], buffer[offset + 12], Cb, Cr); h_goOn = true; } if (height == 3 || h_goOn) { img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 8], Cb, Cr); h_goOn = true; } if (height == 2 || h_goOn) { img.YCbCrtoRGB(out raster[rasterOffset1 + 0], buffer[offset + 4], Cb, Cr); h_goOn = true; } if (height == 1 || h_goOn) img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); } if (x < 4) { rasterOffset += x; rasterOffset1 += x; rasterOffset2 += x; rasterOffset3 += x; x = 0; } else { rasterOffset += 4; rasterOffset1 += 4; rasterOffset2 += 4; rasterOffset3 += 4; x -= 4; } offset += 18; } if (height <= 4) break; height -= 4; rasterOffset += incr; rasterOffset1 += incr; rasterOffset2 += incr; rasterOffset3 += incr; offset += bufferShift; } } }
/// <summary> /// Get an tile-organized image that has /// SamplesPerPixel > 1 /// PlanarConfiguration separated /// We assume that all such images are RGB. /// </summary> private static bool gtTileSeparate(TiffRgbaImage img, int[] raster, int offset, int width, int height) { int tilesize = img.tif.TileSize(); byte[] buf = new byte[(img.alpha != 0 ? 4 : 3) * tilesize]; int p0 = 0; int p1 = p0 + tilesize; int p2 = p1 + tilesize; int pa = (img.alpha != 0 ? (p2 + tilesize) : -1); FieldValue[] result = img.tif.GetField(TiffTag.TileWidth); int tileWidth = result[0].ToInt(); result = img.tif.GetField(TiffTag.TileLength); int tileHeight = result[0].ToInt(); int flip = img.setorientation(); int y; int rasterShift; if ((flip & FLIP_VERTICALLY) != 0) { y = height - 1; rasterShift = -(tileWidth + width); } else { y = 0; rasterShift = -(tileWidth - width); } bool ret = true; for (int row = 0; row < height; ) { int rowstoread = tileHeight - (row + img.row_offset) % tileHeight; int nrow = (row + rowstoread > height ? height - row : rowstoread); for (int col = 0; col < width; col += tileWidth) { if (img.tif.ReadTile(buf, p0, col + img.col_offset, row + img.row_offset, 0, 0) < 0 && img.stoponerr) { ret = false; break; } if (img.tif.ReadTile(buf, p1, col + img.col_offset, row + img.row_offset, 0, 1) < 0 && img.stoponerr) { ret = false; break; } if (img.tif.ReadTile(buf, p2, col + img.col_offset, row + img.row_offset, 0, 2) < 0 && img.stoponerr) { ret = false; break; } if (img.alpha != 0) { if (img.tif.ReadTile(buf, pa, col + img.col_offset, row + img.row_offset, 0, 3) < 0 && img.stoponerr) { ret = false; break; } } int pos = ((row + img.row_offset) % tileHeight) * img.tif.TileRowSize(); if (col + tileWidth > width) { // Tile is clipped horizontally. // Calculate visible portion and skewing factors. int npix = width - col; int bufferShift = tileWidth - npix; img.putSeparate(img, raster, offset + y * width + col, rasterShift + bufferShift, col, y, npix, nrow, buf, p0 + pos, p1 + pos, p2 + pos, img.alpha != 0 ? (pa + pos) : -1, bufferShift); } else { img.putSeparate(img, raster, offset + y * width + col, rasterShift, col, y, tileWidth, nrow, buf, p0 + pos, p1 + pos, p2 + pos, img.alpha != 0 ? (pa + pos) : -1, 0); } } y += ((flip & FLIP_VERTICALLY) != 0 ? -nrow : nrow); row += nrow; } if ((flip & FLIP_HORIZONTALLY) != 0) { for (int line = 0; line < height; line++) { int left = offset + line * width; int right = left + width - 1; while (left < right) { int temp = raster[left]; raster[left] = raster[right]; raster[right] = temp; left++; right--; } } } return ret; }
/// <summary> /// 8-bit packed YCbCr samples w/ 2,1 subsampling => RGB /// </summary> private static void putcontig8bitYCbCr21tile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { bufferShift = (bufferShift * 4) / 2; do { x = width >> 1; do { int Cb = buffer[offset + 2]; int Cr = buffer[offset + 3]; img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); rasterOffset += 2; offset += 4; } while (--x != 0); if ((width & 1) != 0) { int Cb = buffer[offset + 2]; int Cr = buffer[offset + 3]; img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); rasterOffset += 1; offset += 4; } rasterOffset += rasterShift; offset += bufferShift; } while (--height != 0); }
/// <summary> /// 16-bit unpacked samples => RGBA w/ associated alpha /// </summary> private static void putRGBAAseparate16bittile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) { short[] wrgba = Tiff.ByteArrayToShorts(buffer, 0, buffer.Length); offset1 /= sizeof(short); offset2 /= sizeof(short); offset3 /= sizeof(short); offset4 /= sizeof(short); while (height-- > 0) { for (x = 0; x < width; x++) { raster[rasterOffset] = PACKW4(wrgba[offset1], wrgba[offset2], wrgba[offset3], wrgba[offset4]); rasterOffset++; offset1++; offset2++; offset3++; offset4++; } offset1 += bufferShift; offset2 += bufferShift; offset3 += bufferShift; offset4 += bufferShift; rasterOffset += rasterShift; } }
/// <summary> /// 8-bit packed YCbCr samples w/ no subsampling => RGB /// </summary> private static void putseparate8bitYCbCr11tile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) { while (height-- > 0) { x = width; do { int r, g, b; img.ycbcr.YCbCrtoRGB(buffer[offset1], buffer[offset2], buffer[offset3], out r, out g, out b); raster[rasterOffset] = PACK(r, g, b); rasterOffset++; offset1++; offset2++; offset3++; } while (--x != 0); offset1 += bufferShift; offset2 += bufferShift; offset3 += bufferShift; rasterOffset += rasterShift; } }
/// <summary> /// 8-bit packed YCbCr samples w/ no subsampling => RGB /// </summary> private static void putcontig8bitYCbCr11tile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { bufferShift *= 3; do { x = width; // was x = w >> 1; patched 2000/09/25 [email protected] do { int Cb = buffer[offset + 1]; int Cr = buffer[offset + 2]; img.YCbCrtoRGB(out raster[rasterOffset], buffer[offset + 0], Cb, Cr); rasterOffset++; offset += 3; } while (--x != 0); rasterOffset += rasterShift; offset += bufferShift; } while (--height != 0); }
/// <summary> /// 8-bit unpacked samples => RGBA w/ associated alpha /// </summary> private static void putRGBAAseparate8bittile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) { while (height-- > 0) { int _x; for (_x = width; _x >= 8; _x -= 8) { for (int rc = 0; rc < 8; rc++) { raster[rasterOffset] = PACK4(buffer[offset1], buffer[offset2], buffer[offset3], buffer[offset4]); rasterOffset++; offset1++; offset2++; offset3++; offset4++; } } if (_x > 0) { if (_x <= 7 && _x > 0) { for (int i = _x; i > 0; i--) { raster[rasterOffset] = PACK4(buffer[offset1], buffer[offset2], buffer[offset3], buffer[offset4]); rasterOffset++; offset1++; offset2++; offset3++; offset4++; } } } offset1 += bufferShift; offset2 += bufferShift; offset3 += bufferShift; offset4 += bufferShift; rasterOffset += rasterShift; } }
/// <summary> /// 2-bit greyscale => colormap/RGB /// </summary> private static void put2bitbwtile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int[][] BWmap = img.BWmap; bufferShift /= 4; while (height-- > 0) { int[] bw = null; int _x; for (_x = width; _x >= 4; _x -= 4) { bw = BWmap[buffer[offset]]; offset++; for (int rc = 0; rc < 4; rc++) { raster[rasterOffset] = bw[rc]; rasterOffset++; } } if (_x > 0) { bw = BWmap[buffer[offset]]; offset++; if (_x <= 3 && _x > 0) { for (int i = 0; i < _x; i++) { raster[rasterOffset] = bw[i]; rasterOffset++; } } } rasterOffset += rasterShift; offset += bufferShift; } }
/// <summary> /// 4-bit greyscale => colormap/RGB /// </summary> private static void put4bitbwtile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int[][] BWmap = img.BWmap; bufferShift /= 2; while (height-- > 0) { int[] bw = null; int _x; for (_x = width; _x >= 2; _x -= 2) { bw = BWmap[buffer[offset]]; offset++; for (int rc = 0; rc < 2; rc++) { raster[rasterOffset] = bw[rc]; rasterOffset++; } } if (_x != 0) { bw = BWmap[buffer[offset]]; offset++; raster[rasterOffset] = bw[0]; rasterOffset++; } rasterOffset += rasterShift; offset += bufferShift; } }
/// <summary> /// 8-bit greyscale => colormap/RGB /// </summary> private static void putgreytile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int samplesperpixel = img.samplesperpixel; int[][] BWmap = img.BWmap; while (height-- > 0) { for (x = width; x-- > 0; ) { raster[rasterOffset] = BWmap[buffer[offset]][0]; rasterOffset++; offset += samplesperpixel; } rasterOffset += rasterShift; offset += bufferShift; } }
/// <summary> /// 16-bit greyscale => colormap/RGB /// </summary> private static void put16bitbwtile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int samplesperpixel = img.samplesperpixel; int[][] BWmap = img.BWmap; while (height-- > 0) { short[] wp = Tiff.ByteArrayToShorts(buffer, offset, buffer.Length - offset); int wpPos = 0; for (x = width; x-- > 0; ) { // use high order byte of 16bit value raster[rasterOffset] = BWmap[(wp[wpPos] & 0xffff) >> 8][0]; rasterOffset++; offset += 2 * samplesperpixel; wpPos += samplesperpixel; } rasterOffset += rasterShift; offset += bufferShift; } }
/// <summary> /// 1-bit palette => colormap/RGB /// </summary> private static void put1bitcmaptile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int[][] PALmap = img.PALmap; bufferShift /= 8; while (height-- > 0) { int[] bw; int bwPos = 0; int _x; for (_x = width; _x >= 8; _x -= 8) { bw = PALmap[buffer[offset++]]; bwPos = 0; for (int i = 0; i < 8; i++) raster[rasterOffset++] = bw[bwPos++]; } if (_x > 0) { bw = PALmap[buffer[offset++]]; bwPos = 0; if (_x <= 7 && _x > 0) { for (int i = 0; i < _x; i++) raster[rasterOffset++] = bw[bwPos++]; } } rasterOffset += rasterShift; offset += bufferShift; } }
/// <summary> /// Get a strip-organized image with /// SamplesPerPixel > 1 /// PlanarConfiguration separated /// We assume that all such images are RGB. /// </summary> private static bool gtStripSeparate(TiffRgbaImage img, int[] raster, int offset, int width, int height) { int stripsize = img.tif.StripSize(); byte[] buf = new byte[(img.alpha != 0 ? 4 : 3) * stripsize]; int p0 = 0; int p1 = p0 + stripsize; int p2 = p1 + stripsize; int pa = p2 + stripsize; pa = (img.alpha != 0 ? (p2 + stripsize) : -1); int flip = img.setorientation(); int y; int rasterShift; if ((flip & FLIP_VERTICALLY) != 0) { y = height - 1; rasterShift = -(width + width); } else { y = 0; rasterShift = -(width - width); } FieldValue[] result = img.tif.GetFieldDefaulted(TiffTag.RowsPerStrip); int rowsperstrip = result[0].ToInt(); int scanline = img.tif.ScanlineSize(); int bufferShift = (width < img.width ? img.width - width : 0); bool ret = true; for (int row = 0; row < height; ) { int rowstoread = rowsperstrip - (row + img.row_offset) % rowsperstrip; int nrow = (row + rowstoread > height ? height - row : rowstoread); int offset_row = row + img.row_offset; if (img.tif.ReadEncodedStrip(img.tif.ComputeStrip(offset_row, 0), buf, p0, ((row + img.row_offset) % rowsperstrip + nrow) * scanline) < 0 && img.stoponerr) { ret = false; break; } if (img.tif.ReadEncodedStrip(img.tif.ComputeStrip(offset_row, 1), buf, p1, ((row + img.row_offset) % rowsperstrip + nrow) * scanline) < 0 && img.stoponerr) { ret = false; break; } if (img.tif.ReadEncodedStrip(img.tif.ComputeStrip(offset_row, 2), buf, p2, ((row + img.row_offset) % rowsperstrip + nrow) * scanline) < 0 && img.stoponerr) { ret = false; break; } if (img.alpha != 0) { if ((img.tif.ReadEncodedStrip(img.tif.ComputeStrip(offset_row, 3), buf, pa, ((row + img.row_offset) % rowsperstrip + nrow) * scanline) < 0 && img.stoponerr)) { ret = false; break; } } int pos = ((row + img.row_offset) % rowsperstrip) * scanline; img.putSeparate(img, raster, offset + y * width, rasterShift, 0, y, width, nrow, buf, p0 + pos, p1 + pos, p2 + pos, img.alpha != 0 ? (pa + pos) : -1, bufferShift); y += (flip & FLIP_VERTICALLY) != 0 ? -nrow : nrow; row += nrow; } if ((flip & FLIP_HORIZONTALLY) != 0) { for (int line = 0; line < height; line++) { int left = offset + line * width; int right = left + width - 1; while (left < right) { int temp = raster[left]; raster[left] = raster[right]; raster[right] = temp; left++; right--; } } } return ret; }
/// <summary> /// Get a strip-organized image that has /// PlanarConfiguration contiguous if SamplesPerPixel > 1 /// or /// SamplesPerPixel == 1 /// </summary> private static bool gtStripContig(TiffRgbaImage img, int[] raster, int offset, int width, int height) { byte[] buf = new byte[img.tif.StripSize()]; int flip = img.setorientation(); int y; int rasterShift; if ((flip & FLIP_VERTICALLY) != 0) { y = height - 1; rasterShift = -(width + width); } else { y = 0; rasterShift = -(width - width); } FieldValue[] result = img.tif.GetFieldDefaulted(TiffTag.RowsPerStrip); int rowsperstrip = result[0].ToInt(); if (rowsperstrip == -1) { // San Chen <*****@*****.**> // HACK: should be UInt32.MaxValue rowsperstrip = Int32.MaxValue; } result = img.tif.GetFieldDefaulted(TiffTag.YCBCRSUBSAMPLING); short subsamplingver = result[1].ToShort(); int scanline = img.tif.newScanlineSize(); int bufferShift = (width < img.width ? img.width - width : 0); bool ret = true; for (int row = 0; row < height; ) { int rowstoread = rowsperstrip - (row + img.row_offset) % rowsperstrip; int nrow = (row + rowstoread > height ? height - row : rowstoread); int nrowsub = nrow; if ((nrowsub % subsamplingver) != 0) nrowsub += subsamplingver - nrowsub % subsamplingver; if (img.tif.ReadEncodedStrip(img.tif.ComputeStrip(row + img.row_offset, 0), buf, 0, ((row + img.row_offset) % rowsperstrip + nrowsub) * scanline) < 0 && img.stoponerr) { ret = false; break; } int pos = ((row + img.row_offset) % rowsperstrip) * scanline; img.putContig(img, raster, offset + y * width, rasterShift, 0, y, width, nrow, buf, pos, bufferShift); y += (flip & FLIP_VERTICALLY) != 0 ? -nrow : nrow; row += nrow; } if ((flip & FLIP_HORIZONTALLY) != 0) { for (int line = 0; line < height; line++) { int left = offset + line * width; int right = left + width - 1; while (left < right) { int temp = raster[left]; raster[left] = raster[right]; raster[right] = temp; left++; right--; } } } return ret; }
/// <summary> /// 8-bit packed YCbCr samples w/ 4,2 subsampling => RGB /// </summary> private static void putcontig8bitYCbCr42tile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int rasterOffset2 = rasterOffset + width + rasterShift; int incr = 2 * rasterShift + width; bufferShift = (bufferShift * 10) / 4; if ((height & 3) == 0 && (width & 1) == 0) { for (; height >= 2; height -= 2) { x = width >> 2; do { int Cb = buffer[offset + 8]; int Cr = buffer[offset + 9]; img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 4], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 5], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 2], buffer[offset + 6], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 3], buffer[offset + 7], Cb, Cr); rasterOffset += 4; rasterOffset2 += 4; offset += 10; } while (--x != 0); rasterOffset += incr; rasterOffset2 += incr; offset += bufferShift; } } else { while (height > 0) { for (x = width; x > 0; ) { int Cb = buffer[offset + 8]; int Cr = buffer[offset + 9]; bool x_goOn = false; if (x < 1 || x > 3) { if (height != 1) img.YCbCrtoRGB(out raster[rasterOffset2 + 3], buffer[offset + 7], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); x_goOn = true; } if (x == 3 || x_goOn) { if (height != 1) img.YCbCrtoRGB(out raster[rasterOffset2 + 2], buffer[offset + 6], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); x_goOn = true; } if (x == 2 || x_goOn) { if (height != 1) img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 5], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); x_goOn = true; } if (x == 1 || x_goOn) { if (height != 1) img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 4], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); } if (x < 4) { rasterOffset += x; rasterOffset2 += x; x = 0; } else { rasterOffset += 4; rasterOffset2 += 4; x -= 4; } offset += 10; } if (height <= 2) break; height -= 2; rasterOffset += incr; rasterOffset2 += incr; offset += bufferShift; } } }
/// <summary> /// 8-bit packed samples => RGBA w/ associated alpha (known to have Map == null) /// </summary> private static void putRGBAAcontig8bittile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int samplesperpixel = img.samplesperpixel; bufferShift *= samplesperpixel; while (height-- > 0) { int _x; for (_x = width; _x >= 8; _x -= 8) { for (int rc = 0; rc < 8; rc++) { raster[rasterOffset] = PACK4(buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3]); rasterOffset++; offset += samplesperpixel; } } if (_x > 0) { if (_x <= 7 && _x > 0) { for (int i = _x; i > 0; i--) { raster[rasterOffset] = PACK4(buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3]); rasterOffset++; offset += samplesperpixel; } } } rasterOffset += rasterShift; offset += bufferShift; } }
/// <summary> /// 8-bit packed YCbCr samples w/ 4,1 subsampling => RGB /// </summary> private static void putcontig8bitYCbCr41tile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { // XXX adjust bufferShift do { x = width >> 2; do { int Cb = buffer[offset + 4]; int Cr = buffer[offset + 5]; img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); rasterOffset += 4; offset += 6; } while (--x != 0); if ((width & 3) != 0) { int Cb = buffer[offset + 4]; int Cr = buffer[offset + 5]; int xx = width & 3; if (xx == 3) img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); if (xx == 3 || xx == 2) img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); if (xx == 3 || xx == 2 || xx == 1) img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); rasterOffset += xx; offset += 6; } rasterOffset += rasterShift; offset += bufferShift; } while (--height != 0); }
/// <summary> /// 8-bit packed samples => RGBA w/ unassociated alpha (known to have Map == null) /// </summary> private static void putRGBUAcontig8bittile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int samplesperpixel = img.samplesperpixel; bufferShift *= samplesperpixel; while (height-- > 0) { for (x = width; x-- > 0; ) { int a = buffer[offset + 3]; int r = (buffer[offset] * a + 127) / 255; int g = (buffer[offset + 1] * a + 127) / 255; int b = (buffer[offset + 2] * a + 127) / 255; raster[rasterOffset] = PACK4(r, g, b, a); rasterOffset++; offset += samplesperpixel; } rasterOffset += rasterShift; offset += bufferShift; } }
/// <summary> /// 8-bit packed YCbCr samples w/ 1,2 subsampling => RGB /// </summary> private static void putcontig8bitYCbCr12tile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { bufferShift = (bufferShift / 2) * 4; int rasterOffset2 = rasterOffset + width + rasterShift; while (height >= 2) { x = width; do { int Cb = buffer[offset + 2]; int Cr = buffer[offset + 3]; img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 1], Cb, Cr); rasterOffset++; rasterOffset2++; offset += 4; } while (--x != 0); rasterOffset += rasterShift * 2 + width; rasterOffset2 += rasterShift * 2 + width; offset += bufferShift; height -= 2; } if (height == 1) { x = width; do { int Cb = buffer[offset + 2]; int Cr = buffer[offset + 3]; img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); rasterOffset++; offset += 4; } while (--x != 0); } }
/// <summary> /// 16-bit packed samples => RGBA w/ unassociated alpha (known to have Map == null) /// </summary> private static void putRGBUAcontig16bittile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int samplesperpixel = img.samplesperpixel; bufferShift *= samplesperpixel; short[] wp = Tiff.ByteArrayToShorts(buffer, offset, buffer.Length); int wpPos = 0; while (height-- > 0) { for (x = width; x-- > 0; ) { int a = W2B(wp[wpPos + 3]); int r = (W2B(wp[wpPos]) * a + 127) / 255; int g = (W2B(wp[wpPos + 1]) * a + 127) / 255; int b = (W2B(wp[wpPos + 2]) * a + 127) / 255; raster[rasterOffset] = PACK4(r, g, b, a); rasterOffset++; wpPos += samplesperpixel; } rasterOffset += rasterShift; wpPos += bufferShift; } }
/// <summary> /// 8-bit unpacked samples => RGBA w/ unassociated alpha /// </summary> private static void putRGBUAseparate8bittile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) { while (height-- > 0) { for (x = width; x-- > 0; ) { int av = buffer[offset4]; int rv = (buffer[offset1] * av + 127) / 255; int gv = (buffer[offset2] * av + 127) / 255; int bv = (buffer[offset3] * av + 127) / 255; raster[rasterOffset] = PACK4(rv, gv, bv, av); rasterOffset++; offset1++; offset2++; offset3++; offset4++; } offset1 += bufferShift; offset2 += bufferShift; offset3 += bufferShift; offset4 += bufferShift; rasterOffset += rasterShift; } }
/// <summary> /// 8-bit packed CMYK samples w/o Map => RGB. /// NB: The conversion of CMYK->RGB is *very* crude. /// </summary> private static void putRGBcontig8bitCMYKtile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int samplesperpixel = img.samplesperpixel; bufferShift *= samplesperpixel; while (height-- > 0) { int _x; for (_x = width; _x >= 8; _x -= 8) { for (int rc = 0; rc < 8; rc++) { short k = (short)(255 - buffer[offset + 3]); short r = (short)((k * (255 - buffer[offset])) / 255); short g = (short)((k * (255 - buffer[offset + 1])) / 255); short b = (short)((k * (255 - buffer[offset + 2])) / 255); raster[rasterOffset] = PACK(r, g, b); rasterOffset++; offset += samplesperpixel; } } if (_x > 0) { if (_x <= 7 && _x > 0) { for (int i = _x; i > 0; i--) { short k = (short)(255 - buffer[offset + 3]); short r = (short)((k * (255 - buffer[offset])) / 255); short g = (short)((k * (255 - buffer[offset + 1])) / 255); short b = (short)((k * (255 - buffer[offset + 2])) / 255); raster[rasterOffset] = PACK(r, g, b); rasterOffset++; offset += samplesperpixel; } } } rasterOffset += rasterShift; offset += bufferShift; } }
/// <summary> /// 16-bit unpacked samples => RGBA w/ unassociated alpha /// </summary> private static void putRGBUAseparate16bittile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) { short[] wrgba = Tiff.ByteArrayToShorts(buffer, 0, buffer.Length); offset1 /= sizeof(short); offset2 /= sizeof(short); offset3 /= sizeof(short); offset4 /= sizeof(short); while (height-- > 0) { for (x = width; x-- > 0; ) { int a = W2B(wrgba[offset4]); int r = (W2B(wrgba[offset1]) * a + 127) / 255; int g = (W2B(wrgba[offset2]) * a + 127) / 255; int b = (W2B(wrgba[offset3]) * a + 127) / 255; raster[rasterOffset] = PACK4(r, g, b, a); rasterOffset++; offset1++; offset2++; offset3++; offset4++; } offset1 += bufferShift; offset2 += bufferShift; offset3 += bufferShift; offset4 += bufferShift; rasterOffset += rasterShift; } }
/// <summary> /// 8-bit packed CIE L*a*b 1976 samples => RGB /// </summary> private static void putcontig8bitCIELab( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { bufferShift *= 3; while (height-- > 0) { for (x = width; x-- > 0; ) { float X, Y, Z; img.cielab.CIELabToXYZ(buffer[offset], (sbyte)buffer[offset + 1], (sbyte)buffer[offset + 2], out X, out Y, out Z); int r, g, b; img.cielab.XYZToRGB(X, Y, Z, out r, out g, out b); raster[rasterOffset] = PACK(r, g, b); rasterOffset++; offset += 3; } rasterOffset += rasterShift; offset += bufferShift; } }
/////////////////////////////////////////////////////////////////////////////////////////// // Untested methods // // This methods are untested (and, probably, should be deleted). // // pickContigCase implicitly *excludes* putRGBcontig8bitCMYKMaptile from possible "put" // methods when it requires images to have Photometric.Separated *and* 8-bit // samples *and* a Map to use putRGBcontig8bitCMYKMaptile. The problem is: // no Map is ever built for Photometric.Separated *and* 8-bit samples. /// <summary> /// 8-bit packed CMYK samples w/Map => RGB /// NB: The conversion of CMYK->RGB is *very* crude. /// </summary> private static void putRGBcontig8bitCMYKMaptile( TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) { int samplesperpixel = img.samplesperpixel; byte[] Map = img.Map; bufferShift *= samplesperpixel; while (height-- > 0) { for (x = width; x-- > 0; ) { short k = (short)(255 - buffer[offset + 3]); short r = (short)((k * (255 - buffer[offset])) / 255); short g = (short)((k * (255 - buffer[offset + 1])) / 255); short b = (short)((k * (255 - buffer[offset + 2])) / 255); raster[rasterOffset] = PACK(Map[r], Map[g], Map[b]); rasterOffset++; offset += samplesperpixel; } offset += bufferShift; rasterOffset += rasterShift; } }
/// <summary> /// Creates new instance of the <see cref="TiffRgbaImage"/> class. /// </summary> /// <param name="tif"> /// The instance of the <see cref="BitMiracle.LibTiff.Classic"/> class used to retrieve /// image data. /// </param> /// <param name="stopOnError"> /// if set to <c>true</c> then an error will terminate the conversion; otherwise "get" /// methods will continue processing data until all the possible data in the image have /// been requested. /// </param> /// <param name="errorMsg">The error message (if any) gets placed here.</param> /// <returns> /// New instance of the <see cref="TiffRgbaImage"/> class if the image specified /// by <paramref name="tif"/> can be converted to RGBA format; otherwise, <c>null</c> is /// returned and <paramref name="errorMsg"/> contains the reason why it is being /// rejected. /// </returns> public static TiffRgbaImage Create(Tiff tif, bool stopOnError, out string errorMsg) { errorMsg = null; // Initialize to normal values TiffRgbaImage img = new TiffRgbaImage(); img.row_offset = 0; img.col_offset = 0; img.redcmap = null; img.greencmap = null; img.bluecmap = null; img.req_orientation = Orientation.BottomLeft; // It is the default img.tif = tif; img.stoponerr = stopOnError; FieldValue[] result = tif.GetFieldDefaulted(TiffTag.BitsPerSample); img.bitspersample = result[0].ToShort(); switch (img.bitspersample) { case 1: case 2: case 4: case 8: case 16: break; default: errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, can not handle images with {0}-bit samples", img.bitspersample); return null; } img.alpha = 0; result = tif.GetFieldDefaulted(TiffTag.SamplesPerPixel); img.samplesperpixel = result[0].ToShort(); result = tif.GetFieldDefaulted(TiffTag.ExtraSamples); short extrasamples = result[0].ToShort(); byte[] sampleinfo = result[1].ToByteArray(); if (extrasamples >= 1) { switch ((ExtraSample)sampleinfo[0]) { case ExtraSample.UnSpecified: if (img.samplesperpixel > 3) { // Workaround for some images without correct info about alpha channel img.alpha = ExtraSample.AssociatedAlpha; } break; case ExtraSample.AssociatedAlpha: // data is pre-multiplied case ExtraSample.UnAssociatedAlpha: // data is not pre-multiplied img.alpha = (ExtraSample)sampleinfo[0]; break; } } if (Tiff.DEFAULT_EXTRASAMPLE_AS_ALPHA) { result = tif.GetField(TiffTag.Photometric); if (result == null) img.photometric = Photometric.MinIsWhite; if (extrasamples == 0 && img.samplesperpixel == 4 && img.photometric == Photometric.RGB) { img.alpha = ExtraSample.AssociatedAlpha; extrasamples = 1; } } int colorchannels = img.samplesperpixel - extrasamples; result = tif.GetFieldDefaulted(TiffTag.Compression); Compression compress = (Compression)result[0].ToInt(); result = tif.GetFieldDefaulted(TiffTag.PlanarConfig); PlanarConfig planarconfig = (PlanarConfig)result[0].ToShort(); result = tif.GetField(TiffTag.Photometric); if (result == null) { switch (colorchannels) { case 1: if (img.isCCITTCompression()) img.photometric = Photometric.MinIsWhite; else img.photometric = Photometric.MinIsBlack; break; case 3: img.photometric = Photometric.RGB; break; default: errorMsg = string.Format(CultureInfo.InvariantCulture, "Missing needed {0} tag", photoTag); return null; } } else img.photometric = (Photometric)result[0].ToInt(); switch (img.photometric) { case Photometric.Palette: result = tif.GetField(TiffTag.Colormap); if (result == null) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Missing required \"Colormap\" tag"); return null; } short[] red_orig = result[0].ToShortArray(); short[] green_orig = result[1].ToShortArray(); short[] blue_orig = result[2].ToShortArray(); // copy the colormaps so we can modify them int n_color = (1 << img.bitspersample); img.redcmap = new short[n_color]; img.greencmap = new short[n_color]; img.bluecmap = new short[n_color]; Buffer.BlockCopy(red_orig, 0, img.redcmap, 0, n_color * sizeof(short)); Buffer.BlockCopy(green_orig, 0, img.greencmap, 0, n_color * sizeof(short)); Buffer.BlockCopy(blue_orig, 0, img.bluecmap, 0, n_color * sizeof(short)); if (planarconfig == PlanarConfig.Contig && img.samplesperpixel != 1 && img.bitspersample < 8) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, can not handle contiguous data with {0}={1}, and {2}={3} and Bits/Sample={4}", photoTag, img.photometric, "Samples/pixel", img.samplesperpixel, img.bitspersample); return null; } break; case Photometric.MinIsWhite: case Photometric.MinIsBlack: if (planarconfig == PlanarConfig.Contig && img.samplesperpixel != 1 && img.bitspersample < 8) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, can not handle contiguous data with {0}={1}, and {2}={3} and Bits/Sample={4}", photoTag, img.photometric, "Samples/pixel", img.samplesperpixel, img.bitspersample); return null; } break; case Photometric.YCBCR: // It would probably be nice to have a reality check here. if (planarconfig == PlanarConfig.Contig) { // can rely on LibJpeg.Net to convert to RGB // XXX should restore current state on exit switch (compress) { case Compression.JPEG: // TODO: when complete tests verify complete desubsampling and // YCbCr handling, remove use of JPEGCOLORMODE in favor of native // handling tif.SetField(TiffTag.JPEGCOLORMODE, JpegColorMode.RGB); img.photometric = Photometric.RGB; break; default: // do nothing break; } } // TODO: if at all meaningful and useful, make more complete support check // here, or better still, refactor to let supporting code decide whether there // is support and what meaningfull error to return break; case Photometric.RGB: if (colorchannels < 3) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, can not handle RGB image with {0}={1}", "Color channels", colorchannels); return null; } break; case Photometric.Separated: result = tif.GetFieldDefaulted(TiffTag.InkSet); InkSet inkset = (InkSet)result[0].ToByte(); if (inkset != InkSet.CMYK) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, can not handle separated image with {0}={1}", "InkSet", inkset); return null; } if (img.samplesperpixel < 4) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, can not handle separated image with {0}={1}", "Samples/pixel", img.samplesperpixel); return null; } break; case Photometric.LogL: if (compress != Compression.SGILOG) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, LogL data must have {0}={1}", "Compression", Compression.SGILOG); return null; } tif.SetField(TiffTag.SGILOGDATAFMT, 3); // 8-bit RGB monitor values. img.photometric = Photometric.MinIsBlack; // little white lie img.bitspersample = 8; break; case Photometric.LogLUV: if (compress != Compression.SGILOG && compress != Compression.SGILOG24) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, LogLuv data must have {0}={1} or {2}", "Compression", Compression.SGILOG, Compression.SGILOG24); return null; } if (planarconfig != PlanarConfig.Contig) { errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, can not handle LogLuv images with {0}={1}", "Planarconfiguration", planarconfig); return null; } tif.SetField(TiffTag.SGILOGDATAFMT, 3); // 8-bit RGB monitor values. img.photometric = Photometric.RGB; // little white lie img.bitspersample = 8; break; case Photometric.CIELAB: break; default: errorMsg = string.Format(CultureInfo.InvariantCulture, "Sorry, can not handle image with {0}={1}", photoTag, img.photometric); return null; } img.Map = null; img.BWmap = null; img.PALmap = null; img.ycbcr = null; img.cielab = null; result = tif.GetField(TiffTag.ImageWidth); img.width = result[0].ToInt(); result = tif.GetField(TiffTag.ImageLength); img.height = result[0].ToInt(); result = tif.GetFieldDefaulted(TiffTag.Orientation); img.orientation = (Orientation)result[0].ToByte(); img.isContig = !(planarconfig == PlanarConfig.Separate && colorchannels > 1); if (img.isContig) { if (!img.pickContigCase()) { errorMsg = "Sorry, can not handle image"; return null; } } else { if (!img.pickSeparateCase()) { errorMsg = "Sorry, can not handle image"; return null; } } return img; }