@ 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 мс.
Надеюсь, я не звучу слишком резко / негативно по отношению к вашему коду, я не имею в виду это никоим образом.