2010-05-10 30 views
12

Tôi làm cách nào để lưu hình ảnh bằng mã hóa ban đầu?Làm cách nào để lưu hình ảnh ở định dạng gốc?

Dường như cách duy nhất để lưu hình ảnh là sử dụng BitmapEncoder nhưng tôi không biết làm cách nào để có được định dạng chính xác từ hình ảnh.

Ví dụ:

Clipboard.GetImage() trả về một InteropBitmap mà dường như không chứa bất kỳ thông tin về định dạng ban đầu.

Tôi cũng đã cố gắng sử dụng một phương pháp mở rộng:

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); 
} 

nhưng vấn đề là

  1. các ImageSource không phải là luôn luôn là một BitmapImage (Clipboard.GetImage()) ví dụ và
  2. image.StreamSource có thể rỗng trong một số trường hợp (có vẻ như khi hình ảnh được tải qua Uri tương đối)

Bất kỳ đề xuất nào?

+0

Bạn có thể giải thích ngữ cảnh mà bạn đang gặp phải điều này không? Dường như với tôi rằng nếu bạn đang lưu một hình ảnh, chỉ cần lưu nó ở định dạng bạn muốn. BitmapImage có vẻ là một định dạng và hình ảnh gốc đã được chuyển đổi. –

Trả lời

19

Câu hỏi cơ bản ở đây là "Làm cách nào để có một Nguồn hình ảnh tùy ý và tìm hiểu định dạng ban đầu được mã hóa?"

Câu trả lời là bạn không thể luôn làm điều này, nhưng đối với hầu hết các mục đích thực tế, có một giải pháp thay thế. Như mã của bạn ở trên cho thấy, một khi bạn đã học được những gì định dạng để sử dụng phần còn lại là dễ dàng.

Lý do bạn không thể luôn luôn tìm ra định dạng ban đầu là có thể (với một số mã rất khó!) Để phân lớp BitmapSource để tạo một ImageSource mới lấy dữ liệu từ bất cứ nơi nào bạn muốn. Ví dụ, tôi có thể thực hiện một PseudoRandomBitmapSource trả về các pixel ngẫu nhiên. Trong trường hợp này, "định dạng gốc" có thể là hạt giống được sử dụng cho trình tạo số ngẫu nhiên.

Nếu bạn đang đối phó với một trong những xây dựng trong lớp ImageSource, cách để tìm hiểu mã hóa ban đầu phụ thuộc vào lớp chính xác mà bạn đang sử dụng:

  1. Đối BitmapImage, bạn có thể sử dụng StreamSource hoặc UriSource , bất kỳ cái nào được đặt. Một trong hai cách này có thể được chuyển đến một quá tải BitmapDecoder.Create. Mã ví dụ của bạn cho thấy cách thực hiện điều này cho StreamSource. Nó hoàn toàn giống với UriSource ngoại trừ bạn cần phải có Uri(BaseUri, UriSource) mới và chuyển nó tới quá tải BitmapDecoder.Create có một Uri.

  2. Đối với ColorConvertedBitmap, CroppedBitmap, FormatConvertedBitmap và TransformedBitmap có thuộc tính "Nguồn" công khai mà bạn có thể sử dụng để lấy nguồn cơ bản, sau đó kiểm tra mã hóa theo cách đệ quy bằng thuật toán này.

  3. Đối với CachedBitmap, bạn chỉ có thể truy cập bitmap nguồn thông qua trường nội bộ. Bạn có thể truy cập bằng cách sử dụng phản ánh nếu bạn có đủ đặc quyền, nếu không bạn sẽ không may mắn.

  4. Đối với RenderTargetBitmap, WritableBitmap, D3DImage và DrawingImage không có mã hóa ban đầu vì hình ảnh đang được xây dựng "đang hoạt động" từ định dạng vectơ hoặc thuật toán.

  5. Đối với BitmapFrame, hãy sử dụng thuộc tính Bộ giải mã để nhận bộ giải mã, sau đó sử dụng CodecInfo.ContainerFormat.

  6. Đối với InteropBitmap hoặc UnmanagedBitmapWrapper, nó rất phức tạp. Về cơ bản, bạn cần sử dụng sự phản chiếu để đọc thuộc tính WicSourceHandle bên trong, sau đó gọi cho DangerousGetHandle() để nhận IntPtr mà thực ra là một IUnkown không được quản lý. Sử dụng mã không được quản lý, QueryInterface cho IWICBitmapDecoder. Nếu thành công bạn có thể gọi IWICBitmapDecoder.GetContainerFormat để lấy định dạng Guid (đây vẫn là mã không được quản lý). Nếu không, tất cả thông tin về nguồn gốc đã bị mất.

Như bạn có thể thấy, có khá một vài tình huống mà bạn không thể có được nguồn (ví dụ như một InteropBitmap cho một bitmap giải mã đầy đủ) hoặc trong trường hợp nhận được nguồn đòi hỏi kỹ thuật đặc biệt và ưu đãi (unmanaged code cho InteropBitmap hoặc phản chiếu trên các trường nội bộ cho CachedBitmap).

Do khó có thể lấy định dạng ban đầu, nên tìm cách bảo toàn thông tin này khi thông tin được chuyển vào mã của bạn. Nếu bạn kiểm soát nguồn của hình ảnh, có thể đơn giản như việc tạo lớp ImageSourceAndFormat:

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

Điều này đặc biệt hữu ích nếu bạn đang đặt hình ảnh vào khay nhớ tạm. Ngoài việc thêm hình ảnh vào DataObject bình thường, bạn cũng có thể thêm một đối tượng BitmapSourceAndFormat. Nếu bạn làm điều này, thao tác Dán sẽ nhận được một DataObject chứa cả hai định dạng này. Mã của bạn sau đó chỉ cần kiểm tra đối tượng BitmapSourceAndFormat trước tiên. Nếu tìm thấy nó chỉ đơn giản là có thể truy cập nó để có được định dạng ban đầu. Nếu không, nó phải nghỉ mát với các phương tiện được mô tả ở trên.

Lưu ý cuối cùng: Đối với bột nhão trong clipboard, bạn có thể kiểm tra các chuỗi định dạng dữ liệu có sẵn. Một số công cụ hữu ích là: Bitmap, Dib, Tiff, Gif, Jpeg và FileName. Đối với "Tiff", "Gif" và "Jpeg", bạn có thể mã hóa cứng định dạng bắt buộc. Đối với "FileName", bạn có thể tự mở tập tin để có được một luồng để chuyển đến một BitmapDecoder. Đối với "Dib" hoặc "Bitmap" không có gì khác, bạn biết định dạng gốc đã bị mất.

+0

wow - đây là một câu trả lời tuyệt vời. Cảm ơn bạn rất nhiều! Lời khuyên tuyệt vời về sao chép/dán! –

+0

+ cảm ơn không chỉ cho tôi biết những gì tôi muốn biết mà còn cho tôi biết câu hỏi cơ bản của tôi là gì! Tôi nên mô tả nó tốt hơn. –

Các vấn đề liên quan