|
16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"JPEGファイルの画像サイズを得る"
【タイトル】JPEGファイルの画像サイズを得る
JPEG形式の画像ファイルを TJPEGImage に読み込ませること無く画像
サイズを得るサンプルコードです。
【JPEGファイルの構造】
JPEGファイルの構造はセグメントの集まりになっていて、
[$ff, $d8]
{ セグメント }
{ セグメント }
{ セグメント }
...
(イメージデータなど)
ほとんどのセグメントは4バイトのヘッダを持つ(例外あり)ので、
以下のようになります。
$ff <= セグメントのマーク(1 byte)
$?? <= セグメントの種類を示す(1 byte)
$???? <= セグメントのサイズ(2 bytes)
(ただし、HiByte と LoByte が Intel系CPUの逆です)
... <= セグメントの中身(データ)
画像サイズが入っているセグメントは $c0(SOF0)なので、このセ
グメントが見つかるまでセグメントをたどっていけば、JPEGの画像サ
イズが得られるはずです。
注意すべき点が2点あります。
注意1:セグメントの長さ情報もデータもないセグメントがあり得ま
す($01 および $b0〜$b7)。
注意2:セグメント間に $ff が何バイト存在しても有効なフォーマッ
トです。次のセグメントを探す際にはスキップします。
【サンプルコード】
以下が、JPEGのファイル名を渡して画像サイズを得る関数のサンプ
ルコードです。
function GetJPEGSize(var Size: TSize; FileName: string): Boolean;
var
Stream: TStream;
bCode: BYTE;
wData: WORD;
begin
Result := FALSE;
Stream := TFileStream.Create(FileName,
fmOpenRead or fmShareDenyWrite);
try
Stream.Position := 0;
if (Stream.Read(wData, 2) = 2)and(wData = $d8ff) then
begin
while Stream.Read(bCode, 1) = 1 do
begin
if bCode <> $ff then
break; // フォーマットがおかしい
// セグメント間の $ff は無視する
while Stream.Read(bCode, 1) = 1 do
if bCode <> $ff then break;
// 次のセグメントが見つからなかった
if bCode = $ff then
break;
if bCode = $c0 then
begin
// SOF(Start Of Frame) セグメント
// 長さ情報とdata precision の部分をスキップ
Stream.Seek(3, soFromCurrent);
// 画像の高さ
if Stream.Read(wData, 2) <> 2 then
break;
Size.cy := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
// 画像の幅
if Stream.Read(wData, 2) <> 2 then
break;
Size.cx := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
// 画像サイズが得られたのでループを抜ける
Result := TRUE;
break;
end
else if bCode = $da then
// SOS(Start Of Scan) セグメント
// このセグメント以降はイメージデータ
break
else if bCode in [$01, $b0..$b7] then
// これらのセグメントは長さ情報も何もない
// do nothing
else // その他のセグメントはスキップする
begin
if Stream.Read(wData, 2) <> 2 then
break;
wData := (wData shl 8 and $ff00)or(wData shr 8 and $ff);
if wData < 2 then
break; // サイズ情報が異常
Stream.Seek(wData - 2, soFromCurrent);
end;
end;
end
else ; // ファイルの最初の2バイトが $d8ff ではない
finally
Stream.Free;
end;
end;
この関数の使用例は、
// Button1.OnClick イベントハンドラの例
procedure TForm1.Button1Click(Sender: TObject);
var
Size: TSize;
begin
if OpenPictureDialog1.Execute then
begin
if GetJPEGSize(Size, OpenPictureDialog1.FileName) then
ShowMessage(IntToStr(Size.cx) + '*' + IntToStr(Size.cy))
else
ShowMessage('サイズの取得に失敗しました');
end;
end;
【あとがき】
たぶん上記のコードに問題はないとは思いますが、使用される場合
には自己責任でデバッグ等を行い、必要に応じて修正してください。
サンプルコードの関数は Stream.Read を使っていますが、
Stream.ReadBuffer を使って読み損なったら例外を投げる方が
Object Pascal の正しい作法かもしれません。(^^;
2001/06/12、河邦 正(GCC02240@nifty.com)
(http://homepage2.nifty.com/kht0000/、NIFTY外へ私作のComponentの
公開用)
Original document by 河邦 正 氏 ID:(GCC02240)
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。
Copyright 1996-2002 Delphi Users' Forum
|