C++ Builder XE4 > 画像処理 > 画面に入りきらないTChartを部分ごとにキャプチャし、合成した上でクリップボードに送る

```txt:動作環境
C++ Builder XE4
```

### 関連

- [C++ Builder XE4 > FastReport VCL 5 > PDF Export > TEdit付きのChart1をFastReportへ送る (クリップボード経由) > 解像度は72dpi](https://qiita.com/7of9/items/81e8af645a659a647bab)
- [C++ Builder XE4 > 画像処理 > 4つのファイル画像をN個合成して、表示枠を超える画像を作成する + クリップボードへ送る > ペイントブラシで画像を端から端まで貼付けできるか?](https://qiita.com/7of9/items/8524d1e9e262d82e98ed)
- [C++ Builder XE4 > 画像処理 > 4つのファイル画像を合成してTImageとして表示](https://qiita.com/7of9/items/f5513720860db98b7b00)
- [C++ Builder XE4 > 実装例: (a)TPanel上の表示をStretchBlt()使用で拡大 + (b)クリップボードへ送る + (c)ペイントブラシ起動](https://qiita.com/7of9/items/bf19cbec133302b90b5b)
- [C++ Builder XE4 > Clipboard > クリップボードへ画像を送り、クリップボードから画像ファイルへ保存](https://qiita.com/7of9/items/ce32066dc8d9d92cbe30)


### 目的

- TChartを高解像度でFastReportへ送る

障害

- FastReport上のTfrxChartViewに対してCloneChart()処理がまともに動かない
    - グラフタイトル、軸タイトルしかクローンできない
    - 内部の系列データは自前実装？でコピーする
        - その実装もまともに動かない?

### 検討した方法

- 画像を部分ごとにキャプチャする
- 個々の画像を連結して一つのTImageとする
- TImageからクリップボードで送る

### フォームデザイン

![2018-12-26_09h57_39.png](https://qiita-image-store.s3.amazonaws.com/0/32870/ed014c8a-d7bd-58be-2f59-652fbb71efa1.png)

- 左上は連結用TImge
- 右下はキャプチャ対象画像
    - TChart上に３つのTShapeを配置
    - 実行時に拡大をして、画面に収まりきらない状態にする
        - その状態でキャプチャできることを確認するため

### 実装

```Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
#include <VCLTee.Chart.hpp>
#include <VCLTee.TeEngine.hpp>
#include <VCLTee.TeeProcs.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TImage *Image1;
	TButton *Button1;
	TPanel *Panel1;
	TChart *Chart1;
	TShape *Shape1;
	TShape *Shape2;
	TShape *Shape3;
	void __fastcall Button1Click(TObject *Sender);
	void __fastcall FormCreate(TObject *Sender);
private:	// ユーザー宣言
public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
```

```Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <pngimage.hpp>
#include <ClipBrd.hpp>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------

static const int kNum_div = 6;  // 画像分割数 (水平と垂直で同じ分割). 3:任意

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	// 0. オリジナルの位置を保持
	int preTop = Chart1->Top;
	int preLeft = Chart1->Left;

	// 1. 部分ごとにキャプチャして連結する
	int clipWidth = Panel1->Width / kNum_div;  // 切取り高さ
	int clipHeight = Panel1->Height / kNum_div;  // 切取り幅

	TBitmap *bmp_whole = new TBitmap();  // A. 全画像を載せる
	TBitmap *bmp_frac = new TBitmap();   // B. 部分ごとにキャプチャし、逐次Aに載せる

	bmp_frac->SetSize(clipWidth, clipHeight);
	bmp_whole->SetSize( Panel1->Width, Panel1->Height );

	HDC dc_frac = GetDC(Panel1->Handle);  // BitBlt()用
	for(int ri = 0; ri < kNum_div; ri++) {  // ri: row index
		for(int ci = 0; ci < kNum_div; ci++) {  // ci: column index
			// Panel上でキャプチャ対象部分をキャプチャ位置に移動 (左上でキャプチャする)
			Chart1->Top  = -(ri * clipHeight);
			Chart1->Left = -(ci * clipWidth);

			// 下記のwaitがないと画像取得に失敗し、[左上の画像 * N個]になる
			Sleep(10);  // msec
			Application->ProcessMessages();

			// 常に左上の位置(0,0)でキャプチャする
			::BitBlt(bmp_frac->Canvas->Handle, 0, 0, clipWidth, clipHeight, dc_frac, 0, 0, SRCCOPY);

			// キャプチャしたものを逐次、全画像用BMPに描く
			bmp_whole->Canvas->Draw(clipWidth * ci, clipHeight * ri, bmp_frac);
		}
	}
	Image1->Picture->Assign(bmp_whole);

	// 2. Chart1を元の位置に戻す
	Chart1->Top = preTop;
	Chart1->Left = preLeft;

	// 3. クリップボードへ
	Clipboard()->Assign(bmp_whole);

	// 4, 手仕舞い
	delete bmp_whole;
	delete bmp_frac;

   //  5. ペイントブラシ起動 (ctrl + vでの動作確認用)
	ShellExecute(NULL, NULL, L"pbrush.exe", L"", NULL, SW_SHOWNORMAL);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
	// Chart1はPanel1上に配置されている
	// 上下左右をPanel1に合わせて伸縮するようにする
	Chart1->Anchors = TAnchors() << akLeft << akTop << akRight << akBottom;

	// デザイン画面で重なりを明白にするため、サイズを変えていたが、
	// 本来はChart1とPanel1は同じサイズとする
	Chart1->Top = 0;
	Chart1->Left = 0;
	Chart1->Height = Panel1->Height;
	Chart1->Width = Panel1->Width;

	// Panelのサイズを拡大 (画面に入りきらないサイズの例として)
	Panel1->Height *= 2;
}
//---------------------------------------------------------------------------
```

### 動作例

ソフト起動時
![2018-12-26_09h59_34.png](https://qiita-image-store.s3.amazonaws.com/0/32870/37b5a78d-154b-4201-973d-d52e679c1a60.png)

Button1押下後
![2018-12-26_09h59_50(1).png](https://qiita-image-store.s3.amazonaws.com/0/32870/05a9999b-aefd-6197-db8e-aedf72f570cb.png)

起動したペイントブラシにて<kbd>ctrl</kbd>+<kbd>v</kbd>した後
![2018-12-26_10h01_58.png](https://qiita-image-store.s3.amazonaws.com/0/32870/c9a0b493-de49-0ba4-e23b-99325ffa6fb5.png)

画面には表示しきれない画像をキャプチャし、クリップボードで送ることができた。


### 失敗

上記実装時にriとciを使い間違える失敗をした。
loop-index cross-talk

- [技術英語 > cross-talk > 悪い例 (loop-index cross-talk) / 悪い例 / 良い例](https://qiita.com/7of9/items/a8191dfbc15c0a124a45)

### Sleep()

```cpp
			Sleep(10);  // msec
			Application->ProcessMessages();
```

上記は`Sleep(1); //msec`でも成功する場合もある。

実環境で実行して、値を調整すること。


### Image1不要

動作確認用に使用しているが、処理において重要ではないため削除して問題ない。