SkiaSharp Tiff支持

Dou*_*g S 3 .net c# libtiff.net .net-core skiasharp

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

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

一个想法:也许有一种有效的方法来转换tiff流> png流> SKBitmap对象?我不确定System.Drawing能否有效处理tiff> png流.另一个可能的选择是LibTiff.Net,但需要一个如何将tiff流转换为png流的示例.

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

其他想法?

Mat*_*hew 8

@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)
Run Code Online (Sandbox Code Playgroud)

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

// 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;
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

一个要点住在这里: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);
Run Code Online (Sandbox Code Playgroud)

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

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