12

元のエンコーディングで画像を保存するにはどうすればよいですか?

画像を保存する唯一の方法はBitmapEncoderを使用することのようですが、画像から正しい形式を取得する方法がわかりません。

例:

Clipboard.GetImage()は、元の形式に関する情報が含まれていないように見えるInteropBitmapを返します。

また、Extensionメソッドを使用してみました。

public static void Save(this BitmapImage image, System.IO.Stream stream)
{
    var decoder = BitmapDecoder.Create(image.StreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    var encoder = BitmapEncoder.Create(decoder.CodecInfo.ContainerFormat);
    foreach (var frame in decoder.Frames)
    {
        encoder.Frames.Add(BitmapFrame.Create(frame));
    }
    encoder.Save(stream);
}

しかし問題はそれです

  1. たとえば、ImageSourceは必ずしもBitmapImage(Clipboard.GetImage())であるとは限りません。
  2. image.StreamSourceは、場合によってはnullになる可能性があります(相対URIを介して画像が読み込まれる場合のようです)

助言がありますか?

4

1 に答える 1

20

ここでの基本的な質問は、「任意のImageSourceを取得して、それが最初にエンコードされた形式を知るにはどうすればよいですか?」です。

答えは、常にこれを実行できるとは限らないということですが、ほとんどの実用的な目的のために、利用可能な回避策があります。上記のコードが示すように、使用する形式を学んだら、残りは簡単です。

元の形式を常に見つけることができない理由は、(非常にトリッキーなコードで!)BitmapSourceをサブクラス化して、好きな場所からデータを取得する新しいImageSourceを作成できるためです。たとえば、ランダムなピクセルを返すPseudoRandomBitmapSourceを実装できます。この場合、「元の形式」はおそらく乱数ジェネレーターに使用されるシードです。

組み込みのImageSourceクラスのいずれかを扱っている場合、元のエンコーディングを見つける方法は、使用している正確なクラスによって異なります。

  1. BitmapImageの場合、StreamSourceまたはUriSourceのいずれかが設定されている方を使用できます。これらのいずれかをBitmapDecoder.Createオーバーロードに渡すことができます。サンプルコードは、StreamSourceに対してこれを行う方法を示しています。これはUriSourceでもまったく同じですが、新しくUri(BaseUri, UriSource)してBitmapDecoderに渡す必要がある点が異なります。Uriを使用するオーバーロードを作成します。

  2. ColorConvertedBitmap、CroppedBitmap、FormatConvertedBitmap、およびTransformedBitmapには、基になるソースを取得し、このアルゴリズムを使用してそのエンコーディングを再帰的にチェックするために使用できるパブリック「Source」プロパティがあります。

  3. CachedBitmapの場合、内部フィールドを介してのみソースビットマップにアクセスできます。十分な特権がある場合はリフレクションを使用してアクセスできます。そうでない場合は運が悪いです。

  4. RenderTargetBitmap、WritableBitmap、D3DImage、およびDrawingImageの場合、画像はベクトル形式またはアルゴリズムから「オンザフライ」で構築されているため、元のエンコードはありません。

  5. BitmapFrameの場合、Decoderプロパティを使用してデコーダーを取得してから、CodecInfo.ContainerFormatを使用します。

  6. InteropBitmapまたはUnmanagedBitmapWrapperの場合、これは非常に複雑です。基本的に、リフレクションを使用して内部WicSourceHandleプロパティを読み取り、DangerousGetHandle()を呼び出して、実際には管理されていないIUnkownであるIntPtrを取得する必要があります。アンマネージコードを使用して、IWICBitmapDecoderのQueryInterface。成功した場合は、IWICBitmapDecoder.GetContainerFormatを呼び出して、フォーマットGuidを取得できます(これはまだ管理されていないコードです)。そうでない場合、元のソースに関するすべての情報が失われています。

ご覧のとおり、ソースを取得できない状況(完全にデコードされたビットマップのInteropBitmapなど)や、ソースの取得に特別な技術と特権(InteropBitmapのアンマネージコード、または内部フィールドへの反映)が必要な状況がかなりあります。 CachedBitmapの場合)。

元の形式を取得することは困難または不可能であることが多いため、コードに渡されるときにこの情報を保持する方法を探すことをお勧めします。画像のソースを制御する場合は、ImageSourceAndFormatクラスを作成するのと同じくらい簡単です。

public class BitmapSourceAndFormat
{
  public ImageSource Source { get; set; }
  public Guid OriginalFormat { get; set; }
}

これは、画像をクリップボードに配置する場合に特に便利です。通常のデータオブジェクトへの画像の追加に加えて、BitmapSourceAndFormatオブジェクトを追加することもできます。これを行うと、貼り付け操作はこれらの両方の形式を含むDataObjectを受け取ります。この場合、コードは最初にBitmapSourceAndFormatオブジェクトをチェックするだけで済みます。見つかった場合は、単にアクセスして元の形式を取得できます。そうでない場合は、上記の手段に頼らなければなりません。

最後の注意:クリップボードの貼り付けについては、使用可能なデータ形式の文字列を確認できます。便利なものには、Bitmap、Dib、Tiff、Gif、Jpeg、FileNameなどがあります。「Tiff」、「Gif」、および「Jpeg」の場合、必要な形式をハードコーディングできます。「FileName」の場合、ファイルを自分で開いて、BitmapDecoderに渡すストリームを取得できます。他に何もない「Dib」または「Bitmap」の場合、元の形式が失われていることがわかります。

于 2010-08-05T01:00:33.183 に答える