Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Here is my attempt to subclass BuilderExtension, override DecodeStream, and glue in libtiff.net.</p> <p>Here are my two test tiffs <a href="http://minus.com/lbch5irLumoSQQ" rel="nofollow">different size pages</a> &amp; <a href="http://minus.com/lboR49aXnS7tlZ" rel="nofollow">pages with different colour space</a>. I've compiled against ImageBuilder v3.2.2.</p> <p>I'm getting a fault when I use the ConsoleApplication project in the ImageResizer v3.2.2 source with the following code. I've run out of time to fix this issue at the moment, so I thought I'd put my progress here in case anyone else wants to take this further.</p> <p><strong>My test code</strong> </p> <p>I think is failing at <code>ImageResizer.ImageBuilder.PrepareDestinationBitmap()</code> on line <code>s.destBitmap = ...</code> with a <em>Parameter is not valid.</em> error. The dimensions are w=9994,h=7102 and I suspect this might be a memory issue - however if I decode the tiff using <code>DecodeTiffTo32BitBitmap()</code> first and pass that to ImageResizer as a memory stream the error does not exist. Also, this failure appears for both v3.2.2 and v3.2.3.</p> <pre><code>var Path = @"[whatever]\TiffDecoderTests\"; var c = new Config(); new TiffDecoder().Install(c); c.BuildImage(Path + "different_page_sizes.TIF", Path + "different_page_sizes-page2-TiffReader.png", "format=png&amp;page=2&amp;decoder=tiffdecoder"); </code></pre> <p><strong>My TiffDecoder class:</strong></p> <pre><code>namespace ImageResizer.Plugins.TiffDecoder { using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Globalization; using System.IO; using System.Linq; using BitMiracle.LibTiff.Classic; using ImageResizer; using ImageResizer.Configuration; using ImageResizer.Resizing; public class TiffDecoder : BuilderExtension, IPlugin, IFileExtensionPlugin { private static readonly string[] supportedExtensions = new[] { ".tiff", ".tif", ".tff" }; public IPlugin Install(Config c) { c.Plugins.add_plugin(this); return this; } public bool Uninstall(Config c) { c.Plugins.remove_plugin(this); return true; } public IEnumerable&lt;string&gt; GetSupportedFileExtensions() { return supportedExtensions; } public override Bitmap DecodeStream(Stream s, ResizeSettings settings, string optionalPath) { var requested = "tiffdecoder".Equals(settings["decoder"], StringComparison.OrdinalIgnoreCase); if (!string.IsNullOrEmpty(settings["decoder"]) &amp;&amp; !requested) { return null; // Don't take it away from the requested decoder } // If a tiff is coming in, try first, before Bitmap tries to parse it. if (requested || (optionalPath != null &amp;&amp; supportedExtensions.Any(ext =&gt; optionalPath.EndsWith(ext, StringComparison.OrdinalIgnoreCase)))) { return this.Decode(s, settings); } return null; } public override Bitmap DecodeStreamFailed(Stream s, ResizeSettings settings, string optionalPath) { // Catch tiff files not ending in an expected extension try { return this.Decode(s, settings); } catch { return null; } } public Bitmap Decode(Stream s, ResizeSettings settings) { return DecodeTiffTo32BitBitmap(s, settings["page"]); } private static Bitmap DecodeTiffTo32BitBitmap(Stream s, string page) { s.Position = 0; using (var image = Tiff.ClientOpen(string.Empty, "r", s, new TiffStream())) { SetDirectory(image, page); // Find the width and height of the image var value = image.GetField(TiffTag.IMAGEWIDTH); var width = value[0].ToInt(); value = image.GetField(TiffTag.IMAGELENGTH); var height = value[0].ToInt(); // Read the image into the memory buffer var raster = new int[height * width]; if (!image.ReadRGBAImage(width, height, raster)) { return null; } // Caller needs to handle disposing the bitmap. var bmp = new Bitmap(width, height, PixelFormat.Format32bppRgb); var rect = new Rectangle(0, 0, bmp.Width, bmp.Height); var bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb); var bits = new byte[bmpdata.Stride * bmpdata.Height]; for (var y = 0; y &lt; bmp.Height; y++) { var rasterOffset = y * bmp.Width; var bitsOffset = (bmp.Height - y - 1) * bmpdata.Stride; for (var x = 0; x &lt; bmp.Width; x++) { var rgba = raster[rasterOffset++]; bits[bitsOffset++] = (byte)((rgba &gt;&gt; 16) &amp; 0xff); bits[bitsOffset++] = (byte)((rgba &gt;&gt; 8) &amp; 0xff); bits[bitsOffset++] = (byte)(rgba &amp; 0xff); bits[bitsOffset++] = (byte)((rgba &gt;&gt; 24) &amp; 0xff); } } System.Runtime.InteropServices.Marshal.Copy(bits, 0, bmpdata.Scan0, bits.Length); bmp.UnlockBits(bmpdata); return bmp; } } /// &lt;summary&gt; /// Sets the TIFF directory to the first that matches the requested page number, otherwise: /// - If page not set OR less than 0, use the first page in the TIFF. /// - If tiff has no directories with the "PAGE" tag, use the first frame in the TIFF. /// &lt;/summary&gt; /// &lt;param name="image"&gt;The image.&lt;/param&gt; /// &lt;param name="page"&gt;The page.&lt;/param&gt; /// &lt;remarks&gt; /// From what I can tell a TIFF image can store any pages in any directory, so the 1st dir /// might not hold the 1st page - this is why we first iterate all frames looking for a PAGE tag. /// &lt;/remarks&gt; private static void SetDirectory(Tiff image, string page) { var pageIndex = GetAdjustedPage(page); var numberOfDirectories = image.NumberOfDirectories(); for (short dirNumber = 0; dirNumber &lt; numberOfDirectories; ++dirNumber) { image.SetDirectory(dirNumber); // Ignore pages that might be thumbnails var subFileType = image.GetField(TiffTag.SUBFILETYPE); if (subFileType == null || subFileType[0].ToString().ToUpper() != "PAGE") { continue; } // Stop if page found if (pageIndex == int.Parse(image.GetField(TiffTag.PAGENUMBER)[0].ToString())) { return; } } // Default to the first frame if the page wasn't found. image.SetDirectory(0); } private static int GetAdjustedPage(string page) { var pageIndex = 1; if (!string.IsNullOrEmpty(page)) { int.TryParse(page, NumberStyles.Number, NumberFormatInfo.InvariantInfo, out pageIndex); } // So users can use 1-based numbers return Math.Max(1, pageIndex) - 1; } } } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload