2013-03-28 120 views
9

我的應用程序應該能夠將32位圖像(RGB + alpha通道)複製到剪貼板並從剪貼板粘貼這些圖像。爲此,我計劃使用CF_DIBV5,因爲BITMAPV5HEADER結構的字段爲bV5AlphaMaskWin32剪貼板和alpha通道圖像

問題是,關於圖像數據應該如何存儲在剪貼板中,似乎並沒有達成共識。在進行一些測試時,我發現應用程序之間存在若干差異,使得幾乎不可能提出一個通用解決方案。

這裏是我的意見:

  1. 當我從Word 2010或XnView的alpha通道圖像複製到剪貼板,它存儲不左乘的像素數據。

  2. 但是,當我使用Firefox或Chrome複製圖像時,像素數據似乎被Alpha通道預乘。

  3. Firefox的設置bV5AlphaMask到0xff000000,而大多數其他應用程序沒有設置這個根本,但保持0。這很奇怪,因爲這些應用程序放置到的DIB實際包含在最高的8位alpha通道但他們仍然剪貼板設置bV5AlphaMask爲0。所以,人們必須作一個假設,如果位深度爲32,有即使bV5AlphaMask爲0

爲了削減長話短說我的基本問題是alpha通道:是有關alpha通道數據如何存儲在剪貼板上的官方信息?我特別想知道數據是否必須預乘。正如您在上面看到的那樣,Word 2010和XnView不會預覽,而Firefox和Chrome則可以。但是瞭解色彩通道是否應該預乘是非常重要的。

非常感謝你爲此付出了一些光芒!

UPDATE 2 粘貼到Paint.NET現在可以正常工作了。它是由我的代碼中的一個錯誤引起的,如果alpha通道爲0,那麼沒有將顏色通道設置爲0,即在這種情況下預處理沒有正確完成,這似乎讓Paint.NET感到困惑。

仍然未解決的是Internet Explorer 10的問題。將帶alpha通道的PNG複製到剪貼板時,IE 10只在剪貼板上放置了24位CF_DIBV5,但Paint.NET可以將此位圖粘貼到alpha通道必須是IE 10暴露給剪貼板的另一種格式。也許它暴露了一個PNG使用CFSTR_FILECONTENTS和CFSTR_FILEDESCRIPTOR。

UPDATE 我現在按照arx下面描述的方式實現它,它工作得很好。但是,仍然有兩件事讓我感到困惑:

1)從我的應用程序粘貼到Paint.NET的Alpha通道圖像不保留Alpha通道。圖像在Paint.NET中不透明。但是,從Firefox和Chrome粘貼到Paint.NET的工作完美無缺,alpha通道仍然保留!我甩掉了完整的DIBV5,它與我的應用程序完全相同,但它仍然適用於FF和Chrome,但不適用於我的應用程序,所以必須有其他東西! Firefox和Chrome必須做別的事情,我的應用程序不會做!?

2)對於Internet Explorer 10也是如此。從IE 10將alpha通道圖像粘貼到我的應用程序根本無法工作......我得到的DIB深度爲24,即根本沒有alpha通道。但是,從IE 10粘貼到Paint.NET時,Alpha通道就在那裏!所以在這裏肯定還有更多的東西......

+0

當你從一個瀏覽器,它還會複製一個HTML片段複製到剪貼板的圖像。 (Internet Explorer額外複製圖像URL。)如果Paint.NET從HTML片段中獲取URL並直接加載圖像,這將解釋1)和2)。請注意,這種技術有其自身的問題:如果圖像URL不公開(即,如果它需要登錄),那麼粘貼應用程序將無法訪問它。 – arx

+0

我將PNG上傳到我的網站空間上的.htaccess保護文件夾,使用Firefox登錄,將PNG複製到剪貼板。然後我關閉Firefox並打開Paint.NET並粘貼圖像......它工作的很好!所以它似乎沒有直接使用URL。嗯,也許我需要查看Firefox或Chrome源代碼,看看它們到剪貼板的內容到底是什麼? – Andreas

+0

當我探索這個時,我寫了一個簡單的工具來顯示和轉儲剪貼板格式。我已將源添加到我的答案中。 – arx

回答

13

我確定在CF_DIBV5中存儲alpha的正確方法,但它確實沒有關係。應用程序已經不一致地處理它,所以如果你希望你的應用程序能夠很好地與其他應用程序一起玩,你就不能使用CF_DIBV5。

我研究過複製和粘貼透明位圖。我的目標是成功地將透明位圖粘貼到兩個版本的Office和GIMP中。我看了幾個可能的格式:

CF_BITMAP

透明度總是被忽略。

CF_DIB

在常規0xAARRGGBB格式使用32bpp的BI_RGB。 GIMP支持這一點,但沒有別的。

CF_DIBV5

GIMP不支持這一點。

「PNG」

粘貼支持:GIMP,Word 2000中,Excel 2000中,Excel 2007和PowerPoint 2007中
粘貼不支持:Word 2007和OneNote 2007中

所有這些應用的成功如果您複製位圖,則導出「PNG」。

但是,Word和OneNote 2007 粘貼從資源管理器複製的PNG文件。所以,我想出了以下內容:

解決方案複製

將您的透明位圖以PNG格式。

廣告以下剪貼板格式:

「PNG」 - 原始PNG數據。
CF_DIB - 用於不處理透明度的應用程序(如油漆)。
CFSTR_FILEDESCRIPTOR - 使PNG看起來像一個文件。文件描述符應該有一個帶有「.png」擴展名的發明文件名。
CFSTR_FILECONTENTS - 內容必須公開爲IStream;只是使用HGLOBAL似乎不起作用。數據與「PNG」數據相同。

完成此操作後,我可以成功地將透明位圖粘貼到GIMP,Office 2000和Office 2007.您還可以將PNG直接粘貼到資源管理器文件夾中。

更新

我意識到,我只回答了一半的問題。這對複製非常有用,但如果您想從僅複製CF_DIBV5的應用程序(如Firefox)粘貼,則無用。

我建議您使用「PNG」(如果可用),否則回退到CF_DIBV5,將其視爲預乘。這將正確處理Word 2010(導出「PNG」),Firefox和Chrome。 XnView只導出非乘法的CF_DIBV5,所以這將無法正常工作。我不確定你能做得更好。

LSCF - 一種探索剪貼板格式

這個工具是用於顯示可用的剪貼板格式列表的工具的來源。它也可以寫一個文件。我稱之爲lscf。在Visual Studio中創建一個win32控制檯應用程序,並將此源粘貼到主函數上。它有一個非常小的錯誤:如果您輸錯了格式名稱,它從不顯示「未知格式」錯誤。

#include <Windows.h> 

#include <stdio.h> 
#include <tchar.h> 

#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) 

LPCTSTR cfNames[] = { 
    _T("CF_TEXT"), 
    _T("CF_BITMAP"), 
    _T("CF_METAFILEPICT"), 
    _T("CF_SYLK"), 
    _T("CF_DIF"), 
    _T("CF_TIFF"), 
    _T("CF_OEMTEXT"), 
    _T("CF_DIB"), 
    _T("CF_PALETTE"), 
    _T("CF_PENDATA"), 
    _T("CF_RIFF"), 
    _T("CF_WAVE"), 
    _T("CF_UNICODETEXT"), 
    _T("CF_ENHMETAFILE"), 
    _T("CF_HDROP"), 
    _T("CF_LOCALE"), 
    _T("CF_DIBV5") 
}; 

int LookupFormat(LPCTSTR name) 
{ 
    for (int i = 0; i != ARRAY_SIZE(cfNames); ++i) 
    { 
     if (_tcscmp(cfNames[i], name) == 0) 
      return i + 1; 
    } 

    return RegisterClipboardFormat(name); 
} 

void PrintFormatName(int format) 
{ 
    if (!format) 
     return; 

    if ((format > 0) && (format <= ARRAY_SIZE(cfNames))) 
    { 
     _tprintf(_T("%s\n"), cfNames[format - 1]); 
    } 
    else 
    { 
     TCHAR buffer[100]; 

     if (GetClipboardFormatName(format, buffer, ARRAY_SIZE(buffer))) 
      _tprintf(_T("%s\n"), buffer); 
     else 
      _tprintf(_T("#%i\n"), format); 
    } 
} 

void WriteFormats() 
{ 
    int count = 0; 
    int format = 0; 
    do 
    { 
     format = EnumClipboardFormats(format); 
     if (format) 
     { 
      ++count; 
      PrintFormatName(format); 
     } 
    } 
    while (format != 0); 

    if (!count) 
     _tprintf(_T("Clipboard is empty!\n")); 
} 

void SaveFormat(int format, LPCTSTR filename) 
{ 
    HGLOBAL hData = (HGLOBAL)GetClipboardData(format); 

    LPVOID data = GlobalLock(hData); 

    HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); 
    if (hFile != INVALID_HANDLE_VALUE) 
    { 
     DWORD bytesWritten; 
     WriteFile(hFile, data, GlobalSize(hData), &bytesWritten, 0); 
     CloseHandle(hFile); 
    } 

    GlobalUnlock(hData); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    if (!OpenClipboard(0)) 
    { 
     _tprintf(_T("Cannot open clipboard\n")); 
     return 1; 
    } 

    if (argc == 1) 
    { 
     WriteFormats(); 
    } 
    else if (argc == 3) 
    { 
     int format = LookupFormat(argv[1]); 
     if (format == 0) 
     { 
      _tprintf(_T("Unknown format\n")); 
      return 1; 
     } 

     SaveFormat(format, argv[2]); 
    } 
    else 
    { 
     _tprintf(_T("lscf\n")); 
     _tprintf(_T("List available clipboard formats\n\n")); 
     _tprintf(_T("lscf CF_NAME filename\n")); 
     _tprintf(_T("Write format CF_NAME to file filename\n\n")); 
    } 

    CloseClipboard(); 

    return 0; 
} 
+2

+1多麼令人愉快的答案! –

+0

非常感謝您的努力!我按照您的建議實施了它,但仍然存在一些問題。查看我的原始帖子,瞭解有關此問題的更新。再次感謝 – Andreas

+0

Gimp不是唯一接受帶alpha通道的32bpp DIB圖像的程序。 Flash也接受這些圖像。 – Dwedit