お知らせ

電子会議

ライブラリ

パレット

Delphi FAQ検索

Delphi FAQ一覧

サンプル蔵





FDelphi FAQ
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