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サイドも動きましたので、プレビュー付きダイアログができれば完成ですが、次回はそのダイアログ周辺を取り上げる予定です。
コメント