MPOファイルの表示プログラム

C++ Builder CE版

jpsは左右二つのjpeg画像を横並びにしたもの、mpoはいわば縦並びにしたもので、mposhowのコードを眺めとわかります。再掲しませんがね。

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

が、切れ目なんですが、これってjpegのスタートsignatureですね。

つまりmpoファイルは上図のように、jpegを複数のバイト列として”縦”に並べたものだということだとわかります。そうと分かれば、コンソールプログラムでmpoファイルを読み込んで、左のjpeg画像と、右のjpeg画像に分割するプログラムは、

#include <stdio.h>
#include <string.h>

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

void	write_out_2files(unsigned char* buf,int firstsize,int allsize)
{
	FILE* f1;
	FILE* f2;
	int bytes;

	f1 = fopen("test1.jpg","wb");
	if( !f1 ){
		printf("f1 open error\n");
		return;
	}

	f2 = fopen("test2.jpg","wb");
	if( !f2 ){
		printf("f2 open error\n");
		return;
	}
// size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream)
	bytes = fwrite(buf,1,firstsize,f1);
	if( !bytes ){
		printf("fwrite error f1 \n");
		return;
	}
	bytes = fwrite(buf+firstsize,1,allsize-firstsize,f2);
	if( !bytes ){
		printf("fwrite error f2 \n");
		return;
	}

	fclose(f1);
	fclose(f2);
}

int main( int argc, char** argv)
{
	FILE* fp;
	int found = 0;

	fp = fopen(argv[1],"rb");
	if(!fp){
	  printf("file open error\n");
	  return 1;

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

	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;
		}
	}

	if( found )
		printf("offset is %X\n",offset);
	else
		printf("not found");

	printf("writing two files\n");

	write_out_2files(buf,offset,size);

}

例によって最低限のエラーチェックしかしていませんが、任意のmpoファイルを入力し、出力された左右のjpeg画像を確認するとこれで動いてます。ポータビリティを考慮してこのまま使うかな?基本的にはファイル出力が目的ではなく、

Image1->Picture->LoadFromStream(.....);

で表示するので、BYTE列をTMemoryStreamに変換する必要がありますが、それは作りながら検討しますかね。ちなみにC++ Builder CEでもコンソールプログラムでも、fopen,fread,fscanf系の関数は動きます。黎明期にはC++ Builder Nativeな関数コールとfopen系を混在しない方が良いと言われていましたが、筆者は問題を経験したことがありません。さて、そろそろC++ Builder CE版のmposhowプログラムの掲載です。フォームは、最低限で、

TButton一個、TImage二個、TOpenDialog一個を置きます。コードは、

#include <vcl.h>
#pragma hdrstop

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

#include <string.h>

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


TForm1 *Form1;
//---------------------------------------------------------------------------

void DispJpegs(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();

	left->WriteBuffer(buf,offset);
	right->WriteBuffer(buf+offset,size-offset);
	//write_out_2files(buf,offset,size);

	left->Position = 0;
	right->Position = 0;

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


}
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
	if( OpenDialog1->Execute())
		DispJpegs(OpenDialog1->FileName);
}
//---------------------------------------------------------------------------

MPOサイドも動きましたので、プレビュー付きダイアログができれば完成ですが、次回はそのダイアログ周辺を取り上げる予定です。

コメント