解码多页 TIFF 容器

Decode a Multipage TIFF Container

使用 的反向词,我能够使用 Windows.Graphics.Imaging[= 编写一个函数来解码多页 TIFF 文件19=] :

 private async Task TIFHandler( StorageFile file)
 {
   var random = new Random();
   StorageFolder storage = null;
   try
   {
     uint frameCount;
     using (IRandomAccessStream randomAccessStream = await file.OpenAsync(FileAccessMode.Read, StorageOpenOptions.None))
     {
       Windows.Graphics.Imaging.BitmapDecoder bitmapDecoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(Windows.Graphics.Imaging.BitmapDecoder.TiffDecoderId, randomAccessStream);
       frameCount = bitmapDecoder.FrameCount;
       if (frameCount == 16)
       {
         StorageFolder mainfolder = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFolderAsync((string)ApplicationData.Current.LocalSettings.Values["DynamicFolder"], CreationCollisionOption.OpenIfExists);
         storage = await mainfolder.CreateFolderAsync(String.Format("{0:X6}", random.Next(0x1000000)), CreationCollisionOption.ReplaceExisting);
       }
       if (storage != null)
       {
         for (int frame = 0; frame < frameCount; frame++)
         {
           var bitmapFrame = await bitmapDecoder.GetFrameAsync(Convert.ToUInt32(frame));
           var softImage = await bitmapFrame.GetSoftwareBitmapAsync();
           byte[] array = null;
           using (var ms = new InMemoryRandomAccessStream())
           {
             Windows.Graphics.Imaging.BitmapEncoder bitmapEncoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId, ms);
             bitmapEncoder.SetSoftwareBitmap(softImage);
             try
             {
               await bitmapEncoder.FlushAsync();
             }
             catch (Exception ex) { }
             array = new byte[ms.Size];
             WriteableBitmap wb = new WriteableBitmap((int)bitmapDecoder.PixelWidth, (int)bitmapDecoder.PixelHeight);
             using (Stream stream = wb.PixelBuffer.AsStream())
             {
               await stream.WriteAsync(array, 0, array.Length);
             }
             Guid BitmapEncoderGuid = Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId;
             var bmif = await storage.CreateFileAsync($"X{frame}.png", CreationCollisionOption.ReplaceExisting);
             using (IRandomAccessStream stream = await bmif.OpenAsync(FileAccessMode.ReadWrite))
             {
               Windows.Graphics.Imaging.BitmapEncoder encoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(BitmapEncoderGuid, stream);
               Stream pixelStream = wb.PixelBuffer.AsStream();
               byte[] pixels = new byte[pixelStream.Length];
               await pixelStream.ReadAsync(pixels, 0, pixels.Length);
               encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
                                                (uint)wb.PixelWidth,
                                                (uint)wb.PixelHeight,
                                                96.0,
                                                96.0,
                                                pixels);
               await encoder.FlushAsync();
             }
           }
         }
       }
     }
   }
   catch (Exception)
   {
     this.DynamicOperation.Text = "Error Reading TIFF Container";
     ProgressBar.Visibility = Visibility.Collapsed;
     AddTIFButton.IsEnabled = true;
     if (storage != null) { await storage.DeleteAsync(); }
   }
}

所以从外观上看,我似乎能够解码 TIFF 图像(我得到了正确的帧数)但是因为我收到的是 BitmapFrame,所以我使用该函数将其转换为 SoftImage . 这次我使用 BitmapEncoder 将 SoftImage 保存为 PNG。这是我在使用正确的方法将 SoftImage 另存为 PNG 文件时遇到困难的地方!

获取SoftwareBitmap时,只需调用要另存为.png的StorageFile的OpenAsync方法即可获取随机访问流。然后使用 BitmapEncoder.CreateAsync 方法获取指定流的 BitmapEncoder class 实例,并在编码器中设置 SoftwareBitmap。之后,调用 FlushAsync 使编码器将图像数据写入指定文件。关于如何将SoftwareBitmap保存为png文件的更多细节,可以参考这个document.

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var random = new Random();
    StorageFolder folder = KnownFolders.PicturesLibrary;
    StorageFile MyOriginalfile = await folder.GetFileAsync("file_example_TIFF_1MB.tiff");

    StorageFolder storage = null;
    try
    {
        uint frameCount;
        using (IRandomAccessStream randomAccessStream = await MyOriginalfile.OpenAsync(FileAccessMode.Read, StorageOpenOptions.None))
        {
            Windows.Graphics.Imaging.BitmapDecoder bitmapDecoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(Windows.Graphics.Imaging.BitmapDecoder.TiffDecoderId, randomAccessStream);
            frameCount = bitmapDecoder.FrameCount;
            if (frameCount == 16)
            {
                StorageFolder mainfolder = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFolderAsync("DynamicFolder");
                storage = await mainfolder.CreateFolderAsync(String.Format("{0:X6}", random.Next(0x1000000)), CreationCollisionOption.ReplaceExisting);
            }
            if (storage != null)
            {
                for (int frame = 0; frame < frameCount; frame++)
                {
                    var bitmapFrame = await bitmapDecoder.GetFrameAsync(Convert.ToUInt32(frame));
                    var softImage = await bitmapFrame.GetSoftwareBitmapAsync();
                    var bmif = await storage.CreateFileAsync($"X{frame}.png", CreationCollisionOption.ReplaceExisting);
                    SaveSoftwareBitmapToFile(softImage, bmif);
                }
            }
        }
    }
    catch { }
}

private async void SaveSoftwareBitmapToFile(SoftwareBitmap softwareBitmap, StorageFile outputFile)
{
    using (IRandomAccessStream stream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
    {
        // Create an encoder with the desired format
        BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
        // Set the software bitmap
        encoder.SetSoftwareBitmap(softwareBitmap);
        encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
        encoder.IsThumbnailGenerated = true;
        try
        {
            await encoder.FlushAsync();
        }
        catch (Exception err)
        {
            const int WINCODEC_ERR_UNSUPPORTEDOPERATION = unchecked((int)0x88982F81);
            switch (err.HResult)
            {
                case WINCODEC_ERR_UNSUPPORTEDOPERATION:
                    // If the encoder does not support writing a thumbnail, then try again
                    // but disable thumbnail generation.
                    encoder.IsThumbnailGenerated = false;
                    break;
                default:
                    throw;
            }
        }
        if (encoder.IsThumbnailGenerated == false)
        {
            await encoder.FlushAsync();
        }
    }
}