4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Delphi で Wio Terminal 用画像コンバータを作ってみる

Last updated at Posted at 2020-05-21

はじめに

Wio Terminal を購入しまして。画像を表示させるチュートリアルをやってみようと思ったのですが...

画像表示

画像を表示させるためにはチュートリアルの通りやればいいのですが、画像は 24bit Bitmap から、独自の 8bit / 16bit ビットマップへ変換しなくてはなりません。

色深度 フォーマット
8bit rgb332
16bit rgb565

変換ための Python スクリプト (bmp_converter.py) が用意されているのですが、

ImportError: No module named PIL

だの、

ModuleNotFoundError: No module named 'PIL'

だの言われて難儀しました。結局、チュートリアルページに書いてある pip では解決せず、Anaconda をインストールする必要がありました。その Anaconda を動作させるためには環境変数に...

面倒くさい...

チュートリアルを試したいだけなのに...。

Linux を常用している人 or Python を常用している人 にはなんてことはないのでしょうけど、僕にとっては面倒くさかったので、変換ツールを Delphi 10.3 Rio で書く事にしました。

ソースコード

書きました。

bmp_converter.dpr
program bmp_converter;

{$APPTYPE CONSOLE}

uses
  System.Classes, System.SysUtils, System.IOUtils, System.UITypes, Vcl.Graphics;

type
  TRGBType = (rgb8bit, rgb16bit);

const
  SUBDIR: array [TRGBType] of string = ('rgb332', 'rgb565');

  function ConvertToRGB332(aColor: TColorRec): Byte;
  begin
    with aColor do
      begin
        R := R shr 5;
        G := G shr 5;
        B := B shr 6;
        Result := (R shl 5) or (G shl 2) or B;
      end;
  end; { ConvertToRGB332 }

  function ConvertToRGB565(aColor: TColorRec): Word;
  begin
    with aColor do
      begin
        R := R shr 3;
        G := G shr 2;
        B := B shr 3;
        Result := (R shl 11) or (G shl 5) or B;
      end;
  end; { ConvertToRGB565 }

begin
  // 色深度の選択
  Writeln('Enter (1) for 8-bit colour convert, Enter (2) for 16-bit colour convert');
  var c: Char;
  while True do
    begin
      Readln(c);
      if CharInSet(c, ['1'..'2']) then
        Break;
      Writeln('Invalid input!');
    end;
  var RGBType := TRGBType(Ord(c) - Ord('1'));

  // 入出力フォルダ
  var SrcDir := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), 'bmp');
  var DstDir := TPath.Combine(SrcDir, SUBDIR[RGBType]);
  if not TDirectory.Exists(DstDir) then
    TDirectory.CreateDirectory(DstDir);

  // bmp フォルダにある *.bmp を処理
  var im: TBitmap := TBitmap.Create;
  try
    for var FileName in TDirectory.GetFiles(SrcDir, '*.bmp', TSearchOption.soTopDirectoryOnly) do
      begin
        // 画像をロードして強制的に 24bit ビットマップに変換
        im.LoadFromFile(FileName);
        im.PixelFormat := TPixelFormat.pf24bit;
        // データの生成
        var v: TBytes := [im.Width  and $00FF, im.Width  shr 8,
                          im.Height and $00FF, im.Height shr 8];
        for var y := 0 to Pred(im.Height) do
          for var x := 0 to Pred(im.Width) do
            begin
              var Color: TColorRec;
              Color.Color := im.Canvas.Pixels[x, y];
              case RGBType of
                rgb8bit:
                  v := v + [ConvertToRGB332(Color)];
                rgb16bit:
                  begin
                    var value := ConvertToRGB565(Color);
                    v := v + [value shr 8, value and $00FF];
                  end;
              end;
            end;
        // バイト列をファイルに保存
        var bs := TBytesStream.Create(v);
        try
          bs.SaveToFile(TPath.Combine(DstDir, TPath.GetFileName(FileName)));
        finally
          bs.Free
        end;
      end;
  finally
    im.Free;
  end;
end.

Delphi 10.3 Rio さえインストールされていれば、上記ファイルを bmp_converter.dpr という名前で保存し、[ファイル | プロジェクトを開く] で開いてコンパイルするだけのお手軽さです。

  • 外部ライブラリや特別な設定は必要ありません。
  • 仕様は bmp_converter.py 準拠です (bmp サブフォルダにある *.bmp を変換します)。

See also:

ソースコード (高速化版)

ピクセルの取得に ScanLine を使い、動的配列の領域確保回数を最小限に抑えた高速化版です。

bmp_converter.dpr
program bmp_converter;

{$APPTYPE CONSOLE}

uses
  System.Classes, System.SysUtils, System.IOUtils, System.UITypes,
  Winapi.Windows, Vcl.Graphics;

type
  TRGBType = (rgb8bit, rgb16bit);

const
  SUBDIR: array [TRGBType] of string = ('rgb332', 'rgb565');

  function ConvertToRGB332(aColor: TRGBTriple): Byte;
  begin
    with aColor do
      begin
        rgbtRed   := rgbtRed   shr 5;
        rgbtGreen := rgbtGreen shr 5;
        rgbtBlue  := rgbtBlue  shr 6;
        Result := (rgbtRed shl 5) or (rgbtGreen shl 2) or rgbtBlue;
      end;
  end; { ConvertToRGB332 }

  function ConvertToRGB565(aColor: TRGBTriple): Word;
  begin
    with aColor do
      begin
        rgbtRed   := rgbtRed   shr 3;
        rgbtGreen := rgbtGreen shr 2;
        rgbtBlue  := rgbtBlue  shr 3;
        Result := (rgbtRed shl 11) or (rgbtGreen shl 5) or rgbtBlue;
      end;
  end; { ConvertToRGB565 }
begin
  // 色深度の選択
  Writeln('Enter (1) for 8-bit colour convert, Enter (2) for 16-bit colour convert');
  var c: Char;
  while True do
    begin
      Readln(c);
      if CharInSet(c, ['1'..'2']) then
        Break;
      Writeln('Invalid input!');
    end;
  var RGBType := TRGBType(Ord(c) - Ord('1'));

  // 入出力フォルダ
  var SrcDir := TPath.Combine(TPath.GetDirectoryName(ParamStr(0)), 'bmp');
  var DstDir := TPath.Combine(SrcDir, SUBDIR[RGBType]);
  if not TDirectory.Exists(DstDir) then
    TDirectory.CreateDirectory(DstDir);

  // bmp フォルダにある *.bmp を処理
  var im: TBitmap := TBitmap.Create;
  try
    for var FileName in TDirectory.GetFiles(SrcDir, '*.bmp', TSearchOption.soTopDirectoryOnly) do
      begin
        // 画像をロードして強制的に 24bit ビットマップに変換
        im.LoadFromFile(FileName);
        im.PixelFormat := TPixelFormat.pf24bit;
        // データの生成
        var v: TBytes := [im.Width  and $00FF, im.Width  shr 8,
                          im.Height and $00FF, im.Height shr 8];
        var i := Length(v);
        SetLength(v, im.Width * im.Height * Succ(Ord(RGBType)) + Length(v));
        for var y := 0 to Pred(im.Height) do
          begin
            var Lines: PRGBTriple := im.ScanLine[y];
            for var x := 0 to Pred(im.Width) do
              begin
                case RGBType of
                  rgb8bit:
                    v[i] := ConvertToRGB332(Lines^);
                  rgb16bit:
                    begin
                      var value := ConvertToRGB565(Lines^);
                      v[i] := value shr 8;
                      Inc(i);
                      v[i] := value and $00FF;
                    end;
                end;
                Inc(i);
                Inc(Lines);
              end;
          end;
        // バイト列をファイルに保存
        var bs := TBytesStream.Create(v);
        try
          bs.SaveToFile(TPath.Combine(DstDir, TPath.GetFileName(FileName)));
        finally
          bs.Free
        end;
      end;
  finally
    im.Free;
  end;
end.

多くのファイルを一括置換するならこちらの方がオススメです。

See also:

おわりに

FireMonkey に移植すると macOS や Linux で使えて便利かもしれません。
image.png

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?