プレビュー付き画像選択ダイアログを作る

JPSやらMPOやらのデフォルトではプレビューできないファイルを表示する

C++ Builder CEやらDelphiでファイル選択ダイアログを使う場合に、jpgやらpngとかならばプレビューできますが、非標準のファイルの中身を観ることはできません。で、TOpenDialog等のパネルになんとかして独自のコントロールを追加する方法があります。検索するとでてきます。例えば

ファイル選択ダイアログ コントロール追加

として検索すると、

一番上ですね。

Mr.XRAYさんのこのサイトは網羅的に主にDelphiのかなり高度なプログラミングについて書かれていて、大変参考になります。かなり昔からありますよね。

このようにしてTPanelを追加できます。ので、さらにここへTImageも載せられますね。で、Delphiで書かれたソースをなんとかC++ Builderへポートしてみた結果の一例が、

これですが、TImageを載せるのは、ご覧のように空きスペースの関係で少し無理がありますので、TOpenDialogをカスタマイズするのは止めて、一からダイアログを作ります。最終形の実行時の姿が、

左下のImageに表示されているのがプレビューです。ここで”OK”をクリックすると、

と左右の画像が交差法(Cross)で表示されます。

ダイアログはForm2ですが、いつものようにVCLプログラムを新規作成し、Form1の方には、ボタンを貼りつけて、

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Form1->Image1->Visible = false;
	Form1->Image2->Visible = false;

	Form2 = new TForm2(this);

	try {
		 if( Form2->ShowModal() == mrOk ){
			Form1->Image1->Visible = true;
			Form1->Image2->Visible = true;
		 }
		 else
			return;
	} __finally  {

		Form2->Free();
	}

	if( FileExists(Label1->Caption)){
        Label2->Caption = "there";
		//Image1->Picture->Bitmap = gleft;
		//Image2->Picture->Bitmap = gright;
	}

}

としておきます。Form2は動的に生成して、

Form2 = new TForm2(this);
Form2->ShowModal() == mrOk

として、モーダルダイアログとして表示します。ということで、プログラム起動時にForm2は非表示にしておきます。コードでも可能ですが、簡単には、プロジェクト->オプションから

と設定すればおけです。Form2のデザインですが、

構造というか構成要素は、

ボタン”OK”と”Cancel”は必須ですが、コアな要素は、

TDriveComboBox、TDirectoryListBox、TFileListBoxがセットです。見やすい位置と大きさに置くだけではなく、連携させると吉です。

というように、リンクします。ファイル名のフィルターは、FileListBox1->Maskで設定します。この例では、jpg,jps,mpoなファイルを候補として表示します。

表示初期位置の設定は、

__fastcall TForm2::TForm2(TComponent* Owner)
	: TForm(Owner)
{

	DirectoryListBox1->Directory = "C:\\Users\\docna\\Downloads\\";
}

で、可能ですが、存在しないディレクトリだとエラーになるので注意しましょう。

ダイアログでFileListBoxの要素をクリックしたイベントを捉えて、プレビューを表示すればいいので、

void __fastcall TForm2::FileListBox1Click(TObject *Sender)
{
	String fname = FileListBox1->FileName;

	Form1->Label1->Caption =  fname;

	String ext = ExtractFileExt(fname).LowerCase();
	Label2->Caption = ext;

	if( ext == ".jpg" ){

		TJPEGImage* original = new TJPEGImage();
		TBitmap* origbitmap = new TBitmap();

		original->LoadFromFile(fname);
		origbitmap->Assign(original);
		Image1->Picture->Bitmap = origbitmap;

		delete original;
		delete origbitmap;
	}
	else if( ext == ".mpo" ){
	/*
		TMemoryStream* ms = new TMemoryStream();
		DispMPO(fname,ms);
		ms->Position = 0;

		Image1->Picture->LoadFromStream(ms);

		delete ms;
	*/
		DispMPO(fname);
		Image1->Picture->Assign(Form1->Image1->Picture);
		//Image1->Picture->Bitmap = Form1->Image1->Picture->Bitmap; // copy
	}
	else {  // jps Image2.Picture.Assign(Image1.Picture);

		//TBitmap* ms = new TBitmap();
		DispJPS(fname);
		Image1->Picture->Assign(Form1->Image1->Picture);

	}


}

プレビューは左画像を表示するので、左右の分離はあらかじめしておくロジックです。で、左画像を拾ってきて、Form2->Image1に表示しています。

まとめてソースを掲載しておきます。Unit1.cppは、

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
#include <windows.h>

#include <algorithm>
#include <System.IOUtils.hpp>
#include <string.h>
#include <jpeg.hpp>

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

unsigned char* buf;
unsigned char pat[] = {0xff,0xd8,0xff,0xe1};



TJPEGImage* original = new TJPEGImage();
TBitmap* origbitmap = new TBitmap();


void DispJPS(String filename)
{

	original->LoadFromFile(filename);
	origbitmap->Assign(original);
	//Image1->Picture->Bitmap = origbitmap;
		TBitmap* bitmap = new TBitmap();
	try {
		bitmap->Assign(original);

		int width = bitmap->Width;
		int height = bitmap->Height;

		// Create a cropped bitmap
		TBitmap* croppedBitmap = new TBitmap();
		try {
			croppedBitmap->Width = width/2;
			croppedBitmap->Height = height;

			// Copy the desired region from the original bitmap
			croppedBitmap->Canvas->CopyRect(
				Rect(0, 0, width/2, height),
				bitmap->Canvas,
				Rect(0,0,width/2, height)   // left image
			);

			Form1->Image1->Picture->Bitmap = croppedBitmap;
			//ms = croppedBitmap;
			//gleft = croppedBitmap;

		} __finally {
			delete croppedBitmap;
		}
	} __finally {
		delete bitmap;
	}

	bitmap = new TBitmap();
	try {
		bitmap->Assign(original);

		int width = bitmap->Width;
		int height = bitmap->Height;

		// Create a cropped bitmap
		TBitmap* croppedBitmap = new TBitmap();
		try {
			croppedBitmap->Width = width/2;
			croppedBitmap->Height = height;

			// Copy the desired region from the original bitmap
			croppedBitmap->Canvas->CopyRect(
				Rect(0, 0, width/2, height),
				bitmap->Canvas,
				Rect(0,0,width/2, height)   // left image
			);

			Form1->Image2->Picture->Bitmap = croppedBitmap;
			//gright = croppedBitmap;
		} __finally {
			delete croppedBitmap;
		}
	} __finally {
		delete bitmap;
	}
}

void DispMPO(String filename)
{

	FILE* fp;
	int found = 0;

	fp = fopen(AnsiString(filename).c_str(),"rb");
	if(!fp){
	  ShowMessage("file open error\n");
	  return;

	}
	long sz;
	 fseek( fp, 0, SEEK_END );
  sz = ftell( fp );
  //printf( "filesize %d バイト", sz );
	rewind(fp);
	buf = new unsigned char[sz];

	if( !buf ){
		ShowMessage("not enough core, buy some :-)");
		return;
	}
	int size = fread(buf,1,sz,fp);
	//printf("%d bytes read\n",size);
	fclose(fp);

	unsigned char* p = buf+4; // skip first occurrence
	int	offset = 4;

// int memcmp(const void *ptr1, const void *ptr2, size_t num);

	while( p < buf + size ){
		if( !memcmp(p,pat,4)){
			found = 1;
			break;
		}
		else {
			offset += 4;
			p += 4;
		}
	}

    TMemoryStream* left = new TMemoryStream();
	TMemoryStream* right = new TMemoryStream();

	//ms->WriteBuffer(buf,offset);    // for calling side
	left->WriteBuffer(buf,offset);
	right->WriteBuffer(buf+offset,size-offset);
	//write_out_2files(buf,offset,size);
	left->Position = 0;
	right->Position = 0;

	//Form2->Image1->Picture->LoadFromStream(left);

	Form1->Image1->Picture->LoadFromStream(left);
	Form1->Image2->Picture->LoadFromStream(right);

	delete left;
	delete right;

}



//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{


}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
	Form1->Image1->Visible = false;
	Form1->Image2->Visible = false;

	Form2 = new TForm2(this);

	try {
		 if( Form2->ShowModal() == mrOk ){
			Form1->Image1->Visible = true;
			Form1->Image2->Visible = true;
		 }
		 else
			return;
	} __finally  {

		Form2->Free();
	}

	if( FileExists(Label1->Caption)){
        Label2->Caption = "there";
		//Image1->Picture->Bitmap = gleft;
		//Image2->Picture->Bitmap = gright;
	}

}
//---------------------------------------------------------------------------

Unit2.cppは、

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <jpeg.hpp>

#include "Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;


extern TBitmap* gleft;
extern TBitmap* gright;

void DispMPO(String filename);
void DispJPS(String filename);

//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
	: TForm(Owner)
{

	DirectoryListBox1->Directory = "C:\\Users\\docna\\Downloads\\";
}

//---------------------------------------------------------------------------
void __fastcall TForm2::OKClick(TObject *Sender)
{
	Form1->Label1->Caption = FileListBox1->FileName;
    ModalResult = mrOk;
}

//---------------------------------------------------------------------------
void __fastcall TForm2::CancelClick(TObject *Sender)
{
	ModalResult = mrCancel;
}

//---------------------------------------------------------------------------
// FileListBox1.Mask := '*.txt;*.docx';

void __fastcall TForm2::FileListBox1Click(TObject *Sender)
{
	String fname = FileListBox1->FileName;

	Form1->Label1->Caption =  fname;

	String ext = ExtractFileExt(fname).LowerCase();
	Label2->Caption = ext;

	if( ext == ".jpg" ){

		TJPEGImage* original = new TJPEGImage();
		TBitmap* origbitmap = new TBitmap();

		original->LoadFromFile(fname);
		origbitmap->Assign(original);
		Image1->Picture->Bitmap = origbitmap;

		delete original;
		delete origbitmap;
	}
	else if( ext == ".mpo" ){
	/*
		TMemoryStream* ms = new TMemoryStream();
		DispMPO(fname,ms);
		ms->Position = 0;

		Image1->Picture->LoadFromStream(ms);

		delete ms;
	*/
		DispMPO(fname);
		Image1->Picture->Assign(Form1->Image1->Picture);
		//Image1->Picture->Bitmap = Form1->Image1->Picture->Bitmap; // copy
	}
	else {  // jps Image2.Picture.Assign(Image1.Picture);

		//TBitmap* ms = new TBitmap();
		DispJPS(fname);
		Image1->Picture->Assign(Form1->Image1->Picture);

	}


}
//---------------------------------------------------------------------------

なにやら昨今スマホ内斜視が問題になっているようなので、

デフォルトは交差法でなく平行法に切り替えた方がよいかもしれませんね。

コメント