首页 文章

SkiaSharp Tiff支持

提问于
浏览
2

目前,SkiaSharp不支持tiff图像 . (它支持jpg,gif,bmp,png和其他一些 . )

如何将tiff图像转换为SKBitmap对象?

一个想法:也许's an efficient way to convert a tiff stream > png stream > SKBitmap object? I'我不确定 System.Drawing 能否有效地处理tiff> png流 . 另一种可能的选择是LibTiff.Net,但需要一个如何将tiff流转换为png流的示例 .

另一个想法:访问tiff像素并将其直接绘制到SKCanvas上?

其他想法?

2 回答

  • 4

    @DougS

    您的实现大多是正确的,但由于多个内存分配和副本,它的性能不是很高 .

    我注意到你创建了3个内存块,每个块的总大小为(w * h * 4字节):

    // the int[]
    raster = new int[width * height];
    
    // the SKColor[]
    pixels = new SKColor[width * height];
    
    // the bitmap
    bitmap = new SKBitmap(width, height)
    

    您还要多次复制内存之间的像素:

    // decode the TIFF (first copy)
    tifImg.ReadRGBAImageOriented(width, height, raster, Orientation.TOPLEFT)
    
    // convert to SKColor (second copy)
    pixels[arrayOffset] = new SKColor(...);
    
    // set bitmap pixels (third copy)
    bitmap.Pixels = pixels;
    

    我想我设法创建了一个解码流的类似方法,只有一个副本和内存分配:

    public static SKBitmap OpenTiff(Stream tiffStream)
    {
        // open a TIFF stored in the stream
        using (var tifImg = Tiff.ClientOpen("in-memory", "r", tiffStream, new TiffStream()))
        {
            // read the dimensions
            var width = tifImg.GetField(TiffTag.IMAGEWIDTH)[0].ToInt();
            var height = tifImg.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
    
            // create the bitmap
            var bitmap = new SKBitmap();
            var info = new SKImageInfo(width, height);
    
            // create the buffer that will hold the pixels
            var raster = new int[width * height];
    
            // get a pointer to the buffer, and give it to the bitmap
            var ptr = GCHandle.Alloc(raster, GCHandleType.Pinned);
            bitmap.InstallPixels(info, ptr.AddrOfPinnedObject(), info.RowBytes, null, (addr, ctx) => ptr.Free(), null);
    
            // read the image into the memory buffer
            if (!tifImg.ReadRGBAImageOriented(width, height, raster, Orientation.TOPLEFT))
            {
                // not a valid TIF image.
                return null;
            }
    
            // swap the red and blue because SkiaSharp may differ from the tiff
            if (SKImageInfo.PlatformColorType == SKColorType.Bgra8888)
            {
                SKSwizzle.SwapRedBlue(ptr.AddrOfPinnedObject(), raster.Length);
            }
    
            return bitmap;
        }
    }
    

    一个要点住在这里:https://gist.github.com/mattleibow/0a09babdf0dc9d2bc3deedf85f9b57d6

    让我解释一下代码......我基本上是在创建 int[] ,但是然后将其传递给 SKBitmap 并让它接管 . 我正在固定它,因为 SKBitmap 位于非托管内存中,GC可能会移动它,但我确定在处理位图时取消固定它 .

    这是一个更详细的步骤:

    // this does not actually allocate anything
    //  - the size is 0x0 / 0 bytes of pixels
    var bitmap = new SKBitmap();
    
    // I create the only buffer for pixel data
    var raster = new int[width * height];
    
    // pin the managed array so it can be passed to unmanaged memory
    var ptr = GCHandle.Alloc(raster, GCHandleType.Pinned);
    
    // pass the pointer of the array to the bitmap
    // making sure to free the pinned memory in the dispose delegate
    //  - this is also not an allocation, as the memory already exists
    bitmap.InstallPixels(info, ptr.AddrOfPinnedObject(), info.RowBytes, null, (addr, ctx) => ptr.Free(), null);
    
    // the first and only copy from the TIFF stream into memory
    tifImg.ReadRGBAImageOriented(width, height, raster, Orientation.TOPLEFT)
    
    // an unfortunate extra memory operation for some platforms
    //  - this is usually just for Windows as it uses a BGR color format
    //  - Linux, macOS, iOS, Android all are RGB, so no swizzle is needed
    SKSwizzle.SwapRedBlue(ptr.AddrOfPinnedObject(), raster.Length);
    

    对于调试会话中的一些原始统计数据,我的一个图像的代码大约需要500毫秒,但我的代码只需要20毫秒 .

    我希望我对你的代码听起来不太苛刻/消极,我不是以任何方式表达的 .

  • 0

    我不是专家,所以我欢迎任何能够使这些代码更有效率的专家(或者有完全不同的想法来获取SKBitmap) .

    这使用LibTiff.Net

    using BitMiracle.LibTiff.Classic;
    
    . . . .
    
    public static void ConvertTiffToSKBitmap(MemoryStream tifImage)
    {
        SKColor[] pixels;
        int width, height;
        // open a Tiff stored in the memory stream, and grab its pixels
        using (Tiff tifImg = Tiff.ClientOpen("in-memory", "r", tifImage, new TiffStream()))
        {
            FieldValue[] value = tifImg.GetField(TiffTag.IMAGEWIDTH);
            width = value[0].ToInt();
    
            value = tifImg.GetField(TiffTag.IMAGELENGTH);
            height = value[0].ToInt();
    
            // Read the image into the memory buffer 
            int[] raster = new int[width * height];
            if (!tifImg.ReadRGBAImageOriented(width, height, raster, Orientation.TOPLEFT))
            {
                // Not a valid TIF image.
            }
            // store the pixels
            pixels = new SKColor[width * height];
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int arrayOffset = y * width + x;
                    int rgba = raster[arrayOffset];
                    pixels[arrayOffset] = new SKColor((byte)Tiff.GetR(rgba), (byte)Tiff.GetG(rgba), (byte)Tiff.GetB(rgba), (byte)Tiff.GetA(rgba));
                }
            }
        }
        using (SKBitmap bitmap = new SKBitmap(width, height))
        {
            bitmap.Pixels = pixels;
    
            // do something with the SKBitmap
        }
    }
    

相关问题