public TiffGrid(string filename, string NewFileName) { ValuesToWrite = new Dictionary<int, Dictionary<int, double>>(); tiff_org = BitMiracle.LibTiff.Classic.Tiff.Open(Path.GetFullPath(filename), "r"); tiff = BitMiracle.LibTiff.Classic.Tiff.Open(Path.GetFullPath(NewFileName), "w"); foreach (TiffTag enu in Enum.GetValues(typeof(TiffTag))) { var val = tiff_org.GetField(enu); if (val != null & enu != TiffTag.EXTRASAMPLES) tiff.SetField(enu, val[0]); } for (int i = 0; i < tiff_org.GetTagListCount(); i++) { int k = tiff_org.GetTagListEntry(i); var ff = tiff_org.FindFieldInfo((TiffTag)k, TiffType.ANY); tiff.MergeFieldInfo(new TiffFieldInfo[] { ff }, 1); var val = tiff_org.GetField((TiffTag)tiff_org.GetTagListEntry(i)); tiff.SetField((TiffTag)k, val[0], val[1]); } var val2 = tiff_org.GetField((TiffTag)33922)[1].ToDoubleArray(); XOrigin = val2[3]; YOrigin = val2[4]; //Upper basegrid assumes Lower val2 = tiff_org.GetField((TiffTag)33550)[1].ToDoubleArray(); GridSize = val2[0]; GridSize = val2[1]; NumberOfColumns = tiff_org.GetField(TiffTag.IMAGEWIDTH)[0].ToInt(); NumberOfRows = tiff_org.GetField(TiffTag.IMAGELENGTH)[0].ToInt(); //Shift YOrigin to lower left YOrigin -= GridSize * NumberOfRows; scanline = new byte[tiff_org.ScanlineSize()]; bits = scanline.Count() / NumberOfColumns; ScanLineCache = new Dictionary<int, byte[]>(); }
public bool Copy(Tiff inImage, Tiff outImage) { int width = 0; FieldValue[] result = inImage.GetField(TiffTag.IMAGEWIDTH); if (result != null) { width = result[0].ToInt(); outImage.SetField(TiffTag.IMAGEWIDTH, width); } int length = 0; result = inImage.GetField(TiffTag.IMAGELENGTH); if (result != null) { length = result[0].ToInt(); outImage.SetField(TiffTag.IMAGELENGTH, length); } short bitspersample = 1; result = inImage.GetField(TiffTag.BITSPERSAMPLE); if (result != null) { bitspersample = result[0].ToShort(); outImage.SetField(TiffTag.BITSPERSAMPLE, bitspersample); } short samplesperpixel = 1; result = inImage.GetField(TiffTag.SAMPLESPERPIXEL); if (result != null) { samplesperpixel = result[0].ToShort(); outImage.SetField(TiffTag.SAMPLESPERPIXEL, samplesperpixel); } if (m_compression != (Compression)(-1)) outImage.SetField(TiffTag.COMPRESSION, m_compression); else { result = inImage.GetField(TiffTag.COMPRESSION); if (result != null) { m_compression = (Compression)result[0].ToInt(); outImage.SetField(TiffTag.COMPRESSION, m_compression); } } result = inImage.GetFieldDefaulted(TiffTag.COMPRESSION); Compression input_compression = (Compression)result[0].ToInt(); result = inImage.GetFieldDefaulted(TiffTag.PHOTOMETRIC); Photometric input_photometric = (Photometric)result[0].ToShort(); if (input_compression == Compression.JPEG) { /* Force conversion to RGB */ inImage.SetField(TiffTag.JPEGCOLORMODE, JpegColorMode.RGB); } else if (input_photometric == Photometric.YCBCR) { /* Otherwise, can't handle subsampled input */ result = inImage.GetFieldDefaulted(TiffTag.YCBCRSUBSAMPLING); short subsamplinghor = result[0].ToShort(); short subsamplingver = result[1].ToShort(); if (subsamplinghor != 1 || subsamplingver != 1) { Console.Error.WriteLine("tiffcp: {0}: Can't copy/convert subsampled image.", inImage.FileName()); return false; } } if (m_compression == Compression.JPEG) { if (input_photometric == Photometric.RGB && m_jpegcolormode == JpegColorMode.RGB) outImage.SetField(TiffTag.PHOTOMETRIC, Photometric.YCBCR); else outImage.SetField(TiffTag.PHOTOMETRIC, input_photometric); } else if (m_compression == Compression.SGILOG || m_compression == Compression.SGILOG24) { outImage.SetField(TiffTag.PHOTOMETRIC, samplesperpixel == 1 ? Photometric.LOGL : Photometric.LOGLUV); } else { if (input_compression != Compression.JPEG) copyTag(inImage, outImage, TiffTag.PHOTOMETRIC, 1, TiffType.SHORT); } if (m_fillorder != 0) outImage.SetField(TiffTag.FILLORDER, m_fillorder); else copyTag(inImage, outImage, TiffTag.FILLORDER, 1, TiffType.SHORT); /* * Will copy `Orientation' tag from input image */ result = inImage.GetFieldDefaulted(TiffTag.ORIENTATION); m_orientation = (Orientation)result[0].ToByte(); switch (m_orientation) { case Orientation.BOTRIGHT: case Orientation.RIGHTBOT: Tiff.Warning(inImage.FileName(), "using bottom-left orientation"); m_orientation = Orientation.BOTLEFT; break; case Orientation.LEFTBOT: case Orientation.BOTLEFT: break; case Orientation.TOPRIGHT: case Orientation.RIGHTTOP: default: Tiff.Warning(inImage.FileName(), "using top-left orientation"); m_orientation = Orientation.TOPLEFT; break; case Orientation.LEFTTOP: case Orientation.TOPLEFT: break; } outImage.SetField(TiffTag.ORIENTATION, m_orientation); /* * Choose tiles/strip for the output image according to * the command line arguments (-tiles, -strips) and the * structure of the input image. */ if (m_outtiled == -1) { if (inImage.IsTiled()) m_outtiled = 1; else m_outtiled = 0; } if (m_outtiled != 0) { /* * Setup output file's tile width&height. If either * is not specified, use either the value from the * input image or, if nothing is defined, use the * library default. */ if (m_tilewidth == -1) { result = inImage.GetFieldDefaulted(TiffTag.TILEWIDTH); if (result != null) m_tilewidth = result[0].ToInt(); } if (m_tilelength == -1) { result = inImage.GetFieldDefaulted(TiffTag.TILELENGTH); if (result != null) m_tilelength = result[0].ToInt(); } outImage.DefaultTileSize(ref m_tilewidth, ref m_tilelength); outImage.SetField(TiffTag.TILEWIDTH, m_tilewidth); outImage.SetField(TiffTag.TILELENGTH, m_tilelength); } else { /* * RowsPerStrip is left unspecified: use either the * value from the input image or, if nothing is defined, * use the library default. */ if (m_rowsperstrip == 0) { result = inImage.GetField(TiffTag.ROWSPERSTRIP); if (result == null) m_rowsperstrip = outImage.DefaultStripSize(m_rowsperstrip); else m_rowsperstrip = result[0].ToInt(); if (m_rowsperstrip > length && m_rowsperstrip != -1) m_rowsperstrip = length; } else if (m_rowsperstrip == -1) m_rowsperstrip = length; outImage.SetField(TiffTag.ROWSPERSTRIP, m_rowsperstrip); } if (m_config != PlanarConfig.UNKNOWN) outImage.SetField(TiffTag.PLANARCONFIG, m_config); else { result = inImage.GetField(TiffTag.PLANARCONFIG); if (result != null) { m_config = (PlanarConfig)result[0].ToShort(); outImage.SetField(TiffTag.PLANARCONFIG, m_config); } } if (samplesperpixel <= 4) copyTag(inImage, outImage, TiffTag.TRANSFERFUNCTION, 4, TiffType.SHORT); copyTag(inImage, outImage, TiffTag.COLORMAP, 4, TiffType.SHORT); /* SMinSampleValue & SMaxSampleValue */ switch (m_compression) { case Compression.JPEG: outImage.SetField(TiffTag.JPEGQUALITY, m_quality); outImage.SetField(TiffTag.JPEGCOLORMODE, m_jpegcolormode); break; case Compression.LZW: case Compression.ADOBE_DEFLATE: case Compression.DEFLATE: if (m_predictor != -1) outImage.SetField(TiffTag.PREDICTOR, m_predictor); else { result = inImage.GetField(TiffTag.PREDICTOR); if (result != null) { m_predictor = result[0].ToShort(); outImage.SetField(TiffTag.PREDICTOR, m_predictor); } } break; case Compression.CCITTFAX3: case Compression.CCITTFAX4: if (m_compression == Compression.CCITTFAX3) { if (m_g3opts != Group3Opt.UNKNOWN) outImage.SetField(TiffTag.GROUP3OPTIONS, m_g3opts); else { result = inImage.GetField(TiffTag.GROUP3OPTIONS); if (result != null) { m_g3opts = (Group3Opt)result[0].ToShort(); outImage.SetField(TiffTag.GROUP3OPTIONS, m_g3opts); } } } else copyTag(inImage, outImage, TiffTag.GROUP4OPTIONS, 1, TiffType.LONG); copyTag(inImage, outImage, TiffTag.BADFAXLINES, 1, TiffType.LONG); copyTag(inImage, outImage, TiffTag.CLEANFAXDATA, 1, TiffType.LONG); copyTag(inImage, outImage, TiffTag.CONSECUTIVEBADFAXLINES, 1, TiffType.LONG); copyTag(inImage, outImage, TiffTag.FAXRECVPARAMS, 1, TiffType.LONG); copyTag(inImage, outImage, TiffTag.FAXRECVTIME, 1, TiffType.LONG); copyTag(inImage, outImage, TiffTag.FAXSUBADDRESS, 1, TiffType.ASCII); break; } result = inImage.GetField(TiffTag.ICCPROFILE); if (result != null) outImage.SetField(TiffTag.ICCPROFILE, result[0], result[1]); result = inImage.GetField(TiffTag.NUMBEROFINKS); if (result != null) { short ninks = result[0].ToShort(); outImage.SetField(TiffTag.NUMBEROFINKS, ninks); result = inImage.GetField(TiffTag.INKNAMES); if (result != null) { string inknames = result[0].ToString(); string[] parts = inknames.Split(new char[] { '\0' }); int inknameslen = 0; foreach (string part in parts) inknameslen += part.Length + 1; outImage.SetField(TiffTag.INKNAMES, inknameslen, inknames); } } result = inImage.GetField(TiffTag.PAGENUMBER); if (m_pageInSeq == 1) { if (m_pageNum < 0) { /* only one input file */ if (result != null) outImage.SetField(TiffTag.PAGENUMBER, result[0], result[1]); } else { outImage.SetField(TiffTag.PAGENUMBER, m_pageNum++, 0); } } else { if (result != null) { if (m_pageNum < 0) { /* only one input file */ outImage.SetField(TiffTag.PAGENUMBER, result[0], result[1]); } else { outImage.SetField(TiffTag.PAGENUMBER, m_pageNum++, 0); } } } int NTAGS = g_tags.Length; for (int i = 0; i < NTAGS; i++) { tagToCopy p = g_tags[i]; copyTag(inImage, outImage, p.tag, p.count, p.type); } return pickFuncAndCopy(inImage, outImage, bitspersample, samplesperpixel, length, width); }
private static void copyTag(Tiff inImage, Tiff outImage, TiffTag tag, short count, TiffType type) { FieldValue[] result = null; switch (type) { case TiffType.SHORT: result = inImage.GetField(tag); if (result != null) { if (count == 1) outImage.SetField(tag, result[0]); else if (count == 2) outImage.SetField(tag, result[0], result[1]); else if (count == 4) outImage.SetField(tag, result[0], result[1], result[2]); else if (count == -1) outImage.SetField(tag, result[0], result[1]); } break; case TiffType.LONG: result = inImage.GetField(tag); if (result != null) outImage.SetField(tag, result[0]); break; case TiffType.RATIONAL: result = inImage.GetField(tag); if (result != null) outImage.SetField(tag, result[0]); break; case TiffType.ASCII: result = inImage.GetField(tag); if (result != null) outImage.SetField(tag, result[0]); break; case TiffType.DOUBLE: result = inImage.GetField(tag); if (result != null) outImage.SetField(tag, result[0]); break; default: Tiff.Error(inImage.FileName(), "Data type {0} is not supported, tag {1} skipped.", tag, type); break; } }
static bool cvt_by_tile(Tiff inImage, Tiff outImage, int width, int height) { int tile_width = 0; int tile_height = 0; FieldValue[] result = inImage.GetField(TiffTag.TILEWIDTH); if (result != null) { tile_width = result[0].ToInt(); result = inImage.GetField(TiffTag.TILELENGTH); if (result != null) tile_height = result[0].ToInt(); } if (result == null) { Tiff.Error(inImage.FileName(), "Source image not tiled"); return false; } outImage.SetField(TiffTag.TILEWIDTH, tile_width); outImage.SetField(TiffTag.TILELENGTH, tile_height); // Allocate tile buffer int raster_size = multiply(tile_width, tile_height); int rasterByteSize = multiply(raster_size, sizeof(int)); if (raster_size == 0 || rasterByteSize == 0) { Tiff.Error(inImage.FileName(), "Can't allocate buffer for raster of size {0}x{1}", tile_width, tile_height); return false; } int[] raster = new int[raster_size]; byte[] rasterBytes = new byte[rasterByteSize]; // Allocate a scanline buffer for swapping during the vertical mirroring pass. // (Request can't overflow given prior checks.) int[] wrk_line = new int[tile_width]; // Loop over the tiles. for (int row = 0; row < height; row += tile_height) { for (int col = 0; col < width; col += tile_width) { // Read the tile into an RGBA array if (!inImage.ReadRGBATile(col, row, raster)) return false; // For some reason the ReadRGBATile() function chooses the lower left corner // as the origin. Vertically mirror scanlines. for (int i_row = 0; i_row < tile_height / 2; i_row++) { int topIndex = tile_width * i_row * sizeof(int); int bottomIndex = tile_width * (tile_height - i_row - 1) * sizeof(int); Buffer.BlockCopy(raster, topIndex, wrk_line, 0, tile_width * sizeof(int)); Buffer.BlockCopy(raster, bottomIndex, raster, topIndex, tile_width * sizeof(int)); Buffer.BlockCopy(wrk_line, 0, raster, bottomIndex, tile_width * sizeof(int)); } // Write out the result in a tile. int tile = outImage.ComputeTile(col, row, 0, 0); Buffer.BlockCopy(raster, 0, rasterBytes, 0, rasterByteSize); if (outImage.WriteEncodedTile(tile, rasterBytes, rasterByteSize) == -1) return false; } } return true; }
public bool tiffcvt(Tiff inImage, Tiff outImage) { FieldValue[] result = inImage.GetField(TiffTag.IMAGEWIDTH); if (result == null) return false; int width = result[0].ToInt(); result = inImage.GetField(TiffTag.IMAGELENGTH); if (result == null) return false; int height = result[0].ToInt(); copyField(inImage, outImage, TiffTag.SUBFILETYPE); outImage.SetField(TiffTag.IMAGEWIDTH, width); outImage.SetField(TiffTag.IMAGELENGTH, height); outImage.SetField(TiffTag.BITSPERSAMPLE, 8); outImage.SetField(TiffTag.COMPRESSION, m_compression); outImage.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB); copyField(inImage, outImage, TiffTag.FILLORDER); outImage.SetField(TiffTag.ORIENTATION, Orientation.TOPLEFT); if (m_noAlpha) outImage.SetField(TiffTag.SAMPLESPERPIXEL, 3); else outImage.SetField(TiffTag.SAMPLESPERPIXEL, 4); if (!m_noAlpha) { short[] v = new short[1]; v[0] = (short)ExtraSample.ASSOCALPHA; outImage.SetField(TiffTag.EXTRASAMPLES, 1, v); } copyField(inImage, outImage, TiffTag.XRESOLUTION); copyField(inImage, outImage, TiffTag.YRESOLUTION); copyField(inImage, outImage, TiffTag.RESOLUTIONUNIT); outImage.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG); if (!m_testFriendly) outImage.SetField(TiffTag.SOFTWARE, Tiff.GetVersion()); copyField(inImage, outImage, TiffTag.DOCUMENTNAME); if (m_processByBlock && inImage.IsTiled()) return cvt_by_tile(inImage, outImage, width, height); else if (m_processByBlock) return cvt_by_strip(inImage, outImage, width, height); return cvt_whole_image(inImage, outImage, width, height); }
private static void copyField(Tiff inImage, Tiff outImage, TiffTag tag) { FieldValue[] result = inImage.GetField(tag); if (result != null) outImage.SetField(tag, result[0]); }
/// <summary> /// Read the whole image into one big RGBA buffer and then write out /// strips from that. This is using the traditional TIFFReadRGBAImage() /// API that we trust. /// </summary> private bool cvt_whole_image(Tiff inImage, Tiff outImage, int width, int height) { int pixel_count = width * height; /* XXX: Check the integer overflow. */ if (width == 0 || height == 0 || (pixel_count / width) != height) { Tiff.Error(inImage.FileName(), "Malformed input file; can't allocate buffer for raster of {0}x{1} size", width, height); return false; } m_rowsPerStrip = outImage.DefaultStripSize(m_rowsPerStrip); outImage.SetField(TiffTag.ROWSPERSTRIP, m_rowsPerStrip); int[] raster = new int[pixel_count]; /* Read the image in one chunk into an RGBA array */ if (!inImage.ReadRGBAImageOriented(width, height, raster, Orientation.TOPLEFT, false)) return false; /* * Do we want to strip away alpha components? */ byte[] rasterBytes; int rasterByteSize; if (m_noAlpha) { rasterByteSize = pixel_count * 3; rasterBytes = new byte[rasterByteSize]; for (int i = 0, rasterBytesPos = 0; i < pixel_count; i++) { byte[] bytes = BitConverter.GetBytes(raster[i]); rasterBytes[rasterBytesPos++] = bytes[0]; rasterBytes[rasterBytesPos++] = bytes[1]; rasterBytes[rasterBytesPos++] = bytes[2]; } } else { rasterByteSize = pixel_count * 4; rasterBytes = new byte[rasterByteSize]; Buffer.BlockCopy(raster, 0, rasterBytes, 0, rasterByteSize); } /* * Write out the result in strips */ for (int row = 0; row < height; row += m_rowsPerStrip) { int bytes_per_pixel; if (m_noAlpha) bytes_per_pixel = 3; else bytes_per_pixel = 4; int rows_to_write; if (row + m_rowsPerStrip > height) rows_to_write = height - row; else rows_to_write = m_rowsPerStrip; int offset = bytes_per_pixel * row * width; int count = bytes_per_pixel * rows_to_write * width; if (outImage.WriteEncodedStrip(row / m_rowsPerStrip, rasterBytes, offset, count) == -1) return false; } return true; }
private bool cvt_by_strip(Tiff inImage, Tiff outImage, int width, int height) { FieldValue[] result = inImage.GetField(TiffTag.ROWSPERSTRIP); if (result == null) { Tiff.Error(inImage.FileName(), "Source image not in strips"); return false; } m_rowsPerStrip = result[0].ToInt(); outImage.SetField(TiffTag.ROWSPERSTRIP, m_rowsPerStrip); // Allocate strip buffer int raster_size = multiply(width, m_rowsPerStrip); int rasterByteSize = multiply(raster_size, sizeof(int)); if (raster_size == 0 || rasterByteSize == 0) { Tiff.Error(inImage.FileName(), "Can't allocate buffer for raster of size {0}x{1}", width, m_rowsPerStrip); return false; } int[] raster = new int[raster_size]; byte[] rasterBytes = new byte[rasterByteSize]; // Allocate a scanline buffer for swapping during the vertical mirroring pass. // (Request can't overflow given prior checks.) int[] wrk_line = new int[width]; // Loop over the strips. for (int row = 0; row < height; row += m_rowsPerStrip) { // Read the strip into an RGBA array if (!inImage.ReadRGBAStrip(row, raster)) return false; // Figure out the number of scanlines actually in this strip. int rows_to_write; if (row + m_rowsPerStrip > height) rows_to_write = height - row; else rows_to_write = m_rowsPerStrip; // For some reason the TIFFReadRGBAStrip() function chooses the lower left corner // as the origin. Vertically mirror scanlines. for (int i_row = 0; i_row < rows_to_write / 2; i_row++) { int topIndex = width * i_row * sizeof(int); int bottomIndex = width * (rows_to_write - i_row - 1) * sizeof(int); Buffer.BlockCopy(raster, topIndex, wrk_line, 0, width * sizeof(int)); Buffer.BlockCopy(raster, bottomIndex, raster, topIndex, width * sizeof(int)); Buffer.BlockCopy(wrk_line, 0, raster, bottomIndex, width * sizeof(int)); } // Write out the result in a strip int bytesToWrite = rows_to_write * width * sizeof(int); Buffer.BlockCopy(raster, 0, rasterBytes, 0, bytesToWrite); if (outImage.WriteEncodedStrip(row / m_rowsPerStrip, rasterBytes, bytesToWrite) == -1) return false; } return true; }
private static void convertToTiff(Bitmap bmp, Tiff tif, PixelFormat outputFormat) { if (outputFormat != PixelFormat.Format24bppRgb && outputFormat != PixelFormat.Format32bppArgb) throw new System.ArgumentOutOfRangeException(); byte[] raster = getImageRasterBytes(bmp, outputFormat); tif.SetField(TiffTag.IMAGEWIDTH, bmp.Width); tif.SetField(TiffTag.IMAGELENGTH, bmp.Height); tif.SetField(TiffTag.COMPRESSION, Compression.LZW); tif.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB); tif.SetField(TiffTag.ROWSPERSTRIP, bmp.Height); tif.SetField(TiffTag.XRESOLUTION, bmp.HorizontalResolution); tif.SetField(TiffTag.YRESOLUTION, bmp.VerticalResolution); tif.SetField(TiffTag.BITSPERSAMPLE, 8); if (outputFormat == PixelFormat.Format32bppArgb) tif.SetField(TiffTag.SAMPLESPERPIXEL, 4); else tif.SetField(TiffTag.SAMPLESPERPIXEL, 3); tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG); int stride = raster.Length / bmp.Height; convertRGBSamples(raster, bmp.Width, bmp.Height, outputFormat); for (int i = 0, offset = 0; i < bmp.Height; i++) { bool res = tif.WriteScanline(raster, offset, i, 0); Assert.IsTrue(res); offset += stride; } }
/// <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.BOTLEFT; // 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.ASSOCALPHA; } break; case ExtraSample.ASSOCALPHA: // data is pre-multiplied case ExtraSample.UNASSALPHA: // 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.ASSOCALPHA; 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 ushort[n_color]; img.greencmap = new ushort[n_color]; img.bluecmap = new ushort[n_color]; Buffer.BlockCopy(red_orig, 0, img.redcmap, 0, n_color * sizeof(ushort)); Buffer.BlockCopy(green_orig, 0, img.greencmap, 0, n_color * sizeof(ushort)); Buffer.BlockCopy(blue_orig, 0, img.bluecmap, 0, n_color * sizeof(ushort)); 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; }
private static void SetTiffField(TiffInfo info, BitMiracle.LibTiff.Classic.Tiff image) { if (image == null) { throw new NullReferenceException("image"); } // We need to set some values for basic tags before we can add any data image.SetField(TiffTag.IMAGEWIDTH, info.Width); image.SetField(TiffTag.IMAGELENGTH, info.Height); image.SetField(TiffTag.BITSPERSAMPLE, info.BitsPerSample); image.SetField(TiffTag.SAMPLESPERPIXEL, info.SamplesPerPixel); image.SetField(TiffTag.ORIENTATION, info.Orientation); image.SetField(TiffTag.ROWSPERSTRIP, info.Height); image.SetField(TiffTag.XRESOLUTION, info.XResolution); image.SetField(TiffTag.YRESOLUTION, info.YResolution); image.SetField(TiffTag.RESOLUTIONUNIT, info.ResolutionUnit); image.SetField(TiffTag.PLANARCONFIG, info.PlanarConfig); image.SetField(TiffTag.PHOTOMETRIC, info.Photometric); image.SetField(TiffTag.COMPRESSION, info.Compression); image.SetField(TiffTag.FILLORDER, info.FillOrder); }