10

私のアプリケーションは、32 ビット イメージ (RGB + アルファ チャネル) をクリップボードにコピーし、これらのイメージをクリップボードから貼り付けることができるはずです。これには、構造体にフィールドがあるCF_DIBV5ため、使用する予定です。BITMAPV5HEADERbV5AlphaMask

問題は、画像データをクリップボードに正確に格納する方法について合意が得られていないように見えることです。いくつかのテストを行っているときに、アプリケーション間にいくつかの違いがあり、一般的な解決策を考え出すことはほとんど不可能であることがわかりました.

ここに私の観察があります:

  1. Word 2010 または XnView からアルファ チャネル イメージをクリップボードにコピーすると、ピクセル データを乗算せずに保存されます。

  2. しかし、Firefox または Chrome を使用して画像をコピーすると、ピクセル データがアルファ チャネルで事前に乗算されているように見えます。

  3. Firefox はbV5AlphaMask0xff000000 に設定しますが、他のほとんどのアプリケーションはこれをまったく設定せず、0 のままにします。これらのアプリケーションは、実際には上位 8 ビットにアルファ チャネルを含む DIB をクリップボードに配置しますが、それでもbV5AlphaMask0 に設定するため、これは奇妙です。ビット深度が 32 の場合、bV5AlphaMask0 であってもアルファ チャネルがあると仮定する必要があります。

簡単に言うと、私の基本的な質問は次のとおりです。アルファ チャネル データをクリップボードに保存する方法に関する公式情報はありますか? データを事前に乗算する必要があるかどうかを調べることに特に興味があります。上記のように、Word 2010 と XnView は事前乗算を行いませんが、Firefox と Chrome は事前乗算を行います。ただし、カラー チャネルを事前に乗算する必要があるかどうかを知ることは非常に重要です。

これに光を当ててくれてありがとう!

更新 2 Paint.NET への貼り付けが正常に機能するようになりました。これは私のコードのバグが原因で、アルファ チャンネルが 0 の場合にカラー チャンネルが 0 に設定されませんでした。つまり、この場合、事前乗算が正しく行われず、Paint.NET が混乱したようです。

Internet Explorer 10 の問題はまだ解決されていません。アルファ チャネルを含む PNG をクリップボードにコピーする場合、IE 10 は 24 ビット CF_DIBV5 をクリップボードに配置するだけですが、Paint.NET はこのビットマップをアルファ チャネル付きで貼り付けることができるため、別の形式が必要です。 IE 10 がクリップボードに公開すること。おそらく、CFSTR_FILECONTENTS と CFSTR_FILEDESCRIPTOR を使用する PNG を公開します。

更新 私は現在、以下のarxで説明されている方法で実装しましたが、かなりうまく機能します。しかし、まだ私を困惑させていることが 2 つあります。

1) アルファ チャネル イメージをアプリから Paint.NET に貼り付けても、アルファ チャネルが保持されません。Paint.NET では画像が不透明に表示されます。ただし、Firefox および Chrome から Paint.NET への貼り付けは完全に機能し、アルファ チャネルは保持されます。完全なDIBV5をダンプしましたが、それは私のアプリと同じですが、それでもFFとChromeでは機能しますが、私のアプリでは機能しないため、何か他のものがあるに違いありません! Firefox と Chrome は、私のアプリがしない何か他のことをしているに違いありません!?

2) Internet Explorer 10 にも同じことが当てはまります。IE 10 からアプリにアルファ チャネル画像を貼り付けても、まったく機能しません... ビット深度が 24 の DIB を取得しています。全て。ただし、IE 10 から Paint.NET に貼り付けると、アルファ チャネルが表示されます。だから、ここにも何かがあるに違いない...

4

2 に答える 2

20

CF_DIBV5 にアルファを格納する正しい方法があると確信していますが、それは実際には問題ではありません。アプリケーションはすでにこれを一貫して処理していないため、アプリケーションを他のアプリケーションとうまく連携させたい場合は、CF_DIBV5 を使用できません。

少し前に透明なビットマップのコピーと貼り付けについて調べました。私の目的は、透明なビットマップを Office と GIMP の 2 つのバージョンにうまく貼り付けることでした。考えられるいくつかの形式を調べました。

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 を直接 Explorer フォルダーに貼り付けることもできます。

アップデート

質問の半分しか答えていないことに気付きました。これはコピーには最適ですが、CF_DIBV5 のみをコピーするアプリケーション (Firefox など) から貼り付けたい場合は役に立ちません。

利用可能な場合は「PNG」を使用することをお勧めします。それ以外の場合は、CF_DIBV5 にフォールバックして、事前に乗算されたものとして扱います。これにより、Word 2010 (「PNG」をエクスポート)、Firefox、および Chrome が正しく処理されます。XnView は乗算されていない CF_DIBV5 のみをエクスポートするため、これは正しく機能しません。あなたがもっとうまくやれるかどうかはわかりません。

lscf - クリップボード形式を探索するためのツール

これは、使用可能なクリップボード形式のリストを表示するためのツールのソースです。ファイルに書き込むこともできます。私はそれを呼んだlscf。Visual Studio で win32 コンソール アプリケーションを作成し、このソースを main 関数に貼り付けます。非常に小さなバグが 1 つあります。形式名を間違って入力しても、「不明な形式」エラーが表示されません。

#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;
}
于 2013-03-28T19:56:41.450 に答える