2010-05-09 26 views
6

Tôi cần trợ giúp đặt hình ảnh trong suốt vào khay nhớ tạm. Tôi tiếp tục nhận được "xử lý không hợp lệ". Về cơ bản, tôi cần một "bộ mắt thứ hai" để xem xét mã sau đây. (Dự án làm việc hoàn chỉnh tại ftp://missico.net/ImageVisualizer.zip.)Cần trợ giúp Đặt hình ảnh có nền trong suốt thành Clipboard

Đây là hình ảnh Thư viện lớp Visualizer Debug, nhưng tôi đã thực hiện dự án đi kèm để chạy dưới dạng tệp thi hành để thử nghiệm. (Lưu ý rằng cửa sổ là cửa sổ hộp công cụ và hiển thị trên thanh tác vụ được đặt thành false). Tôi đã mệt mỏi vì phải thực hiện chụp màn hình trên cửa sổ hộp công cụ, mở ảnh chụp màn hình bằng trình chỉnh sửa hình ảnh và sau đó xóa nền được thêm vào vì nó là một ảnh chụp màn hình. Vì vậy, tôi nghĩ rằng tôi sẽ nhanh chóng đưa hình ảnh trong suốt vào clipboard. Vâng, vấn đề là ... không hỗ trợ minh bạch cho Clipboard.SetImage. Google để giải cứu ... không hoàn toàn.

Đây là những gì tôi có cho đến nay. Tôi lấy từ một số nguồn. Xem mã cho tham chiếu chính. Vấn đề của tôi là "xử lý không hợp lệ" khi sử dụng CF_DIBV5. Tôi có cần sử dụng BITMAPV5HEADER và CreateDIBitmap không?

Bất kỳ trợ giúp nào từ bạn GDI/GDI + Wizards sẽ được đánh giá cao.

public static void SetClipboardData(Bitmap bitmap, IntPtr hDC) { 

     const uint SRCCOPY = 0x00CC0020; 
     const int CF_DIBV5 = 17; 
     const int CF_BITMAP = 2; 

     //'reference 
     //'http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/816a35f6-9530-442b-9647-e856602cc0e2 

     IntPtr memDC = CreateCompatibleDC(hDC); 
     IntPtr memBM = CreateCompatibleBitmap(hDC, bitmap.Width, bitmap.Height); 

     SelectObject(memDC, memBM); 

     using (Graphics g = Graphics.FromImage(bitmap)) { 

      IntPtr hBitmapDC = g.GetHdc(); 
      IntPtr hBitmap = bitmap.GetHbitmap(); 

      SelectObject(hBitmapDC, hBitmap); 

      BitBlt(memDC, 0, 0, bitmap.Width, bitmap.Height, hBitmapDC, 0, 0, SRCCOPY); 

      if (!OpenClipboard(IntPtr.Zero)) { 
       throw new System.Runtime.InteropServices.ExternalException("Could not open Clipboard", new Win32Exception()); 
      } 

      if (!EmptyClipboard()) { 
       throw new System.Runtime.InteropServices.ExternalException("Unable to empty Clipboard", new Win32Exception()); 
      } 

      //IntPtr hClipboard = SetClipboardData(CF_BITMAP, memBM); //works but image is not transparent 

      //all my attempts result in SetClipboardData returning hClipboard = IntPtr.Zero 
      IntPtr hClipboard = SetClipboardData(CF_DIBV5, memBM); 


      //because 
      if (hClipboard == IntPtr.Zero) { 

       // InnerException: System.ComponentModel.Win32Exception 
       //   Message="The handle is invalid" 
       //   ErrorCode=-2147467259 
       //   NativeErrorCode=6 
       //   InnerException: 

       throw new System.Runtime.InteropServices.ExternalException("Could not put data on Clipboard", new Win32Exception()); 
      } 

      if (!CloseClipboard()) { 
       throw new System.Runtime.InteropServices.ExternalException("Could not close Clipboard", new Win32Exception()); 
      } 

      g.ReleaseHdc(hBitmapDC); 

     } 

    } 

    private void __copyMenuItem_Click(object sender, EventArgs e) { 

     using (Graphics g = __pictureBox.CreateGraphics()) { 

      IntPtr hDC = g.GetHdc(); 

      MemoryStream ms = new MemoryStream(); 

      __pictureBox.Image.Save(ms, ImageFormat.Png); 

      ms.Seek(0, SeekOrigin.Begin); 

      Image imag = Image.FromStream(ms); 

      // Derive BitMap object using Image instance, so that you can avoid the issue 
      //"a graphics object cannot be created from an image that has an indexed pixel format" 

      Bitmap img = new Bitmap(new Bitmap(imag)); 

      SetClipboardData(img, hDC); 

      g.ReleaseHdc(); 

     } 

    } 
+0

Bạn có thể làm rõ bước nào cung cấp cho bạn lỗi xử lý không hợp lệ không? –

Trả lời

0

Bitmap.GetHbitmap() thực sự sẽ tổng hợp bitmap thành nền mờ, mất kênh alpha. This question giải quyết cách lấy HBITMAP với kênh alpha nguyên vẹn.

+0

Cùng một vấn đề về "xử lý không hợp lệ". Hơn nữa, nếu tôi sử dụng CF_BITMAP nền là Đen thay vì "kiểm soát màu xám" được sử dụng bởi các PictureBox kiểm soát. – AMissico

+0

Tài liệu cho các định dạng clipboard (xem http://msdn.microsoft.com/en-us/library/ms649013.aspx) nói rằng khi sử dụng CF_DIB, xử lý là bộ đệm được cấp phát bằng GlobalAlloc, chứa BITMAPINFO cấu trúc, tiếp theo là bit bitmap. Tôi hy vọng rằng bạn sẽ phải marshall này bằng tay. Khi sử dụng CF_BITMAP, bạn có thể vượt qua HBITMAP, nhưng không có siêu dữ liệu liên quan để chỉ định việc sử dụng kênh alpha, vì vậy nó bị bỏ qua. –

1

tôi thấy ba vấn đề:

  1. Các hợp lệ xử lý lỗi có thể đến từ rời memBM chọn vào memDC. Bạn nên luôn luôn chọn bitmap ra khỏi DC trước khi truyền nó ở bất kỳ nơi nào khác.

  2. BitBlt là cuộc gọi GDI (không phải GDI +). GDI không bảo vệ kênh alpha. Trên các phiên bản Windows mới hơn, bạn có thể sử dụng AlphaBlend để tổng hợp một bitmap với alpha lên nền, nhưng tổng hợp sẽ không có kênh alpha.

  3. Bạn đã tạo bitmap tương thích, có nghĩa là bitmap có cùng định dạng màu như DC mà bạn đã truyền vào (mà tôi giả định giống với màn hình). Vì vậy, bitmap của bạn có thể kết thúc như 16-bit, 24-bit, 32-bit, hoặc thậm chí 8-bit, tùy thuộc vào cách màn hình được thiết lập. Nếu BitBlt đã giữ nguyên kênh alpha của bản gốc, bạn có thể mất nó khi chuyển sang định dạng màn hình.

+1

AlphaBlend đã được khoảng một thời gian. –

4

Có một vài điều bạn có thể làm để thắt chặt codebase một chút và tôi đã thực hiện một số bài tập về nhà trên CF_DIBV5 mà bạn có thể thấy hữu ích.

Trước tiên, trong __copyMenuItem_Click(), chúng tôi có bốn bản sao hoàn chỉnh của hình ảnh của bạn, nhiều hơn mức cần thiết.

  1. __pictureBox.Image
  2. Image imag = Image.FromStream(ms);
  3. new Bitmap(imag)
  4. Bitmap img = new Bitmap(new Bitmap(imag)); (bitmap ngoài)

Bên cạnh đó, bạn MemoryStream, imag, new Bitmap(imag), và img không nhận xử lý, có thể dẫn đến các vấn đề.

Nếu không có thay đổi mục đích của mã (và có lẽ không giải quyết vấn đề xử lý), bạn có thể viết lại phương pháp như thế này:

private void __copyMenuItem_Click(object sender, EventArgs e) 
{ 
    var image = __pictureBox.Image; 
    using (var g = __pictureBox.CreateGraphics()) 
    using (var bmp = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb)) 
    using (var bmpg = Graphics.FromImage(bmp)) 
    { 
     IntPtr hDC = g.GetHdc(); 
     bmpg.DrawImage(image, 0, 0, image.Width, image.Height); 
     SetClipboardData(bmp, hDC); 
     g.ReleaseHdc(); 
    } 
} 

Điều tiếp theo mà trông giống như nó sẽ đòi hỏi sự chú ý là dòng:

IntPtr hClipboard = SetClipboardData(CF_DIBV5, memBM); 

Tôi khá chắc chắn rằng bạn phải sắp xếp cấu trúc BITMAPV5HEADER để chuyển bit vào clipboard khi sử dụng CF_DIBV5. Tôi đã sai trước đây, nhưng tôi sẽ xác minh rằng memBM thực sự chứa tiêu đề. Một chỉ báo tốt là liệu DWORD đầu tiên (UInt32) có giá trị 124 (kích thước của tiêu đề theo byte) hay không.

Nhận xét cuối cùng của tôi có nhiều đề xuất hơn cặp mắt thứ hai. Tôi thấy rằng các ứng dụng ảnh như GIMP, Paint.NET, Fireworks và PhotoScape dường như có hỗ trợ kém hoặc không tồn tại đối với dán CF_DIBV5 (Format17). bạn có thể xem xét việc sao chép vào clipboard định dạng PNG, với bitmap mờ như là bản sao lưu chỉ trong trường hợp ứng dụng đích không hỗ trợ PNG. Tôi sử dụng một phương pháp khuyến nông để tạo điều kiện này:

public static void CopyMultiFormatBitmapToClipboard(this Image image) 
{ 
    using (var opaque = image.CreateOpaqueBitmap(Color.White)) 
    using (var stream = new MemoryStream()) 
    { 
     image.Save(stream, ImageFormat.Png); 

     Clipboard.Clear(); 
     var data = new DataObject(); 
     data.SetData(DataFormats.Bitmap, true, opaque); 
     data.SetData("PNG", true, stream); 
     Clipboard.SetDataObject(data, true); 
    } 
} 

Với phương pháp mở rộng trong tay, phương pháp __copyMenuItem_Click() của bạn có thể được giảm xuống sau, và phương pháp SetClipboardData() thể gỡ bỏ hoàn toàn:

private void __copyMenuItem_Click(object sender, EventArgs e) 
{ 
    __pictureBox.Image.CopyMultiFormatBitmapToClipboard(); 
} 

Bây giờ , như chúng ta đã thảo luận về một chủ đề khác, hỗ trợ PNG có thể không cắt nó cho bạn. Tôi đã thử nghiệm phương pháp này trên một vài ứng dụng; tuy nhiên, nó sẽ tùy thuộc vào bạn để xác định liệu đây có phải là sự hỗ trợ minh bạch đầy đủ cho các yêu cầu của bạn hay không.

  • GIMP: minh bạch được hỗ trợ
  • Fireworks (3.0): tính minh bạch hỗ trợ
  • Photoscape: nền trắng
  • Paint.NET: nền trắng
  • MS PowerPoint 2007: minh bạch được hỗ trợ
  • MS Word 2007: nền trắng
  • MS Paint (Win7): nền trắng

Thảo luận về mọi thứ tôi xem xét sẽ quá dài đối với Stack Overflow. Tôi có thêm mã mẫu và thảo luận có sẵn tại blog của tôi: http://www.notesoncode.com/articles/2010/08/16/HandlingTransparentImagesOnTheClipboardIsForSomeReasonHard.aspx

Chúc may mắn!

+0

Tôi không có thời gian để thử mã của bạn, nhưng tôi cho bạn tiền thưởng cho tất cả công việc khó khăn của bạn. Mã được đăng là mã ném đi mà tôi thu thập từ nhiều nguồn để có được bất kỳ thứ gì để hoạt động. Vì vậy, "bộ mắt thứ hai" và đề xuất của bạn là một trợ giúp tuyệt vời. Ngay sau khi tôi bị cuốn vào, tôi sẽ thử các đề xuất của bạn. – AMissico

+0

Tôi phát hiện ra rằng rất nhiều ứng dụng (ab) sử dụng DIB 32 bit (tiêu đề 40 byte) của loại "bitfields" (nén = 3) như là ARGB, trong khi theo thông số kỹ thuật của nó nó chỉ RGB. Trong số này, màn hình in Windows 10 (thiết lập các bit 'alpha' trong ảnh chụp màn hình đến 255 ở mọi nơi ngoại trừ phần không tồn tại của thiết lập màn hình kép của tôi) và Google Chrome. – Nyerguds

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