【问题标题】:How to save PngImage from clipboard如何从剪贴板保存 PngImage
【发布时间】:2012-08-28 19:40:55
【问题描述】:

如何将 pngimage 保存到从 Adob​​eFirewoks(Clipboard) 或 Photoshop 复制的文件中而不丢失透明度。

我正在使用 delphi2009。

提前谢谢你。

@TLama 我试过这段代码,但没有透明度。我也不知道我做得对不对。

  png := TPngimage.Create;
  try
    png.LoadFromClipboardFormat(CF_BITMAP,
      Clipboard.GetAsHandle(CF_BITMAP), CF_BITMAP);
    image1.Picture.Assign(png);
  finally
    png.Free;
  end;

【问题讨论】:

  • 您能否运行this code 并告诉我,当您在剪贴板中有透明图像时,消息会显示什么?你能用从你提到的两个应用程序复制的图像来做吗?
  • @TLama,它返回几个 cfFormat,我不知道如何使用 png.LoadFromClipboardFormat();它总是给我不支持的格式。
  • 这是因为 PNG 图像的 LoadFromClipboardFormat 在内部创建了一个位图并尝试从剪贴板加载该位图。并且当您指定CF_BITMAP 以外的格式时,将引发异常。
  • @TLama,我尝试了您告诉我尝试的链接中的 cfFormat 值。
  • 不,我打算运行this code,但我不记得我建议使用CF_BITMAP。无论如何,这在任何情况下都行不通。无法粘贴从 Photoshop 复制的准确、透明的图像,因为 32 位位图(带有 Alpha 通道的格式)中的每个像素由每个像素 4 个字节组成。 Photoshop 仅将其中的 3 个值复制到剪贴板中 - 用于红色、绿色和蓝色通道。它不包括 Alpha 通道的值。

标签: delphi png transparency clipboard


【解决方案1】:

Photoshop 的剪贴板格式很糟糕。包含存储在剪贴板中的 alpha 通道的唯一非常有效的数据是......猜猜? ...指向Alpha通道内存的指针到“Photoshop粘贴到位”块中......太可怕了。如果您复制某些内容然后重新启动 Photoshop,则 alpha 将...丢失 :)

但是,如果剪贴板中包含 Photoshop 图像,您可以轻松了解。

询问剪贴板它有哪些块。

如果剪贴板有两个块,分别命名为 “Photoshop 粘贴到位”“对象描述符”,您可以确定 99.9% Photoshop IS RUNNING 在系统上且剪贴板包含对 Photoshop 数据的reference。 (当 Photoshop 退出时,对象描述符块会从剪贴板中删除,因此 alpha 将永远丢失)

那么,你有两个选择:

选择 1(不推荐):打开 Photoshop 的进程内存并从指针中读取原始 32 位图像数据...这样做总体上是愚蠢的并且不安全

选择 2(推荐):使用 COM 从 Photoshop 中提取图像数据。当然,COM 方法是最好的方法。让您的程序生成并运行以下 VBS 脚本:

On Error Resume Next
Set Ps = CreateObject("Photoshop.Application")
Set Shell = CreateObject("WScript.Shell")
Set FileSystem = CreateObject("Scripting.FileSystemObject") 

Dim PNGFileName
PNGFileName = Shell.CurrentDirectory & "\psClipboard.png"

If FileSystem.FileExists(PNGFileName) Then 
    FileSystem.DeleteFile PNGFileName
End If

Set Doc = Ps.Documents.Add(1,1,72,"psClipboard",,3)

Doc.Paste()
Doc.RevealAll()

If Err.Number = 0 Then 
    set PNGSaveOptions = CreateObject("Photoshop.PNGSaveOptions")
    doc.saveAs PNGFileName, PNGSaveOptions
End If

doc.Close()

在脚本的 CurrentDirectory 中,将生成一个名为“psClipboard.png”的文件。使用 libPng 或其他方式在程序中读取此文件,并将其视为来自剪贴板。该脚本将删除 psClipboard.png,然后向 Photoshop 请求它。如果粘贴返回错误,脚本将停止并且不会生成文件,在这种情况下,剪贴板不包含有效的 Photoshop 参考数据。

【讨论】:

  • 你能解释一下,exatcly chunk 包含透明图像吗?是“Photoshop 粘贴到位”吗?不幸的是,我不懂基本代码(对不起我的英语不好)
  • 不幸的是,没有块包含任何 Alpha 通道。仅处理当前运行的 Photoshop 进程中的某些内容,该进程包含具有透明度的完整图像。您可以使用上面的 VBS 获取图像,前提是剪贴板中有“Photoshop Paste In Place”和“Object Descriptor”块,
【解决方案2】:

根据我的同事使用以下测试代码使用 Adob​​e Photoshop CS 6 13.0 x32 确认的经验结果,指出无法保存 Adob​​e Photoshop 复制的剪贴板中的图像而不会丢失透明度,因为它不复制Alpha 通道数据。

Adobe Photoshop(至少在上述版本中)使用 24 位像素格式进行剪贴板图像数据传输。而且,因为它是 24 位位图,所以不能有 Alpha 通道。不知道有谁需要验证 Adob​​e Fireworks,但可以肯定的是,他们使用自己注册的剪贴板格式在其产品之间传输图像,包括 alpha 通道。

Adobe Photoshop 剪贴板使用的 CF_BITMAPCF_DIB 格式据说支持 alpha 通道,正如一些人所说(我没有尝试过),但这仅适用于 32 位像素格式,不适用于 24位像素格式。唯一支持透明度的剪贴板格式是CF_DIBV5,但与其他格式一样,图像必须以 32 位像素格式存储以保留 Alpha 通道:

以下代码显示了当前复制的剪贴板内容的信息:

uses
  ActiveX;

function GetClipboardFormatString(Format: Word): string;
var
  S: string;
begin
  case Format of
    1: S := 'CF_TEXT';
    2: S := 'CF_BITMAP';
    3: S := 'CF_METAFILEPICT';
    4: S := 'CF_SYLK';
    5: S := 'CF_DIF';
    6: S := 'CF_TIFF';
    7: S := 'CF_OEMTEXT';
    8: S := 'CF_DIB';
    9: S := 'CF_PALETTE';
    10: S := 'CF_PENDATA';
    11: S := 'CF_RIFF';        
    12: S := 'CF_WAVE';
    13: S := 'CF_UNICODETEXT';
    14: S := 'CF_ENHMETAFILE';
    15: S := 'CF_HDROP';
    16: S := 'CF_LOCALE';
    17: S := 'CF_DIBV5';
    $0080: S := 'CF_OWNERDISPLAY';
    $0081: S := 'CF_DSPTEXT';
    $0082: S := 'CF_DSPBITMAP';
    $0083: S := 'CF_DSPMETAFILEPICT';
    $008E: S := 'CF_DSPENHMETAFILE';
    $0200: S := 'CF_PRIVATEFIRST';
    $02FF: S := 'CF_PRIVATELAST';    
    $0300: S := 'CF_GDIOBJFIRST';
    $03FF: S := 'CF_GDIOBJLAST';
  else
    begin      
      SetLength(S, 255);
      SetLength(S, GetClipboardFormatName(Format, PChar(S), 255));      
      if Length(S) = 0 then
        S := 'Unknown, unregistered clipboard format';
      Result := S + ' (' + IntToStr(Format) + ')';
      Exit;
    end;
  end; 
  Result := 'Standard clipboard format (' + S + ')';
end;

function GetClipboardFormats: string;
var
  S: string;
  FormatEtc: TFormatEtc;
  DataObject: IDataObject;
  EnumFormatEtc: IEnumFormatEtc;
begin
  Result := '';
  if Succeeded(OleGetClipboard(DataObject)) then
  begin
    if Succeeded(DataObject.EnumFormatEtc(DATADIR_GET, EnumFormatEtc)) then
    begin
      S := DupeString('-', 65) + sLineBreak +
        'Clipboard data formats: ' + sLineBreak +
        DupeString('-', 65) + sLineBreak;
      while EnumFormatEtc.Next(1, FormatEtc, nil) = S_OK do
        S := S + GetClipboardFormatString(FormatEtc.cfFormat) + sLineBreak;
      Result := S;
    end;
  end;
end;

function GetClipboardInfoDIB: string;
var
  S: string;
  ClipboardData: HGLOBAL;
  BitmapInfoHeader: PBitmapInfoHeader;
const
  BI_JPEG = 4;
  BI_PNG = 5;
begin
  Result := '';
  if OpenClipboard(0) then
  try
    ClipboardData := GetClipboardData(CF_DIB);
    if ClipboardData <> 0 then
    begin
      BitmapInfoHeader := GlobalLock(ClipboardData);
      if Assigned(BitmapInfoHeader) then
      try
        S := DupeString('-', 65) + sLineBreak +
          'Clipboard data of CF_DIB format: ' + sLineBreak +
          DupeString('-', 65) + sLineBreak +
          'Width: ' + IntToStr(BitmapInfoHeader.biWidth) + ' px' + sLineBreak +
          'Height: ' + IntToStr(BitmapInfoHeader.biHeight) + ' px' + sLineBreak +
          'Bit depth: ' + IntToStr(BitmapInfoHeader.biBitCount) + ' bpp' + sLineBreak +
          'Compression format: ';
        case BitmapInfoHeader.biCompression of
          BI_RGB:   S := S + 'Uncompressed format (BI_RGB)';
          BI_RLE8: S := S + 'RLE format for bitmaps with 8 bpp (BI_RLE8)';
          BI_RLE4: S := S + 'RLE format for bitmaps with 4 bpp (BI_RLE4)';
          BI_BITFIELDS: S := S + 'Not compressed with color masks (BI_BITFIELDS)';
          BI_JPEG: S := S + 'Compressed using JPEG file format (BI_JPEG)';
          BI_PNG:   S := S + 'Compressed using PNG file format (BI_PNG)';
        end;
        S := S + sLineBreak;
        Result := S;
      finally
        GlobalUnlock(ClipboardData);
      end;      
    end;
  finally
    CloseClipboard;
  end;
end;

function GetClipboardInfoDIBV5: string;
var
  S: string;
  ClipboardData: HGLOBAL;
  BitmapInfoHeader: PBitmapV5Header;
const
  BI_JPEG = 4;
  BI_PNG = 5;
begin
  Result := '';
  if OpenClipboard(0) then
  try
    ClipboardData := GetClipboardData(CF_DIBV5);
    if ClipboardData <> 0 then
    begin
      BitmapInfoHeader := GlobalLock(ClipboardData);
      if Assigned(BitmapInfoHeader) then
      try
        S := DupeString('-', 65) + sLineBreak +
          'Clipboard data of CF_DIBV5 format: ' + sLineBreak +
          DupeString('-', 65) + sLineBreak +
          'Width: ' + IntToStr(BitmapInfoHeader.bV5Width) + ' px' + sLineBreak +
          'Height: ' + IntToStr(BitmapInfoHeader.bV5Height) + ' px' + sLineBreak +
          'Bit depth: ' + IntToStr(BitmapInfoHeader.bV5BitCount) + ' bpp' + sLineBreak +
          'Compression format: ';
        case BitmapInfoHeader.bV5Compression of
          BI_RGB:   S := S + 'Uncompressed format (BI_RGB)';
          BI_RLE8: S := S + 'RLE format for bitmaps with 8 bpp (BI_RLE8)';
          BI_RLE4: S := S + 'RLE format for bitmaps with 4 bpp (BI_RLE4)';
          BI_BITFIELDS: S := S + 'Not compressed with color masks (BI_BITFIELDS)';
          BI_JPEG: S := S + 'Compressed using JPEG file format (BI_JPEG)';
          BI_PNG:   S := S + 'Compressed using PNG file format (BI_PNG)';
        end;
        S := S + sLineBreak;
        Result := S;
      finally
        GlobalUnlock(ClipboardData);
      end;      
    end;
  finally
    CloseClipboard;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  S: string;
begin
  S := GetClipboardFormats;
  if IsClipboardFormatAvailable(CF_DIB) then
    S := S + sLineBreak + GetClipboardInfoDIB;
  if IsClipboardFormatAvailable(CF_DIBV5) then
    S := S + sLineBreak + GetClipboardInfoDIBV5;
  ShowMessage(S);
end;

Adobe Photoshop CS 6 13.0 将上述透明图像复制到剪贴板的代码输出(点击放大):

一些有用的阅读:

【讨论】:

  • 事实上,它确实将 REFERENCE 导出到 FULL 32-but 图像。请参阅这两个块 - “Photoshop Paste In Place” - 包含指向图像数据的原始 32 位指针和“对象描述符”。如果这两个块都存在,剪贴板包含一个 Photoshop 图像并且 Photoshop 正在运行!请参阅下面的答案,如何使用 COM / VBS 提取具有 alpha 通道的真实图像:P
【解决方案3】:

explained in this link 的解决方案可能会起作用。

unit EG_ClipboardBitmap32;
{
  Author William Egge. egge@eggcentric.com
  January 17, 2002
  Compiles with ver 1.2 patch #1 of Graphics32

  This unit will copy and paste Bitmap32 pixels to the clipboard and retain the
  alpha channel.

  The clipboard data will still work with regular paint programs because this
  unit adds a new format only for the alpha channel and is kept seperate from
  the regular bitmap storage.
}

interface

uses
  ClipBrd, Windows, SysUtils, GR32;

procedure CopyBitmap32ToClipboard(const Source: TBitmap32);
procedure PasteBitmap32FromClipboard(const Dest: TBitmap32);
function CanPasteBitmap32: Boolean;

implementation

const
  RegisterName = 'G32 Bitmap32 Alpha Channel';
  GlobalUnlockBugErrorCode = ERROR_INVALID_PARAMETER;

var
  FAlphaFormatHandle: Word = 0;

procedure RaiseSysError;
var
  ErrCode: LongWord;
begin
  ErrCode := GetLastError();
  if ErrCode <> NO_ERROR then
    raise Exception.Create(SysErrorMessage(ErrCode));
end;

function GetAlphaFormatHandle: Word;
begin
  if FAlphaFormatHandle = 0 then
  begin
    FAlphaFormatHandle := RegisterClipboardFormat(RegisterName);
    if FAlphaFormatHandle = 0 then
      RaiseSysError;
  end;
  Result := FAlphaFormatHandle;
end;

function CanPasteBitmap32: Boolean;
begin
  Result := Clipboard.HasFormat(CF_BITMAP);
end;

procedure CopyBitmap32ToClipboard(const Source: TBitmap32);
var
  H: HGLOBAL;
  Bytes: LongWord;
  P, Alpha: PByte;
  I: Integer;
begin
  Clipboard.Assign(Source);
  if not OpenClipboard(0) then
    RaiseSysError
  else
    try
      Bytes := 4 + (Source.Width * Source.Height);
      H := GlobalAlloc(GMEM_MOVEABLE and GMEM_DDESHARE, Bytes);
      if H = 0 then
        RaiseSysError;
      P := GlobalLock(H);
      if P = nil then
        RaiseSysError
      else
        try
          PLongWord(P)^ := Bytes - 4;
          Inc(P, 4);
          // Copy Alpha into Array
          Alpha := Pointer(Source.Bits);
          Inc(Alpha, 3); // Align with Alpha
          for I := 1 to (Source.Width * Source.Height) do
          begin
            P^ := Alpha^;
            Inc(Alpha, 4);
            Inc(P);
          end;
        finally
          if (not GlobalUnlock(H)) then
            if (GetLastError() <> GlobalUnlockBugErrorCode) then
              RaiseSysError;
        end;
      SetClipboardData(GetAlphaFormatHandle, H);
    finally
      if not CloseClipboard then
        RaiseSysError;
    end;
end;

procedure PasteBitmap32FromClipboard(const Dest: TBitmap32);
var
  H: HGLOBAL;
  ClipAlpha, Alpha: PByte;
  I, Count, PixelCount: LongWord;
begin
  if Clipboard.HasFormat(CF_BITMAP) then
  begin
    Dest.BeginUpdate;
    try
      Dest.Assign(Clipboard);
      if not OpenClipboard(0) then
        RaiseSysError
      else
        try
          H := GetClipboardData(GetAlphaFormatHandle);
          if H <> 0 then
          begin
            ClipAlpha := GlobalLock(H);
            if ClipAlpha = nil then
              RaiseSysError
            else
              try
                Alpha := Pointer(Dest.Bits);
                Inc(Alpha, 3); // Align with Alpha
                Count := PLongWord(ClipAlpha)^;
                Inc(ClipAlpha, 4);
                PixelCount := Dest.Width * Dest.Height;
                Assert(Count = PixelCount,
                  'Alpha Count does not match Bitmap pixel Count,
                  PasteBitmap32FromClipboard(const Dest: TBitmap32);');

                // Should not happen, but if it does then this is a safety catch.
                if Count > PixelCount then
                  Count := PixelCount;

                for I := 1 to Count do
                begin
                  Alpha^ := ClipAlpha^;
                  Inc(Alpha, 4);
                  Inc(ClipAlpha);
                end;
              finally
                if (not GlobalUnlock(H)) then
                  if (GetLastError() <> GlobalUnlockBugErrorCode) then
                    RaiseSysError;
              end;
          end;
        finally
          if not CloseClipboard then
            RaiseSysError;
        end;
    finally
      Dest.EndUpdate;
      Dest.Changed;
    end;
  end;
end;

end.

函数PasteBitmap32FromClipboard 显然是你需要的。将位图保存为 PNG 是 answered in this question

【讨论】:

  • 这似乎只是用于使用自定义剪贴板格式在Graphics32 库位图之间复制和粘贴。
  • @TLama - 确实如此。这是一个 LMGTFY 答案。
  • @Leonardo, :-) 但无论如何,唯一支持 alpha 的格式似乎是 CF_DIBV5,但我还不知道问题中提到的应用程序是否将它用于复制到剪贴板。他们可能会使用带有 Alpha 通道支持的自定义剪贴板格式,这很难说。这就是为什么我要求运行测试代码以确定支持的格式。
  • 如果它不使用graphic32就好了,因为我只需要剪贴板功能。还有其他消化吗?
  • @XBasic3000,没有。我的朋友试图在 Photoshop 和 GIMP 之间复制和粘贴透明图像,但图像失去了透明度。但是无论如何,您能否从我对您问题的评论中运行 pastebin 中的代码,并让我知道您得到了什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 1970-01-01
  • 1970-01-01
  • 2023-02-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多