|
Delphi Programming / Object Pascal
[掲載 2022年03月22日] [更新 2022年04月11日] Delphi サンプルプログラム集 |
824_ビットマップ画像と PNG 画像のコピー / 貼り付け |
動作確認等 |
Windows 10 ビルド 19042 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32 |
|
824_BmpPngClipboard.zip [10,9 MB] 2022年04月11日版 (EXE 同梱) |
ビットマップ画像と PNG 画像のクリップボードへのコピー / 貼り付け |
Delphi では次のようなコードでビットマップをクリップボードにコピーしたり,クリップボードから貼り付けることができます.この時,クリップボードには,CR_BITAMP 形式と CF_DIB 形式のピッマップデータが格納されます.CF_BITMAP は,デバイス依存のビットマップ (DDB) です.64 ビットのカラーを扱うビデオボードもありますが,現在の Windows 10 の環境では,CF_BITMAP は,32 ビットのカラーと考えて差支えないでしょう.
CF_DIB はデバイス非依存のビットマップ (DIB) で,TBitmap のテータそのものです.
|
Clipboard.Assign(TBitmap);
TBitmap.Assign(Clipboard);
アルファチャネルの処理が必要なく,ビット深さの情報も必要がなければ,以下のコードで十分です.
TBitmap..Assign(Clipboard) の TBitmap は CF_BITMAP 形式のデータです.
このコードと同じ手順で,PNG 画像,GIF 画像,JPEG 画像もクリップボードにコピーできます.その場合,PNG 画像,JPEG 画像は,CF_BITMAP 形式のビットマップとしてクリップボードから取得できます.GIF 画像は,CF_GIF形式の画像として取得できます. |
リスト1
Clipboard.Assign と Assign(Clipboard) を使用するビットマップのコピーと貼り付け |
implementation
uses
Vcl.Clipbrd;
{$R *.dfm}
//=============================================================================
// ビットマップ画像をクリップボードにコピー
// Clipboard.Assign メソッドを使用
//
// Images.bmp は 32 bit のアルファチャンネル付きのビットマップ画像ファイル
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBitmap : TBitmap;
begin
Image1.Picture.Assign(nil);
LBitmap := TBitmap.Create;
try
LBitmap.LoadFromFile(ExpandFileName('.\Images.bmp'));
LBitmap.AlphaFormat := afDefined;
Image1.Canvas.Draw(0, 0, LBitmap);
Clipboard.Assign(LBitmap);
finally
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// クリップボードからヒットマップを取得
// TBitmap.Assign メソッドを使用
//
// 透明色のピクセルは黒になる
// 取得したビットマップのビット深さは常に 32 ビット
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LBitmap : TBitmap;
begin
Image1.Picture.Assign(nil);
// CF_BITMAP のデータがある場合に処理
if Clipboard.HasFormat(CF_BITMAP) then begin
LBitmap := TBitmap.Create;
try
LBitmap.Assign(Clipboard);
Image1.Canvas.Draw(0, 0, LBitmap);
finally
FreeAndNil(LBitmap);
end;
end;
end;
end.
Assign(Clipboard) で,クリップボードから TBitmap のデータを取得した後,HandleType の値を bmDIB にすると,アルファチャネル付き画像として取得できます. |
//=============================================================================
// クリップボードからヒットマップを取得
// TBitmap.Assign メソッドで取得して HandleType を bmDIB にする
//
// アルファチャンネルを使用しないビットマップの場合
// AlphaFormat := afDefined を削除する
// 代わりに Transparent := True にすれば透過表示になる
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LBitmap : TBitmap;
begin
Image1.Picture.Assign(nil);
// CF_BITMAP のデータがある場合に処理
if Clipboard.HasFormat(CF_BITMAP) then begin
LBitmap := TBitmap.Create;
try
LBitmap.Assign(Clipboard);
LBitmap.HandleType := bmDIB;
LBitmap.AlphaFormat := afDefined;
Image1.Canvas.Draw(0, 0, LBitmap);
finally
FreeAndNil(LBitmap);
end;
end;
end;
[備考]
DDB (デバイス依存ビットマップ - Device Dependent Bitma) は,Windows のビデオドライバが直接扱うことのできるビットマップです.その内部構造は,ビデオドライバやビデオモードによって異なります。ビットマップのピクセルをプログラムから直接佐操作することはできません.
DIB (デバイス非依存ビットマップ - Device Independent Bitmap) は,ヘッダ部を含めたデータ構造が定義されたビットマップ形式です.
DDB を DIB に変換するには,HandleType プロパティの値を bmDIB にします.PixelFormat プロパティを設定したり,メモリストリームに保存等の操作をすると市自動的に DIB になります.
Delphi の TBitmap は,基本的に DIB を扱います. |
ビットマップファイルヘッダにも,ビットマップインフォヘッダにも,そのビットマップがアルファチャンネルを使用しててるかという情報はありません.しかし,ピット深さが 32 ビット以上であるという情報は必要です.また,ビット深さに応じた処理が必要な場合もビット深さの値が必要です.
ビット深ささは,以下のように,ビットマップインフォヘッダ (ビットマップ情報ヘッダ) のメンバからも取得できます.
ビットマップファイルヘッダの部分は 40 バイトの固定ですが,ビットマップファイルインフォヘッダ部はいくつか種類あります.ビットマップのサイズとビット深さを取得するだけであれば,TBitmapInfoHeader と仮定して取得すれば十分です. |
|
図1
実行結果 |
リスト2
ビットマップのサイズとビット深さのチェック |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Edit1: TEdit;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Edit1.Text := 'TplChartBar32.bmp';
end;
//=============================================================================
// ビットマップ画像のサイズとビット深さをチェックする例
//
// 主なビットマップインフォヘッダには以下の種類がある
// 幅,高さ,ビット深さしか取得する必要がないので TBitmapInfoHeader だけで十分
// TBitmapInfoHeader
// TBitmapV4Header
// TBitmapV5Header
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LBmpFilePath : string;
LsrcStream : TMemoryStream;
LHeadStr : array[0..1] of AnsiChar;
LBmpInfoHeader : TBitmapInfoHeader;
LBmpWidth : Integer;
LBmpHeight : Integer;
LBmpCount : Integer;
begin
Memo1.Lines.Clear;
LBmpFilePath := ExpandFileName(Trim(Edit1.Text));
LsrcStream := TMemoryStream.Create;
try
LsrcStream.LoadFromFile(LBmpFilePath);
// ビットマップかどうかのチェック
LsrcStream.Read(LHeadStr, 2);
if LHeadStr <> 'BM' then exit;
LsrcStream.Seek(SizeOf(TBitmapFileHeader), soBeginning);
LsrcStream.ReadBuffer(LBmpInfoHeader, SizeOf(TBitmapInfoHeader));
// ビットマップのサイズとビット深さを取得
LBmpWidth := LBmpInfoHeader.biWidth;
LBmpHeight := LBmpInfoHeader.biHeight;
LBmpCount := LBmpInfoHeader.biBitCount;
Memo1.Lines.Add('biWidth : ' + IntToStr(LBmpWidth));
Memo1.Lines.Add('biHeight : ' + IntToStr(LBmpHeight));
Memo1.Lines.Add('bitCount : ' + IntToStr(LBmpCount));
finally
FreeAndNil(LsrcStream);
end;
end;
end.
02_ビット深さを指定したクリップボードへのコピー |
CF_DIB にはデバイス非依存のビットマップのデータをセットすることができます.そうすると,クリップボードからデータを取得する際,CF_DIB のデータを取得すればデバイスに依存しないビットマップのデータを取得することが可能になります.以下のコードはその例です.
このサンプルでは,指定したビット深さの画像を読み込んでクリップボードにコピーします.そして,そのクリップボードのビットマップデータを取得してディスクに保存します.コピーした時と同じビット深さのビットマップがクリップボードから取得できることが確認できます.
保存したビットマップ画像のビット深さはファイルのプロパティのダイアログでも確認できます.
このサンプルコードでは,32 ビットのアルファチャンネル付きの画像は,アルファ値を保持したままでコピーできます.受け取る側でも,CF_DIB のデータを取得すればアルファチャンネル付きで取得できます.
本サンプルで使用している添付のビットマップ画像は,4 ビット (16 色) の画像から作成しています.したがって, 基本的にビット数による画像の違いはありません. |
|
図2
実行時の画面
- ラジオボタンを選択
- [Copy] ボタンをクリック
- [Paste] ボタンをクリック
|
|
図3
[ファイルのプロパティ] ダイアログで確認
|
リスト3
ビット深さを指定したビットマップのクリップボードへのコピーと貼り付け |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Image1: TImage;
RadioGroup1: TRadioGroup;
ListBox1: TListBox;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
function AddBmpFileHeader(AStream: TMemoryStream): Boolean;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
RadioGroup1.ItemIndex := 0;
end;
//=============================================================================
// クリップボードにビットマップのデータを DIB としてコピー
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LLoadFilePath : string;
LStream : TMemoryStream;
LBitmap : TBitmap;
LFormatID : WORD;
LData : THandle;
LPalette : HPALETTE;
begin
Image1.Picture.Assign(nil);
ListBox1.Items.Clear;
Clipboard.Open;
Clipboard.Clear;
LLoadFilePath := 'Images\' + Trim(RadioGroup1.Items[RadioGroup1.ItemIndex]);
LStream := TMemoryStream.Create;
LBitmap := TBitmap.Create;
try
// ビットマップ画像ファイルをメモリストリームに読み込む
LStream.LoadFromFile(LLoadFilePath);
LStream.Position := 0;
LBitmap.LoadFromStream(LStream);
// それをクリップボードにセット
SetdataToClipboard(CF_DIB, LStream);
// CF_BITMAP 形式のビットマップを取得できるようにする
// LFormatID の値は常に CF_BITMAP (VCL のソースコード参照)
LBitmap.SaveToClipboardFormat(LFormatID, LData, LPalette);
SetClipboardData(LFormatID, LData);
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// クリップボードからヒットマップのデータを DIB として取得
// つまり[貼り付け]の処理
// それを保存.保存した画像を読み込んで TImage に描画
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LSaveFilePath : string;
LBmpStream : TMemoryStream;
LBmpInfoHeader : TBitmapInfoHeader;
LBitmap : TBitmap;
begin
Image1.Picture.Assign(nil);
LSaveFilePath := 'Images\DIB_' + Trim(RadioGroup1.Items[RadioGroup1.ItemIndex]);
// CF_DIB のデータがある場合に処理
if Clipboard.HasFormat(CF_DIB) then begin
LBitmap := TBitmap.Create;
LBmpStream := TMemoryStream.Create;
try
// クリップボードから CF_DIB のデータを取得
GetDataFromClipboard(CF_DIB, LBmpStream);
// クリップボードのデータにファイルヘッダがなければ追加
AddbmpFileHeader(LBmpStream);
// メモリストリームを保存
LBmpStream.SaveToFile(LSaveFilePath);
// 保存したファイルからビットマップ画像を読み込んで表示
LBitmap.LoadFromFile(LSaveFilePath);
LBitmap.Transparent := True;
Image1.Canvas.Draw(0, 0, LBitmap);
// ビットマップインフォヘッダ部分を読み出す
LBmpStream.Position := SizeOf(TBitmapFileHeader);
LBmpStream.Read(LBmpInfoHeader, SizeOf(TBitmapInfoHeader));
// ビットマップのサイズとビット深さを表示
ListBox1.Items.Clear;
ListBox1.Items.Add('Width : ' + IntToStr(LBmpInfoHeader.biWidth));
ListBox1.Items.Add('Height : ' + IntToStr(LBmpInfoHeader.biHeight));
ListBox1.Items.Add('Colors : ' + IntToStr(LBmpInfoHeader.biBitCount) + ' bit');
finally
Clipboard.Close;
FreeAndNil(LBmpStream);
FreeAndNil(LBitmap);
end;
end;
end;
//-----------------------------------------------------------------------------
// クリップボードへメモリストリームのデータを,指定形式でセット
// SetClipboardData 関数を使用
//-----------------------------------------------------------------------------
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
// クリップボードのデータ格納領域を確保してそのポインタを取得
LhGlobale := GlobalAlloc(GHND, AStream.Size);
LpBuffer := GlobalLock(LhGlobale);
try
AStream.Position := 0;
AStream.ReadBuffer(LpBuffer^, AStream.Size);
// クリップボードへセット
SetClipboardData(AFmtID, LhGlobale);
finally
GlobalUnlock(LhGlobale);
end;
end;
//-----------------------------------------------------------------------------
// クリップボードから,指定形式のデータをメモリストリームに格納
//-----------------------------------------------------------------------------
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
AStream.Clear;
// クリップボードのデータのポインタを取得
LhGlobale := Clipboard.GetAsHandle(AFmtID);
LpBuffer := GlobalLock(LhGlobale);
try
// メモリストリームにクリップボードのデータを書き込む
AStream.WriteBuffer(LpBuffer^, GlobalSize(LhGlobale));
AStream.Position := 0;
finally
GlobalUnlock(LhGlobale);
end;
end;
//-----------------------------------------------------------------------------
// メモリストリームにビットマップファイルヘッダの情報を追加
// ウェブブラウザの画像をコピーした場合等はファイルヘッダのデータがない
//-----------------------------------------------------------------------------
function AddBmpFileHeader(AStream: TMemoryStream): Boolean;
var
LSigChar : array[0..1] of AnsiChar;
LFileHeader : TBitmapFileHeader;
LStream : TMemoryStream;
begin
Result := True;
AStream.Position := 0;
AStream.Read(LSigChar, SizeOf(LSigChar));
AStream.Position := 0;
if LSigChar = 'BM' then Exit;
Result := False;
// BM がない場合はファイルヘッダがないので追加する
LFileHeader.bfType := $4D42;
LFileHeader.bfSize := AStream.Size;
LFileHeader.bfReserved1 := 0;
LFileHeader.bfReserved2 := 0;
LFileHeader.bfOffBits := SizeOf(TBitmapFileHeader);
// クリップボードのデータの前にビットマップファイルヘッダ部を追加
LStream := TMemoryStream.Create;
try
LStream.Write(LFileHeader, SizeOf(TBitmapFileHeader));
LStream.Write(AStream.Memory, AStream.Size);
LStream.Position := 0;
AStream.Clear;
LStream.SaveToStream(AStream);
AStream.Position := 0;
finally
FreeAndNil(LStream);
end;
end;
end.
03_フォーマット形式を定義して PNG 画像をクリップボードにコピー |
Windows の,定義済みのクリップボードのフォーマット形式には,PNG 形式はありません.独自に形式を定義して処理することになります.
ワードやエクセルではフォーマット名に PNG が使われているので,本サンプルもそうしています.他のアプリケーションでは,このフォーマット名で検出しないと,PNG 画像としては取得できません. |
|
図4
[Copy] ボタンをクリック
- 画像は添付の PNG 画像
- この画像をコピーする
- ワードまたはエクセルに PNG 画像として貼り付けできる
|
|
図5
[Paste] ボタンをクリック
- クリップボードから PNG 画像だけを取得
- ワードやエクセルの PNG 画像も,PNG 画像として取得できる
|
サンプルの EXE を実行してボタンをクリックするとクリップボードに,メモリストリームに格納した PNG 画像のデータをセットするようになっています.
以下の参考リンクのサンプルプログラムを使用して,クリップボードのフォーマット形式を確認したのが下図です.PNG というフォーマット名が確認できます. |
|
図6
クリップボード内のフォーマット形式を確認 |
リスト4
PNG 形式の画像をクリップボードにコピー / 貼り付け |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
FFilePath : string;
public
{ Public 宣言 }
end;
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd,
Vcl.Imaging.pngimage;
{$R *.dfm}
const
// これはワードやエクセルが使用しているフォーマット名
FPngFormatdStr = 'PNG';
var
FPngFormatID : Cardinal;
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FFilePath := ExpandFileName('.\Images.png');
// フォーマット名からフォーマットの ID 値を取得 (値は Windows が決定する)
FPngFormatID := RegisterClipboardFormat(PChar(FPngFormatdStr));
end;
//=============================================================================
// クリップボードに PNG 形式で画像をコピー
// Windows の標準のクリップボードのフォーマットには PNG 形式はない
// 独自のフォーマット名を定義して実行する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStream : TMemoryStream;
LPngImg : TPngImage;
begin
Image1.Picture.Assign(nil);
Clipboard.Open;
Clipboard.Clear;
LStream := TMemoryStream.Create;
LPngImg := TPngImage.Create;
try
// Png 画像のデータをメモリストリームに格納
LPngImg.LoadFromFile(FFilePath);
LPngImg.SaveToStream(LStream);
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0, 0, LPngImg);
// クリップボードに格納
SetDataToClipboard(FPngFormatID, LStream);
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LPngImg);
end;
end;
//=============================================================================
// クリップボードから PNG 画像のデータを取得
// つまり[貼り付け]の処理
// クリップボードのデータをメモリストリームに格納して TPngImage に読み込む
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LPngImg : TPngImage;
LStream : TMemoryStream;
begin
Image1.Picture.Assign(nil);
// 指定フォーマットのデータがある場合に処理
if Clipboard.HasFormat(FPngFormatID) then begin
LPngImg := TPngImage.Create;
LStream := TMemoryStream.Create;
try
// クリップボードから画像データを取得
GetDataFromClipboard(FPngFormatID, LStream);
// メモリストリームから画像データを読み込む
LPngImg.LoadFromStream(LStream);
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0, 0, LPngImg);
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LPngImg);
end;
end;
end;
//-----------------------------------------------------------------------------
// クリップボードへメモリストリームのデータを,指定形式でセット
// SetClipboardData 関数を使用
//-----------------------------------------------------------------------------
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
// クリップボードのデータ格納領域を確保してそのポインタを取得
LhGlobale := GlobalAlloc(GHND, AStream.Size);
LpBuffer := GlobalLock(LhGlobale);
try
AStream.Position := 0;
AStream.ReadBuffer(LpBuffer^, AStream.Size);
// クリップボードへセット
SetClipboardData(AFmtID, LhGlobale);
finally
GlobalUnlock(LhGlobale);
end;
end;
//-----------------------------------------------------------------------------
// クリップボードから,指定形式のデータをメモリストリームに格納
//-----------------------------------------------------------------------------
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
AStream.Clear;
// クリップボードのデータのポインタを取得
LhGlobale := Clipboard.GetAsHandle(AFmtID);
LpBuffer := GlobalLock(LhGlobale);
try
// メモリストリームにクリップボードのデータを書き込む
AStream.WriteBuffer(LpBuffer^, GlobalSize(LhGlobale));
AStream.Position := 0;
finally
GlobalUnlock(LhGlobale);
end;
end;
end.
04_PNG 画像とビットマップ画像の両方をクリップボードへコピー |
前項のサンプルでは,PNG 形式の画像だけしかクリップボードにセットしません.
画像関係の多くのアプリケーションでは,クリップボードの CF_BITMAP, CF_DIB 形式からビットマップ画像のデータを取得しますから,これでは不便です.そこで,ビットマップ画像のデータもクリップボードにセットするようにしたのが本サンプルです.
複数の形式のデータをクリップボードへのコピーするには,Clipboard.Assign や Clipboard.SetAsHandle ではなく,SetClipboardData 関数を使用します.
Clipboard.Close は,SetClipboardData 関数で全ての形式のデータをセットしてから実行します.
クリップボードへのコピーと貼り付けの部分に,既に作成した自前の関数を使用しています. |
//クリップボードへセット
Clipboard.SetAsHandle(FPngFormatID, LhGlobale);
↓
SetClipboardData(FPngFormatID, LhGlobale);
// Close は全ての形式のデータを SetClipboardData した後に実行する
Clipboard.Close;
|
図7
[Button1] クリックでコピー
- この PNG 画像をコピーする
- ワードやエクセルに PNG 画像として貼り付けできる
|
|
図8
[Button2] クリックで貼り付け
- [Button1] クリック直後であれば PNG 画像の取得
- ワードやエクセルの PNG 画像も,PNG 画像として取得できる
|
このサンプルでは,クリップボードから CF_DIB のデータも取得するようにしています.下図は,ウェブブラウザで表示している画像を取得した例です.
Clipboard.Assign(TBitma) でコピーしたアルファチャンネル付きのビットマップ画像も,アルファチャンネル付きのビットマップとして取得できます. |
|
図9
CF_DIB 形式のデータを取得
- ウェブブラウザで表示中の画像の例
- TBitmap.AlphaFormat := afDefined としている場合
|
前項のサンプルの時と同じように,クリップボードのフォーマット形式を確認したのが下図です.PNG というフォーマット名に続いて CF_DIB 等のビットマップ形式名が確認できます.
クリップボードからPNG 画像を取得するには,前項のサンプルと同じコードを使用しています. |
|
図10
クリップボード内のフォーマット形式を確認
- PNG 形式とビットマップ形式の両方がある
- 順番はクリップボードにセットした順
|
リスト5
複数のフォーマット形式のデータをクリップボードにコピーする例 |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Image1: TImage;
Button2: TButton;
CheckBox1: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
FFilePath : string;
public
{ Public 宣言 }
end;
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
function AddBmpFileHeader(AStream: TMemoryStream): Boolean;
function BmpCheckUsedAlpha(ABitmap: TBitmap): Boolean;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd,
Vcl.Imaging.pngimage;
{$R *.dfm}
const
// これはワードやエクセルが使用しているフォーマット名
FPngFormatdStr = 'PNG';
var
FPngFormatID : Cardinal;
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FFilePath := ExpandFileName('.\Images.png');
FPngFormatID := RegisterClipboardFormat(PChar(FPngFormatdStr));
end;
//=============================================================================
// PNG 画像とビットマップ画像をクリップボードにコピー
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStream : TMemoryStream;
LPngimg : TPngImage;
LBitmap : TBitmap;
LFormatID : WORD;
LData : THandle;
LPalette : HPALETTE;
begin
Image1.Picture.Assign(nil);
Clipboard.Open;
Clipboard.Clear;
LStream := TMemoryStream.Create;
LPngimg := TPngImage.Create;
LBitmap := TBitmap.Create;
try
LPngimg.LoadFromFile(FFilePath);
// 透明ピクセルの確認のため TImage.Canvas を塗り潰してから描画
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0, 0, LPngimg);
// Png 画像のデータをメモリストリームに格納
LPngimg.LoadFromFile(FFilePath);
LPngimg.SaveToStream(LStream);
// TPngImage をクリップボードにコピー
SetDatatoClipboard(FPngFormatID, LStream);
// TBitmap としてもをクリップボードにコピー
LBitmap.Assign(LPngimg);
LStream.Clear;
LBitmap.SaveToStream(LStream);
SetDataToClipboard(CF_DIB, LStream);
// CF_BITMAP 形式のビットマップを取得できるようにする
// LFormatID の値は常に CF_BITMAP (VCL のソースコード参照)
LBitmap.SaveToClipboardFormat(LFormatID, LData, LPalette);
SetClipboardData(LFormatID, LData);
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LPngimg);
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// クリップボードからヒットマップのデータを取得
// つまり[貼り付け]の処理
// クリップボードのデータをメモリストリームに格納して読み込む
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LStream : TMemoryStream;
LPngImg : TPngImage;
LBitmap : TBitmap;
begin
// 透明ピクセルの確認のため TImage.Canvas を塗り潰す
Image1.Picture.Assign(nil);
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
LPngImg := TPngImage.Create;
LBitmap := TBitmap.Create;
LStream := TMemoryStream.Create;
try
// PNG 形式のデータがクリップボードにある場合
if Clipboard.HasFormat(FPngFormatID) then begin
GetDataFromClipboard(FPngFormatID, LStream);
LPngImg.LoadFromStream(LStream);
Image1.Canvas.Draw(0, 0, LPngImg);
end else
// CF_DIB のデータがある場合
if Clipboard.HasFormat(CF_DIB) then begin
// クリップボードのデータを取得
GetDataFromClipboard(CF_DIB, LStream);
// ファイルヘッダを検査してなければ追加
AddBmpFileHeader(LStream);
// メモリストリームからビットマップ画像データを読み込んで表示
LBitmap.LoadFromStream(LStream);
if CheckBox1.Checked then begin
// アルファ値を使用しているか (アルファ値が有効か) をチェック
if BmpCheckUsedAlpha(LBitmap) then begin
// AlphaFormat := afDefined にしないと透明ピクセルは黒になる
LBitmap.AlphaFormat := afDefined;
end else begin
// 透過色を透過状態にする
LBitmap.PixelFormat := pf24bit;
LBitmap.Transparent := True;
end;
end;
Image1.Canvas.Draw(0, 0, LBitmap);
end;
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LPngImg);
FreeAndNil(LBitmap);
end;
end;
//-----------------------------------------------------------------------------
// クリップボードへメモリストリームのデータを,指定形式でセット
// SetClipboardData 関数を使用
//-----------------------------------------------------------------------------
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
// クリップボードのデータ格納領域を確保してそのポインタを取得
LhGlobale := GlobalAlloc(GHND, AStream.Size);
LpBuffer := GlobalLock(LhGlobale);
try
AStream.Position := 0;
AStream.ReadBuffer(LpBuffer^, AStream.Size);
// クリップボードへセット
SetClipboardData(AFmtID, LhGlobale);
finally
GlobalUnlock(LhGlobale);
end;
end;
//-----------------------------------------------------------------------------
// クリップボードから,指定形式のデータをメモリストリームに格納
//-----------------------------------------------------------------------------
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
AStream.Clear;
// クリップボードのデータのポインタを取得
LhGlobale := Clipboard.GetAsHandle(AFmtID);
LpBuffer := GlobalLock(LhGlobale);
try
// メモリストリームにクリップボードのデータを書き込む
AStream.WriteBuffer(LpBuffer^, GlobalSize(LhGlobale));
AStream.Position := 0;
finally
GlobalUnlock(LhGlobale);
end;
end;
//-----------------------------------------------------------------------------
// メモリストリームにビットマップファイルヘッダの情報を追加
// ウェブブラウザの画像をコピーした場合等はファイルヘッダのデータがない
//-----------------------------------------------------------------------------
function AddBmpFileHeader(AStream: TMemoryStream): Boolean;
var
LSigChar : array[0..1] of AnsiChar;
LFileHeader : TBitmapFileHeader;
LStream : TMemoryStream;
begin
Result := True;
AStream.Position := 0;
AStream.Read(LSigChar, SizeOf(LSigChar));
AStream.Position := 0;
if LSigChar = 'BM' then Exit;
Result := False;
// BM がない場合はファイルヘッダがないので追加する
LFileHeader.bfType := $4D42;
LFileHeader.bfSize := AStream.Size;
LFileHeader.bfReserved1 := 0;
LFileHeader.bfReserved2 := 0;
LFileHeader.bfOffBits := SizeOf(TBitmapFileHeader);
// クリップボードのデータの前にビットマップファイルヘッダ部を追加
LStream := TMemoryStream.Create;
try
LStream.Write(LFileHeader, SizeOf(TBitmapFileHeader));
LStream.Write(AStream.Memory, AStream.Size);
LStream.Position := 0;
AStream.Clear;
LStream.SaveToStream(AStream);
AStream.Position := 0;
finally
FreeAndNil(LStream);
end;
end;
//-----------------------------------------------------------------------------
// ビットマップのビット深さを取得
// PixelFormat の値は,PixelFormat を明示的に設定していないと検出不可なので注意
//-----------------------------------------------------------------------------
function BmpCheckBitCount(ABitmap: TBitmap): WORD;
var
LStream : TMemoryStream;
LInfoHeader : TBitmapInfoHeader;
begin
LStream := TMemoryStream.Create;
try
ABitmap.SaveToStream(LStream);
LStream.Position := SizeOf(TBitmapFileHeader);
LStream.ReadBuffer(LInfoHeader, SizeOf(TBitmapInfoHeader));
Result := LInfoHeader.biBitCount;
finally
FreeAndNil(LStream);
end;
end;
//-----------------------------------------------------------------------------
// アルファチャンネルを使用しているかをチェック
// 32 ビット未満なら False
// 32 ビットの場合はアルファ値が全て 0 または $FF ならば False
// 32 ビットの TBitmap のデフォルト状態ではアルファ値は全て 0
// 結果が False のビットマップは 24 ビットにした方が無難
//-----------------------------------------------------------------------------
function BmpCheckUsedAlpha(ABitmap: TBitmap): Boolean;
type
TRGBQuadArray = array [0..MaxInt div SizeOf(TRGBQuad) - 1] of TRGBQuad;
PRGBQuadArray = ^TRGBQuadArray;
var
LYPos : Integer;
LXPos : Integer;
LAlpha : Byte;
LScanArray : PRGBQuadArray;
LCntTotal : Integer;
LCntZero : Integer;
LCntHexFF : Integer;
begin
Result := False;
if BmpCheckBitCount(ABitmap) < 32 then Exit;
ABitmap.Canvas.Lock;
LCntTotal := ABitmap.Width * ABitmap.Height;
LCntZero := 0;
LCntHexFF := 0;
try
for LYPos := 0 to ABitmap.Height - 1 do begin
LScanArray := ABitmap.ScanLine[LYPos];
for LXPos := 0 to ABitmap.Width - 1 do begin
LAlpha := LScanArray[LXPos].rgbReserved;
if LAlpha = $00 then Inc(LCntZero);
if LAlpha = $FF then Inc(LCntHexFF);
end;
end;
Result := not((LCntTotal = LCntZero) or (LCntTotal = LCntHexFF));
finally
ABitmap.Canvas.Unlock;
end;
end;
end.
上の Button2 クリックには,クリップボードから取得したデータにファイルヘッダ部を追加する処理があります.クリップボードにファイルヘッダがない場合,この処理がないと下図のようなエラーが発生します.
同じアプリ内でコピーと貼り付けを行う場合は,このような処理は必要ありません.
クリップボードにファイルヘッダ部があるのかは,コピー元のアプリの仕様によります. |
|
図11
ビットマップ画像が無効です |
クリップボードにコピーする時に,改めて CF_DIB 形式のデータとCF_BITMAP 形式のデータをセットしています.CF_DIB 形式でクリップボードにコピーすると自動的に CF_BITMAP のデータも格納されますが,その場合の CF_BITMAP のデータは,TBitmap.Assign(Clipboard) メソッドでは取得できません.下図のようなエラーが発生します.CF_DIB 形式のデータと CF_BITMAP 形式のデータは,個別に処理する必要があります. |
|
図12
サポートされていないクリップボード形式です |
[備考]
クリップボードの形式が CF_DIBV5 のビットマップは,ピッマップインフォヘッダ (ビットマップ情報ヘッダ の構造体が BITMAPV5HEADER (Delphi では TBitmapV5Header レコード型) のビットマップです.このビットマップは,ビットマップ・バージョン 5 とも言われていました.Windows 98 で採用されましたが,現在の Windows では使われていません. |
05_TPngImage.SaveToClipboardFormat メソッドを使用するコピー |
Delphi には,TBitmap.SaveToClipboardFormat, TBitmap.LoadFromClipboardFormat
等,クリップボードへのコピー,クリップボードからのデータ取得用のメソッドが実装されています.
TPNGImage にも同じメソッドがあります.下に示すコードは,それらのメソッドを使用する例ですが,透明 / 半透明の情報は失われます.
TBitmap.SaveToClipboardFormat メソッドも,CF_BITMAP 形式でのコピーとなります.アルファチャンネルの情報を保持したままでクリップボードにデータをセットすることはできません. |
|
図13
コピー元の PNG 画像 |
|
図14
貼り付け |
リスト6
TPngImage.SaveToClipboardFormat メソッドを使用したクリップボードへのコピー
CF_BITMAP 形式でのコピーとなり,アルファチャンネルの情報は失われる |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
FFilePath : string;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd,
Vcl.imaging.pngImage;
{$R *.dfm}
var
FFormatID : WORD;
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FFilePath := ExpandFileName('.\Images.png');
FFormatID := CF_BITMAP;
end;
//=============================================================================
// クリップボードに PNG 画像をコピー
// Windows の標準のクリップボードのフォーマットには PNG 形式はない
// アルファチャネルの情報は失われる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LPngimg : TPngImage;
LData : THandle;
LPalette : HPALETTE;
begin
Clipboard.Open;
Clipboard.Clear;
LPngimg := TPngImage.Create;
try
// Png 画像を読み込む
LPngimg.LoadFromFile(FFilePath);
// PNG 画像を TImage に描画
// 透明ピクセルの確認のため TImage.Canvas を塗り潰してから描画
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0, 0, LPngImg);
// クリップボードにコピーするために必要なデータを取得
// FFormatID の戻り値は CF_BITMAP
LPngimg.SaveToClipboardFormat(FFormatID, LData, LPalette);
// クリップボードへセット
Clipboard.SetAsHandle(FFormatID, LData);
finally
Clipboard.Close;
FreeAndNil(LPngimg);
end;
end;
//=============================================================================
// クリップボードから PNG 画像のデータを取得
// FFormatID を CF_BITMAP にしても結果は同じ
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LPngImage : TPngImage;
LData : THandle;
begin
Image1.Picture.Assign(nil);
if Clipboard.HasFormat(FFormatID) then begin
LPngImage := TPngImage.Create;
try
// クリップボードのデータのポインタを取得
LData := Clipboard.GetAsHandle(FFormatID);
// クリップボードのデータを TPngImaage のデータとして取得
LPngImage.LoadFromClipboardFormat(FFormatID, LData, 0);
// PNG 画像を TImage に描画
// 透明ピクセルの確認のため TImage.Canvas を塗り潰してから描画
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0, 0, LPngImage);
finally
Clipboard.Close;
FreeAndNil(LPngImage);
end;
end;
end;
end.
GIF 画像のクリップボードのフォーマット形式は CF_GIF です.今回作成した自前の関数も使用できますが,TGIFImage には,TBitmap と同じように,クリップボード入出力用の SaveToClipboardFormat メソッドと .LoadFromClipboardForma メソッドがあります.それらのメソッドを使用する例です.
このサンプルでは,CF_BITMAP 形式の画像としても取得できるようにしています.CF_BITMAP で取得したビットマップのビット深さは,常に 32 ビットです. |
|
図15
[Copy] ボタンをクリック
- 画像は添付の GIF 画像
- この画像をコピーする
- TGifImage.Transparent の値を False にして表示している
|
|
図16
[Paste] ボタンをクリック
|
リスト7
GIF 画像をクリップボードへコピー / 貼り付け |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
FFilePath : string;
public
{ Public 宣言 }
end;
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd,
Vcl.Imaging.GIFImg;
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FFilePath := ExpandFileName('.\Images.gif');
end;
//=============================================================================
// クリップボードに GIF 画像をコピー
// SaveToClipboardFormat と Clipboard.SetAsHandle を使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStream : TMemoryStream;
LGifImg : TGIFImage;
LFormatID : WORD;
LData : THandle;
LPalette : HPALETTE;
begin
Image1.Picture.Assign(nil);
Clipboard.Open;
Clipboard.Clear;
LStream := TMemoryStream.Create;
LGifImg := TGIFImage.Create;
try
// GIF 画像ファイルをメモリストリームに読み込む
LGifImg.LoadFromFile(FFilePath);
LGifImg.Transparent := False;
Image1.Canvas.Draw(0, 0, LGifImg);
// クリップボードにセット
// LFormatID の値は常に CF_GIF (VCL のソースコード参照)
LGifImg.SaveToClipboardFormat(LFormatID, LData, LPalette);
Clipboard.SetAsHandle(LFormatID, LData);
// CF_DIB 形式のビットマップを取得できるようにする
LGifImg.Bitmap.PixelFormat := pf8bit;
LGifImg.Bitmap.SaveToStream(LStream);
SetDataToClipboard(CF_DIB, LStream);
// CF_BITMAP 形式のビットマップを取得できるようにする
// LFormatID の値は常に CF_BITMAP (VCL のソースコード参照)
LGifImg.Bitmap.SaveToClipboardFormat(LFormatID, LData, LPalette);
SetClipboardData(LFormatID, LData);
finally
Clipboard.Close;
FreeAndNil(LGifImg);
FreeAndNil(LStream);
end;
end;
//=============================================================================
// クリップボードから GIF 画像のデータを取得
// Clipboard.GetAsHandle と LoadFromClipboardFormat を使用
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGifImg : TGIFImage;
LData : THandle;
begin
Image1.Picture.Assign(nil);
if Clipboard.HasFormat(CF_GIF) then begin
LGifImg := TGIFImage.Create;
try
// クリップボードのデータのポインタを取得
LData := Clipboard.GetAsHandle(CF_GIF);
// クリップボードのデータを TGifImaage のデータとして取得
LGifImg.LoadFromClipboardFormat(CF_GIF, LData, 0);
Image1.Canvas.Draw(0, 0, LGifImg);
finally
Clipboard.Close;
FreeAndNil(LGifImg);
end;
end;
end;
//-----------------------------------------------------------------------------
// クリップボードへメモリストリームのデータを,指定形式でセット
// SetClipboardData 関数を使用
//-----------------------------------------------------------------------------
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
// クリップボードのデータ格納領域を確保してそのポインタを取得
LhGlobale := GlobalAlloc(GHND, AStream.Size);
LpBuffer := GlobalLock(LhGlobale);
try
AStream.Position := 0;
AStream.ReadBuffer(LpBuffer^, AStream.Size);
// クリップボードへセット
SetClipboardData(AFmtID, LhGlobale);
finally
GlobalUnlock(LhGlobale);
end;
end;
end.
GIF 画像は,本ページの最初で述べたように,以下のコードでもクリップボードにコピー / 貼り付けできます.ただし,クリップボードに格納されるのは CF_GIF 形式のデータだけです. |
implementation
uses
Vcl.Clipbrd,
Vcl.Imaging.GifImg;
{$R *.dfm}
//=============================================================================
// GIF 画像をクリップボードにコピー
// Clipboard.Assign を使用する例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LGifImg : TGIFImage;
begin
Image1.Picture.Assign(nil);
LGifImg := TGIFImage.Create;
try
LGifImg.LoadFromFile(ExpandFileName('.\Images.gif'));
Clipboard.Assign(LGifImg);
LGifImg.Transparent := False;
Image1.Canvas.Draw(0, 0, LGifImg);
finally
FreeAndNil(LGifImg);
end;
end;
//=============================================================================
// クリップボードから GIF 画像のデータを取得
// TGIFImage.Assign を使用する例
// 上のコードでは,クリップボートのデータは CF_GIF だけ
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGifImg : TGIFImage;
begin
Image1.Picture.Assign(nil);
// CF_GIF のデータがある場合に処理
if Clipboard.HasFormat(CF_GIF) then begin
LGifImg := TGIFImage.Create;
try
LGifImg.Assign(Clipboard);
Image1.Canvas.Draw(0, 0, LGifImg);
finally
FreeAndNil(LGifImg);
end;
end;
end;
Windows の,定義済みのクリップボードのフォーマット形式には,JPEG 形式はありません.PNG 画像のサンプルと同じように,独自に形式を定義して処理することになります.
ワードやエクセルではフォーマット名に JFIF が使われているので,本サンプルもそうしています.他のアプリケーションでは,このフォーマット名で検出しないと,JPEG 画像ししては取得できません.そのような機能を実装していないアプリでも,CF_DIB 形式と CF_BITMAP 形式の画像としては取得できるようにしています.CF_BITMAP で取得したビットマップのビット深さは,常に 32 ビットです.
添付の JPEG 画像のビット深さは 24 ビットです.TJPEGImage.PixelFormat プロパティの値を jf8Bit (256 色) にしても,クリップボードには 24 ビットのデータとして格納されます. |
|
図17
[Copy] ボタンをクリック
- 画像は添付の JPEG 画像
- この画像をコピーする
- ワードやエクセルに JPEG 画像として貼り付けできる
|
|
図18
[Paste] ボタンをクリック
- クリップボードから JPEG 画像だけを取得
- ワードやエクセルの JPEG 画像も,JPEG 画像として取得できる
|
リスト8
JPEG 画像をクリップボードへコピー / 貼り付け |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
FFilePath : string;
public
{ Public 宣言 }
end;
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd,
Vcl.imaging.Jpeg;
{$R *.dfm}
const
// これはワードやエクセルが使用しているフォーマット名
FJpgFormatdStr = 'JFIF';
var
FJpgFormatID : Cardinal;
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FFilePath := ExpandFileName('Images.jpg');
// フォーマット名からフォーマットの ID 値を取得 (値は Windows が決定する)
FJpgFormatID := RegisterClipboardFormat(PChar(FJpgFormatdStr));
end;
//=============================================================================
// クリップボードに PNG 形式で画像をコピー
// Windows の標準のクリップボードのフォーマットには JPEG 形式はない
// 独自のフォーマット名を定義して実行する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStream : TMemoryStream;
LJpgimg : TJPEGImage;
LBitmap : TBitmap;
LFormatID : WORD;
LData : THandle;
LPalette : HPALETTE;
begin
Image1.Picture.Assign(nil);
Clipboard.Open;
Clipboard.Clear;
LStream := TMemoryStream.Create;
LJpgimg := TJPEGImage.Create;
LBitmap := TBitmap.Create;
try
// Jpeg 画像のデータをメモリストリームに格納
LJpgimg.LoadFromFile(FFilePath);
LJpgimg.SaveToStream(LStream);
Image1.Canvas.Draw(0, 0, LJpgimg);
// クリップボードに格納
SetDataToClipboard(FJpgFormatID, LStream);
LBitmap.Assign(LJpgimg);
// CF_DIB 形式のビットマップを取得できるようにする
LStream.Clear;
LBitmap.SaveToStream(LStream);
SetDataToClipboard(CF_DIB, LStream);
// CF_BITMAP 形式のビットマップを取得できるようにする
// LFormatID の値は常に CF_BITMAP (VCL のソースコード参照)
LBitmap.SaveToClipboardFormat(LFormatID, LData, LPalette);
SetClipboardData(LFormatID, LData);
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LJpgimg);
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// クリップボードから JPEG 画像のデータを取得
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LJpgImg : TJPEGImage;
LStream : TMemoryStream;
begin
Image1.Picture.Assign(nil);
// 指定フォーマットのデータがある場合に処理
if Clipboard.HasFormat(FJpgFormatID) then begin
LJpgImg := TJPEGImage.Create;
LStream := TMemoryStream.Create;
try
// クリップボードから画像データを取得
GetDataFromClipboard(FJpgFormatID, LStream);
// メモリストリームから画像データを読み込む
LJpgImg.LoadFromStream(LStream);
Image1.Canvas.Draw(0, 0, LJpgImg);
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LJpgImg);
end;
end;
end;
//-----------------------------------------------------------------------------
// クリップボードへメモリストリームのデータを,指定形式でセット
// SetClipboardData 関数を使用
//-----------------------------------------------------------------------------
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
// クリップボードのデータ格納領域を確保してそのポインタを取得
LhGlobale := GlobalAlloc(GHND, AStream.Size);
LpBuffer := GlobalLock(LhGlobale);
try
AStream.Position := 0;
AStream.ReadBuffer(LpBuffer^, AStream.Size);
// クリップボードへセット
SetClipboardData(AFmtID, LhGlobale);
finally
GlobalUnlock(LhGlobale);
end;
end;
//-----------------------------------------------------------------------------
// クリップボードから,指定形式のデータをメモリストリームに格納
//-----------------------------------------------------------------------------
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
AStream.Clear;
// クリップボードのデータのポインタを取得
LhGlobale := Clipboard.GetAsHandle(AFmtID);
LpBuffer := GlobalLock(LhGlobale);
try
// メモリストリームにクリップボードのデータを書き込む
AStream.WriteBuffer(LpBuffer^, GlobalSize(LhGlobale));
AStream.Position := 0;
finally
GlobalUnlock(LhGlobale);
end;
end;
end.
ICON 画像のクリップボードへのコピーと取得のサンプルです.
拡張子が .ico のアイコン画像も,定義済みのフォーマット形式がありまん.独自に定義することになります.このサンプルでは ICON としました.
他のアプリケーションでは,このフォーマット名で検出しないと,ICONG 画像しして取得できません.そのような機能を実装していないアプリでも,CF_DIB または CF_BITMAP 形式の画像として取得できるようにしています.このサンプルで使用している ICON 画像のビット深さは,32 ビットです. |
[備考]
Delphi の TIcon には,クリップボード入出力の機能は実装されていません. |
|
図19
[Copy] ボタンをクリック
|
|
図20
[Paste] ボタンをクリック
|
このサンプルでは,コピーした画像を CF_DIB または CF_BITMAP 形式の画像として取得できるようにしています.
下図は,これまでのサンプルで取得して貼り付けた結果です. |
|
図21
本ページの最初のサンプルで取得した結果
|
|
図22
[PNG 画像とビットマップ画像・・・] のサンプルで取得した結果
- アルファ値が有効な状態での取得になる
- TBitmap.AlphaFormat := afDefined にした場合
|
マイクロソフトでは,256 x 256 以上のアイコン画像として PNG 形式を使用することを推奨しています.
ワードやエクセルに,拡張子が .ico の画像を挿入すると,PNG 画像として扱われます.それらの画像をクリップボードにコピーすると,PNG 画像と,アルファチャンネル付きのビットマップ画像もクリップボードに格納されます. |
リスト9
ICON 画像をクリップボードへコピー / 貼り付け |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
FFilePath : string;
public
{ Public 宣言 }
end;
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd;
{$R *.dfm}
const
FIconFormatdStr = 'ICON';
var
FIconFormatID : Cardinal;
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
FFilePath := ExpandFileName('128_32bit.ico');
// フォーマット名からフォーマットの ID 値を取得 (値は Windows が決定する)
FIconFormatID := RegisterClipboardFormat(PChar(FIconFormatdStr));
end;
//=============================================================================
// クリップボードに TIcon の画像をコピー
// Windows の標準のクリップボードのフォーマットには TIcon に相当する形式はない
// 独自のフォーマット名を定義して実行する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LStream : TMemoryStream;
LIcon : TIcon;
LBitmap : TBitmap;
LFormatID : WORD;
LData : THandle;
LPalette : HPALETTE;
begin
Image1.Picture.Assign(nil);
Clipboard.Open;
Clipboard.Clear;
LStream := TMemoryStream.Create;
LIcon := TIcon.Create;
LBitmap := TBitmap.Create;
try
// アイコン画像のデータをメモリストリームに格納
LIcon.LoadFromFile(FFilePath);
LIcon.SaveToStream(LStream);
// 透過状態確認のため TImage.Canvas を塗り潰してから描画
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0, 0, LIcon);
Image1.Canvas.Draw(0, 0, LIcon);
// クリップボードに独自形式のデータを格納
SetDataToClipboard(FIconFormatID, LStream);
// CF_DIB 形式のビットマップを取得できるようにする
LBitmap.Assign(LIcon);
LStream.Clear;
LBitmap.SaveToStream(LStream);
SetDataToClipboard(CF_DIB, LStream);
// CF_BITMAP 形式のビットマップを取得できるようにする
LBitmap.SaveToClipboardFormat(LFormatID, LData, LPalette);
SetClipboardData(LFormatID, LData);
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LIcon);
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// クリップボードから TIcon 画像のデータを取得
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LIcon : TIcon;
LIcoStream : TMemoryStream;
begin
Image1.Picture.Assign(nil);
// 指定フォーマットのデータがある場合に処理
if Clipboard.HasFormat(FIconFormatID) then begin
LIcon := TIcon.Create;
LIcoStream := TMemoryStream.Create;
try
// クリップボードから指定フォーマットのデータを取得
GetDataFromClipboard(FIconFormatID, LIcoStream);
// メモリストリームから画像データを読み込む
LIcon.LoadFromStream(LIcoStream);
// 透過状態確認のため TImage.Canvas を塗り潰してから描画
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0, 0, LIcon);
finally
Clipboard.Close;
FreeAndNil(LIcoStream);
FreeAndNil(LIcon);
end;
end;
end;
//-----------------------------------------------------------------------------
// クリップボードへメモリストリームのデータを,指定形式でセット
// SetClipboardData 関数を使用
//-----------------------------------------------------------------------------
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
// クリップボードのデータ格納領域を確保してそのポインタを取得
LhGlobale := GlobalAlloc(GHND, AStream.Size);
LpBuffer := GlobalLock(LhGlobale);
try
AStream.Position := 0;
AStream.ReadBuffer(LpBuffer^, AStream.Size);
// クリップボードへセット
SetClipboardData(AFmtID, LhGlobale);
finally
GlobalUnlock(LhGlobale);
end;
end;
//-----------------------------------------------------------------------------
// クリップボードから,指定形式のデータをメモリストリームに格納
//-----------------------------------------------------------------------------
function GetDataFromClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
AStream.Clear;
// クリップボードのデータのポインタを取得
LhGlobale := Clipboard.GetAsHandle(AFmtID);
LpBuffer := GlobalLock(LhGlobale);
try
// メモリストリームにクリップボードのデータを書き込む
AStream.WriteBuffer(LpBuffer^, GlobalSize(LhGlobale));
AStream.Position := 0;
finally
GlobalUnlock(LhGlobale);
end;
end;
end.
ICON 画像は,32 ビットのアルファチャンネル付きの TBitmap,あるいは TPngImage 画像に変換して扱う方が確実と言えます.
アルファチャンネル付きの 32 ビットの TBitmap 画像には,以下のコードで変換できます.以下のコードでは,TPngImage 画像に正常に変換できません.アルファ値の情報が失われてしまいます.以下の記事のサンプルコードを参考にしてくだささい. |
|
図23
下にす示すコードを使用して変換した結果
- 左から順番に
元のアイコン画像ビットマップ画像
PNG 画像
|
リスト10
ICON 画像を TBitmap 画像と TPngImage 画像に変換 |
implementation
uses
Vcl.Clipbrd,
Vcl.Imaging.pngimage;
{$R *.dfm}
//=============================================================================
// ICON 画像を ビットマップと PNG 画像に変換
// テストに使用しているのは 32ビットの ICON 画像
// TBitmap は 32 ビットで透明 / 半透明の情報が保持される
// TPngImage は 24 ビットになる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
procedure TImageFillRectColor(AImage: TImage; AColor: TColor);
begin
AImage.Picture.Assign(nil);
AImage.Canvas.Brush.Color := AColor;
AImage.Canvas.FillRect(Image1.Canvas.ClipRect);
end;
var
LFileName : string;
LIcon : TIcon;
LBitamp : TBitmap;
LPngImg : TPngImage;
begin
TImageFillRectColor(Image1, clSilver);
TImageFillRectColor(Image2, clSilver);
TImageFillRectColor(Image3, clSilver);
if Edit1.Text = '' then exit;
LFileName := Edit1.Text;
LIcon := TIcon.Create;
LBitamp := TBitmap.Create;
LPngImg := TPngImage.Create;
try
LIcon.LoadFromFile(LFileName);
Image1.Canvas.Draw(0, 0, LIcon);
LBitamp.Assign(LIcon);
LBitamp.SaveToFile(ChangeFileExt(LFileName, '.bmp'));
Image2.Canvas.Draw(0, 0, LBitamp);
LPngImg.Assign(LBitamp);
LPngImg.SaveToFile(ChangeFileExt(LFileName, '.png'));
Image3.Canvas.Draw(0, 0, LPngImg);
finally
FreeAndNil(LIcon);
FreeAndNil(LBitamp);
FreeAndNil(LPngImg);
end;
end;
TKIcon を使用すると,透明 / 半透明ピクセルの情報を保持したまま PNG 画像に変換できます.
TKIcon は, 908_ExtractIcon.zip に同梱している kcontrols_1.7.zip 書庫内にあります. |
|
図24
TKIcon を使用して画像形式を変換
- 左から順番に
元の ICON 画像
ピッマップ画像
PNG 画像
|
リスト11
TKIcon を使用して,TIcon 画像を TBitmap 画像と TPngImage 画像に変換 |
implementation
uses
Vcl.Clipbrd,
Vcl.Imaging.pngimage,
KIcon;
{$R *.dfm}
//=============================================================================
// TKIcon を使用して ICON 画像を ビットマップと PNG 画像に変換
// TKIcon は 256 x 256 以上の icon 画像を扱える
// 元の ICON のビット数に関係なく,BMP, PNG 共に 32 ビットの画像となる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
procedure TImageFillRectColor(AImage: TImage; AColor: TColor);
begin
AImage.Picture.Assign(nil);
AImage.Canvas.Brush.Color := AColor;
AImage.Canvas.FillRect(Image1.Canvas.ClipRect);
end;
var
LFileName : string;
LKIcon : TKIcon;
LBitamp : TBitmap;
LPngImg : TPngImage;
begin
TImageFillRectColor(Image1, clSilver);
TImageFillRectColor(Image2, clSilver);
TImageFillRectColor(Image3, clSilver);
if Edit1.Text = '' then exit;
LFileName := Edit1.Text;
LKIcon := TKIcon.Create;
LBitamp := TBitmap.Create;
LPngImg := TPngImage.Create;
try
LKIcon.LoadFromFile(LFileName);
if LKIcon.IconCount = 1 then begin
Image1.Canvas.Draw(0, 0, LKIcon);
LKIcon.CopyToBitmap(0, LBitamp);
LBitamp.SaveToFile(ChangeFileExt(LFileName, '.bmp'));
if LKIcon.IconData[0].Bpp = 32 then begin
LBitamp.AlphaFormat := afDefined;
end else begin
LBitamp.Transparent := True;
end;
Image2.Canvas.Draw(0, 0, LBitamp);
LKIcon.CopyToPng(0, LPngImg);
LPngImg.SaveToFile(ChangeFileExt(LFileName, '.png'));
Image3.Canvas.Draw(0, 0, LPngImg);
end;
finally
FreeAndNil(LKIcon);
FreeAndNil(LBitamp);
FreeAndNil(LPngImg);
end;
end;
09_TPicture の画像をクリップボードへコピー |
TPicture の画像のクリップボードへのコピーと取得のサンプルです.
TImage には TImage.Pictur4 プロパティがありますから,TImage に描画している画像も,このようなコードでクリップボードにコピーできることになります.
TIcon 画像はクリップボードをサポートしていないので,この方法では,クリップボードへのコピーはできません.クリップボードのフォーマット形式がない PNG 画像,JPEGImage 画像は,TBitmap の画像としてコピーされます.画像のアルファ値の情報,あるいは透明 / 半透明の情報は失われます. |
|
図25
[Copy] ボタンをクリック
- TPicture の画像は GIF 画像
- それをクリップボードにコピー
|
|
図26
[Paste] ボタンをクリック
- クリップボードの TPicture オブジェクトを取得
|
リスト12
TPicture の画像をクリップボードへコピー / 貼り付け |
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
Button1: TButton;
Button2: TButton;
CheckBox1: TCheckBox;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd,
Vcl.Imaging.GIFImg;
{$R *.dfm}
//=============================================================================
// TPicture の画像をクリップボードにコピー
// TGifImage の画像の例
//
// クリップボートのフォーマット形式がない PNG, JPEG 等は TBitmap となる
// アルファ値の情報,透明 / 半透明の情報は失われる
// TIcon はクリップボードをサポートしていなので不可
//
// TImage には TImage.Picture プロパティがあるので TImage の画像もコピー可能
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LPicture : TPicture;
begin
Image1.Picture.Assign(nil);
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
LPicture := TPicture.Create;
try
LPicture.LoadFromFile('images.gif');
Clipboard.Assign(LPicture);
if LPicture.Graphic is TGIFImage then begin
// TBitmap であれば TPicture.Bitmap プロパティがある
(LPicture.Graphic as TGIFImage).Transparent := CheckBox1.Checked;
end;
Image1.Canvas.Draw(0, 0, LPicture.Graphic);
finally
FreeAndNil(LPicture);
end;
end;
//=============================================================================
// TPicture の画像をクリップボードから取得
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LPicture : TPicture;
begin
Image1.Picture.Assign(nil);
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
if Clipboard.HasFormat(CF_PICTURE) then begin
LPicture := TPicture.Create;
try
LPicture.Assign(Clipboard);
if LPicture.Graphic is TGIFImage then begin
// TBitmap であれば TPicture.Bitmap プロパティがある
(LPicture.Graphic as TGIFImage).Transparent := CheckBox1.Checked;
end;
Image1.Canvas.Draw(0, 0, LPicture.Graphic);
finally
FreeAndNil(LPicture);
end;
end;
end;
end.
TCF_PICTURE は,Delphi / C++Builder 特有の形式です.Windows のクリップボードの標準フォーマットにはありません.TClipBoard.Formats でも検出できるのは実際にコピーした画像形式だけです.CF_PICTUREW は,Delphi / C++Builder が内部的に使用する形式です.
他のアプリケーションからも,CF_IDB, CF_BITMAP 形式のビットマップ画像として取得できるようにしておくと便利と言えます. |
リスト13
TPicture の画像と CF_DIB, CF_BITMAP 形式のデータもクリップボードへコピー |
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
Form1: TForm1;
implementation
uses
Vcl.Clipbrd,
Vcl.Imaging.GIFImg,
Vcl.Imaging.jpeg,
Vcl.Imaging.pngimage;
{$R *.dfm}
//=============================================================================
// PNG 画像とビットマップ画像をクリップボードにコピー
// CF_DIB, CF_BITMAP 形式のデータもコピー
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
LFilepath : string;
LStream : TMemoryStream;
LPicture : TPicture;
LBitmap : TBitmap;
LFormatID : WORD;
LData : THandle;
LPalette : HPALETTE;
begin
// TIcon の画像の場合
// LFilepath := ExpandFileName('.\128_32bit.ico');
LFilepath := ExpandFileName('.\Images.bmp');
Image1.Picture.Assign(nil);
Clipboard.Open;
Clipboard.Clear;
LStream := TMemoryStream.Create;
LPicture := TPicture.Create;
LBitmap := TBitmap.Create;
try
LPicture.LoadFromFile(LFilePath);
// 透明ピクセルの確認のため TImage.Canvas を塗り潰してから描画
Image1.Canvas.Brush.Color := clSilver;
Image1.Canvas.FillRect(Image1.Canvas.ClipRect);
Image1.Canvas.Draw(0, 0, LPicture.Graphic);
// TPicture の画像をクリップボードにコピー
if not (LPicture.Graphic is TIcon) then begin
LPicture.SaveToClipboardFormat(LFormatID, LData, LPalette);
SetClipboardData(LFormatID, LData);
end;
// CF_DIB 形式のビットマップを取得できるようにする
LBitmap.Assign(LPicture.Graphic);
if LPicture.Graphic is TGIFImage then begin
LBitmap.PixelFormat := pf8bit;
end;
LStream.Clear;
LBitmap.SaveToStream(LStream);
SetDataToClipboard(CF_DIB, LStream);
// CF_BITMAP 形式のビットマップを取得できるようにする
// LFormatID の値は常に CF_BITMAP (VCL のソースコード参照)
LBitmap.SaveToClipboardFormat(LFormatID, LData, LPalette);
SetClipboardData(LFormatID, LData);
finally
Clipboard.Close;
FreeAndNil(LStream);
FreeAndNil(LPicture);
FreeAndNil(LBitmap);
end;
end;
//-----------------------------------------------------------------------------
// クリップボードへメモリストリームのデータを,指定形式でセット
// SetClipboardData 関数を使用
//-----------------------------------------------------------------------------
function SetDataToClipboard(AFmtID: WORD; AStream: TMemoryStream): Boolean;
var
LhGlobale : HGLOBAL;
LpBuffer : Pointer;
begin
Result := True;
// クリップボードのデータ格納領域を確保してそのポインタを取得
LhGlobale := GlobalAlloc(GHND, AStream.Size);
LpBuffer := GlobalLock(LhGlobale);
try
AStream.Position := 0;
AStream.ReadBuffer(LpBuffer^, AStream.Size);
// クリップボードへセット
SetClipboardData(AFmtID, LhGlobale);
finally
GlobalUnlock(LhGlobale);
end;
end;
|