Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2008年08月30日] [更新 2019年08月27日] Delphi サンプルプログラム集
910_Glyph 用ビットマップの作成
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
910_DisableBitmap.zip [3,413 KB] 2019年08月27日版 (EXE 同梱)



  • 2014年06月30日
  • ビットマップの減色とグレイスケール化の記事を,新規にページを作成して移動
  • 2018年01月24日 (これより前の履歴は省略)
  • コンポーネントのアイコン作成支援プログラムに Glyph 用ビットマップ作成の機能を追加したので,関係記事とサンプルの掲載を中止




Glyph 用ビットマップと Disable 用のグレイスケールビットマップ


本ページで扱っている Glyph (グリフ) は,TSpeedButton, TBitBtn 等の Glyph プロパティのことです.この Glyph プロパティは TBitmap 型です.

Glyph のビットマップは,正方のビットマップを複数格納する形式になっていて,最大 4 つまで格納できます.Glyph にビットマップを設定すると TSpeedButton, TBitBtn 等に画像を表示できます.2 番目の画像があると,それらのコントロールの Enabled プロパティを False にした時の画像となります.2 番目のビットマップ画像が存在しない場合は Enabled プロパティの値を False にしても画像は表示されません.

下図は Delphi に付属している Glyph 用の 16 x 16 サイズのビットマップの例です.
透過処理はビットマップのデフォルトの設定で行われます.各々の正方のビットマップ画像の左下のピクセルの色が透過色となります.



図1
Delphi に付属の Glyph 用ビットマップ画像の例
図2
透過状態を確認した画像

[備考 1]
Delphi 7 等の旧い Delphi には各種の形式の画像が作成,編集可能なイメージエディタが付属していましたが,現在の Delphi にはこのようなイメージエディタは付属していません.

[備考 2]
TToolBar では,Glyph 用のビットマップを作成しなくても Images, DisabledImages, HotImages プロパティで個別のビットマップが設定できるようになっています.これらは TImageList 型のプロパティです.




01_Glyph 用ビットマップは正方サイズの画像を結合して作成する


前項の説明,あるいはオンラインヘルプの説明で分かるように,Glyph 用のビットマップ画像は,正方サイズのビットマップを複数結合したビットマップです.
結合する画像の数に応じた幅と高さのビットマップを作成して,そこに表示するビットマップを描画して作成します.以下はその例です.

このサンプルでは 4 つのビットマップ画像を結合した Glyph 用のビットマップを作成しています.元となるビットマップ画像はディスクから読み込みます.元の画像は 32 x 32 の 24 ビットのビットマップなので,作成した Glyph 用ビットマップは 128 x 32 のサイズとなります.
フォームには TBitBtn と TSpeedButton を各々 1 つづつ配置して,作成したビットマップを Glyph プロパティのビットマップとしています.

Glyph 用ビットマップの 4 番目の画像は TSpeedButton の Down プロパティ用ですが,このサンプルでは使われません.


[備考 1]
Glyph 用のビットマップに 2 番目の画像がないと Enabled プロパティが False の時に画像は表示されません.3, 4 番目の画像がない場合は,代わりに 1 番目の画像が表示されます.
3 番目の画像が無効の場合,4 番目の画像が有効でも 3, 4 番目共に無効となります.

[備考 2]
本テストで使用しているビットマップ画像は,添付の PNG 画像から以下のプログラムを使用して作成しています.


図3
設計時画面
  • [Glyph 作成] ボタンの下が TBitBtn
  • その下が TSpeedButton
図4
EXE を実行して [Glyph 作成] をクリック
  • 1 番目の画像が表示される
  • 右が作成した Glyph 用のビットマップ画像
  • 元の画像は 32 x 32 の 24 ビットのビットマップ
図5
TSpeedButton でマウスの左ボタンを押下した状態
  • 3 番目の画像が表示される
図6
マウスの左ボタンを離した状態
  • 1 番目の画像が表示される
図7
Enabled プロパティの値を False にした状態
  • 2 番目の画像が表示される

リスト1
Glyph 用ビットマップの作成
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, ExtCtrls;

type
  TForm1 = class(TForm)
    BitBtn1: TBitBtn;
    Button1: TButton;
    CheckBox1: TCheckBox;
    Image1: TImage;
    SpeedButton1: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  CheckBox1.Checked := True;
  Image1.Center := True;
end;

//=============================================================================
//  Glyph用ビットマップを作成して表示,保存
//  添付の4つのビットマップ画像から作成する
//  添付のビックマップ画像は 32x32の24ビットのビットマップ画像ファイル
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrList : TStringList;
  LGlyphBmp : TBitmap;
  LBitmap  : TBitmap;
  LWidh    : Integer;
  LHeight  : Integer;
  LIndex   : Integer;
  LRect    : TRect;
begin
  LWidh   := 0;
  LHeight := 0;

  LStrList  := TStringList.Create;
  LGlyphBmp := TBitmap.Create;
  LBitmap   := TBitmap.Create;
  try
    //Glyph用ビットマップ画像ファイルのパス名を設定
    LStrList.Add('EMOTION_1_UP.bmp');
    LStrList.Add('EMOTION_2_DISABLE.bmp');
    LStrList.Add('EMOTION_3_CLICK.bmp');
    LStrList.Add('EMOTION_4_DOWN.bmp');

    for LIndex := 0 to LStrList.Count - 1 do begin
      LBitmap.LoadFromFile(LStrList[LIndex]);
      if LBitmap.PixelFormat = pf32bit then begin
        LBitmap.AlphaFormat := afDefined;
        LBitmap.Transparent :=False;
      end else begin
        LBitmap.Transparent :=True;
      end;

      //最初のビットマップ画像の情報を取得
      //全てのビットマップのサイズは同じでなければならない
      if LIndex = 0 then begin
        LWidh   := LBitmap.Width;
        LHeight := LBitmap.Height;

        //Glyph用ビットのマップのサイズを決定
        LGlyphBmp.AlphaFormat := LBitmap.AlphaFormat;
        LGlyphBmp.Transparent := LBitmap.Transparent;
        LGlyphBmp.SetSize(LWidh * LStrList.Count, LHeight);
      end;

      //Glyph用ビットマップのCanvasに読み込んだビットマップ画像を描画する
      //左から順番にCopyRectメソッドを使用してコピー
      LRect := Rect(LWidh * LIndex, 0, LWidh * (LIndex + 1), LHeight);
      LGlyphBmp.Canvas.CopyRect(LRect,
                               LBitmap.Canvas,
                               Rect(0, 0, LBitmap.Width, LBitmap.Height));
    end;

    //TBitBtnの設定
    BitBtn1.NumGlyphs := LStrList.Count;
    BitBtn1.Glyph.Assign(LGlyphBmp);

    //TSpeedButtonの設定
    SpeedButton1.NumGlyphs := LStrList.Count;
    SpeedButton1.Glyph.Assign(LGlyphBmp);

    Image1.Picture.Assign(LGlyphBmp);

    //Glpy用ビットマップの保存
    LGlyphBmp.SaveToFile('emotion_glyph.bmp');
  finally
    FreeAndNil(LStrList);
    FreeAndNil(LGlyphBmp);
    FreeAndNil(LBitmap);
  end;
end;

//=============================================================================
//  Enabledプロパティの切り換え
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  BitBtn1.Enabled      := CheckBox1.Checked;
  SpeedButton1.Enabled := CheckBox1.Checked;
end;

end.




02_Glyph 用ビットマップと TSpeedButton の Down プロパティ


Glyph 用ビットマップの 4 番目の画像は TSpeedButton の Down プロパティ用です.

TSpeedButton の GroupIndex プロパティの値を 1 以上にすると,GroupIndex プロパティの値が同じボタンは,1 つだけが Down プロパティの値を True にできます.この機能により,どの TSpeedButton が押されているかを判定することが可能になります.同じ GroupIndex のボタンであれば,クリックしたボタンが自動的に Down プロパティが True になり,他のボタンは全て False になります.
Down プロパティの値が True の時に使われるのが Glyph 用ビットマップの 4 番目の画像です.

本サンプルはこのことを確認するためのテスト用です.
TSpeedButton を 2 つ配置し,どちらも GroupIndex の値を 1 にしています.  


[備考]
GroupIndex の値が 0 の TSpeedButton は Down プロパティの値を True にできません.
TBitBtn には Down プロパティはありません.


図8
TSpeedButton でマウスの左ボタンを押下した状態
  • 3 番目の画像が表示される
図9
マウスの左ボタンを離した状態
  • 4 番目の画像が表示される
  • 他の TSpeedButton には 1 番目の画像が表示される
図10
下の TSpeedButton をクリックした状態
  • 下のボタンには 4 番目の画像が表示される
  • その他のボタンには 1 番目の画像が表示される
図11
Enabled プロパティの値を False にした状態
  • Down プロパティの値に関係なく,2 番目の画像が表示される

Down プロパティは,ボタンが Down (ダウン) していることを意味しますが,上の図ではグラデーション描画となっていてフレームが太くなっているだけになっています.
下図はテーマを無効にして実行した結果ですが.テーマという概念がない Windows の時代には,実際に下図のように下がった状態になりました.



図12
テーマを無効にした時の Dwon プロパティが True のボタンの表示状態

リスト2
Glyph 用ビットマップと TSpeedButton の Down プロパティ
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    CheckBox1: TCheckBox;
    Image1: TImage;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  CheckBox1.Checked := True;
  Image1.Center := True;
end;

//=============================================================================
//  Glyph用ビットマップを作成して表示,保存
//  TSpeedButtonのDownプロパティの動作確認
//
//  添付の4つのビットマップ画像から作成する
//  添付のビックマップ画像は 32x32の24ビットのビットマップ画像ファイル
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrList  : TStringList;
  LGlyphBmp : TBitmap;
  LBitmap   : TBitmap;
  LWidh     : Integer;
  LHeight   : Integer;
  LIndex    : Integer;
  LRect     : TRect;
begin
  LWidh   := 0;
  LHeight := 0;

  //GroupIndexの値が1以上でないとDownプロパティは機能しない
  SpeedButton1.GroupIndex := 1;
  SpeedButton2.GroupIndex := 1;

  LStrList  := TStringList.Create;
  LGlyphBmp := TBitmap.Create;
  LBitmap   := TBitmap.Create;
  try
    //Glyph用ビットマップ画像ファイルのパス名を設定
    LStrList.Add('EMOTION_1_UP.bmp');
    LStrList.Add('EMOTION_2_DISABLE.bmp');
    LStrList.Add('EMOTION_3_CLICK.bmp');
    LStrList.Add('EMOTION_4_DOWN.bmp');

    for LIndex := 0 to LStrList.Count - 1 do begin
      //ビットマップ画像ファイルを読み込む
      LBitmap.LoadFromFile(LStrList[LIndex]);
      if LBitmap.PixelFormat = pf32bit then begin
        LBitmap.AlphaFormat := afDefined;
        LBitmap.Transparent :=False;
      end else begin
        LBitmap.Transparent :=True;
      end;

      //最初のビットマップ画像の情報を取得
      //全てのビットマップのサイズは同じでなければならない
      if LIndex = 0 then begin
        LWidh   := LBitmap.Width;
        LHeight := LBitmap.Height;

        //Glyph用ビットのマップのサイズを決定
        LGlyphBmp.AlphaFormat := LBitmap.AlphaFormat;
        LGlyphBmp.Transparent := LBitmap.Transparent;
        LGlyphBmp.SetSize(LWidh * LStrList.Count, LHeight);
      end;

      //Glyph用ビットマップのCanvasに読み込んだビットマップ画像を描画する
      //左から順番にCopyRectメソッドを使用してコピー
      LRect := Rect(LWidh * LIndex, 0, LWidh * (LIndex + 1), LHeight);
      LGlyphBmp.Canvas.CopyRect(LRect,
                               LBitmap.Canvas,
                               Rect(0, 0, LBitmap.Width, LBitmap.Height));
    end;

    //TSpeedButtonの設定
    //GroupIndexの値は0でない値にしないとDownプロパティは機能しない
    SpeedButton1.NumGlyphs  := LStrList.Count;
    SpeedButton1.Glyph.Assign(LGlyphBmp);

    SpeedButton2.NumGlyphs := LStrList.Count;
    SpeedButton2.Glyph.Assign(LGlyphBmp);

    Image1.Picture.Assign(LGlyphBmp);

    //Glpy用ビットマップの保存
    LGlyphBmp.SaveToFile('emotion_glyph.bmp');
  finally
    FreeAndNil(LStrList);
    FreeAndNil(LGlyphBmp);
    FreeAndNil(LBitmap);
  end;
end;

//=============================================================================
//  TSpeedButtonのEnabledプロパティの切り換え
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  SpeedButton1.Enabled := CheckBox1.Checked;
  SpeedButton2.Enabled := CheckBox1.Checked;
end;

end.




03_Glyph 用ビットマップとアルファチャンネル付きビットマップ


ここまでのサンプルでは 24 ビットのビットマップで Glyph 用ビットマップを作成しています.
Glyph 用のビットマップとしては,32 ビットのアルファチャンネル付きの半透明のビットマップ画像も使用できます.ただし,透明色は「黒」になります.また,半透明のピクセルは半透明ではなくなります.つまり,アルファチャンネルの値は無視されます.下図はその実際の結果です.使用している元のビットマップは 32 x 32 の 32 ビットのアルファチャンネル付きのビットマップです.



[備考]
TToolBar ではボタンを追加して使用します.このボタンのクラス名は TToolButton です.それらのボタンの画像は TImageList で設定します.Delphi 2009 以降の TmageList では,アルファチャンネル付きの PNG 画像やアルファチャンネル付きビットマップ画像も指定でき,TToolButton にアルファチャンネル付きの画像として描画できます.
Delphi 2009 からは TImage もアルファチャンネル付きの画像の描画が可能となっています.下図の結果でも,Glyph 用ビットマップの半透明ピクセルが半透明で描画されています.


図13
アルファチャンネルの値は無視される
  • 右の TImage では,アルファチャンネル付きの画像が正常に描画されている

32 ビットのビットマップを,以下のように 24 ビットのビットマップすれば,これまでのサンプルとほぼ同じ結果になります.24 ビットと 32 ビットのビットマップの違いは,アルファチャンネルを使用するかしないかの違いてす.
アルファチャンネルを使用したビットマップ画像には半透明のピクセルがあります.TBitmap.PixelFormat プロパティによる減色は簡易的なものです.画質を考慮するのであれば他の減色処理が必要です.


      //ビットマップ画像ファイルを読み込む
      LBitmap.LoadFromFile(LStrList[LIndex]);
      if LBitmap.PixelFormat = pf32bit then begin
        LBitmap.PixelFormat := pf24bit;
        LBitmap.Transparent := True;
      end else begin
        LBitmap.Transparent :=True;
      end;


図14
24 ビットのビットマップにした場合




04_アイコン画像編集ソフトを使用した Glyph 用ビットマップ作成


Glyph 用のビットマップ画像の 2 番目の画像は一般的にグレイスケールの画像です.
この Disable な画像としてのグレイスケールな画像も含めて,Glyph 用のビットマップ画像はアイコン画像の作成編集用のソフトでも作成できます.画像編集ソフトの多くには画像のグレイスケール化の機能が実装されています.

まず,元の画像を作成します.その画像をグレイスケール化した画像を作成します.必要に応じて明るさやコントラストを調整します.次に高さが同じで幅が元の画像の 2 倍の画像を新規に作成します.その画像に元の画像とグレイスケール化した画像をコピーして貼り付けます.
保存する時はビットマップ形式で保存します.

下記の GIGAZINE で紹介しているサイトからダウンロードしたアイコン集には,Disable な画像に使用できるグレイスケールのアイコンが含まれています.それらは PNG 形式の画像です.



図15
KH IconStudio を使用しても Glyph 用のビットマップ画像が作成できる

[備考]
手元の KH IconStudio では [グレイスケール 256 色] を選択してもグレイスケールにできませんでした.上の図は,[ツール] [色相・彩度・明度調整] のダイアログで [彩度] の値を 0 % にして作成しています.





05_実行時における Glyph プロパティの設定と取得と無効化


Glyph プロパティの型は TBitmap です.
したがって,例えば以下のようなコードで実行時に設定したり取得できます.

テストで使用したビットマップ画像は,上のサンプルで作成した Glyph 画像です.円の内部の一部が透過色と同じ色になってしまい,透過状態になっています.テスト使用したような単色に近い色で構成されていて,使用している色数が少ない画像の時はこのような現象が発生することがあります.
このような場合は背景を別の色で塗り潰せば透過しなくなります.背景のピクセルは必ずしも連続しているとは限らないので,自動で処理するのは難しそうです.



図16
設計時画面
  • TBitBtn と TImage を配置
図17
[Glyph 設定] ボタンクリック
  • Glyph 用ビットマップを読み込んで表示
図18
[Glyph 取得] ボタンをクリック
  • 取得したビットマップを TImage に表示
図19
Enabled プロパティの値を Flase にした状態
  • TBitBtn に Glyph の 2 番目のビットマップ画像が表示される
  • 2 番目の画像がない場合は,画像は表示されない
  • この画像の場合,円の内部の一部が透過色と同じ色になってしまい,透過している
図20
Glyph プロパティを無効にした状態

リスト3
Glyph プロパティを実行時に設定または取得あるいは無効にする
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    BitBtn1: TBitBtn;
    CheckBox1: TCheckBox;
    Image1: TImage;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  CheckBox1.Checked := True;
end;

//=============================================================================
//  Glyphプロパティを設定
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFilePath : string;
  LBitmap   : TBitmap;
begin
  LFilePath := ExpandFileName('..\Images\Test_Glyph.bmp');

  LBitmap := TBitmap.Create;
  try
    LBitmap.LoadFromFile(LFilePath);
    BitBtn1.Glyph.Assign(LBitmap);
  finally
    FreeAndNil(LBitmap);
  end;
end;

//=============================================================================
//  Glyphのビットマップを取得
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  Image1.Picture.Bitmap := BitBtn1.Glyph;
end;

//=============================================================================
//  Glyphプロパティのビットマップを削除
//  ボタンにGlyphの画像を表示しなくする
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
  BitBtn1.Glyph.Assign(nil);
end;

//=============================================================================
//  TBitBtnのEnabledプロパティの切り換え
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  BitBtn1.Enabled := CheckBox1.Checked;
end;

end.




06_TImageList を使用した Glyph 用ビットマップの設定


TImageList を使用する方法です.
設計時に TImageList に必要なビットマップ画像をセットしておいて,実行時に,それらの画像を取り出して Glyph 用のビットマップを作成します.この方法は,あらかじめ Glyph 用としてのビットマップを作成しておく必要がありません

最初の TImageList には通常表示用の画像を格納します.次の TImageList には Disable な状態を表示するための画像を格納します.このようにすると,TToolBar で TToolButton の画像を設定する方法と設計時の手順が同じになり,TToolBar で使用する TImageList と共有することも可能になります.また,TToolButton への移行も容易になります.

TImageList には PNG 形式,ICO 形式の画像も格納できますが,TBitBtn, TSpeedButton ではアルファチャンネル付きの画像は正常に描画できません.

Glyph 用のビットマップを作成しなくても,Enabled プロパティ等が変更された時に,TImageList から必要なビットマップ画像を Glyph プロパティに,以下のようにセットすることもできます.
このコードでは ImageList1 の Lindex 番目 (0 ベース) の画像を取得して SpeedButton.Glyph のビットマップとして設定します.


      ImageList1.GetBitmap(LIndex, SpeedButton1.Glyph);


図21
設計時画面
  • TSpeedButton を 2 つ配置
  • TImageList を 2 つ配置
図22
TImageList の設定
  • サイズと ColorDepth を使用するビットマップと同じにする

上の図で示すような設定をしたら,TImageList に必要なビットマップ画像をセットします.画像を読み込んでからサイズや色数 ( ColorDepth) を変更すると,読み込んだ画像が消去されてしまいます.



図23
ImaeList1 に通常表示用としてのビットマップ画像を読み込む
図24
ImaeList2 に Disable な画像としてグレイスケールのビットマップ画像をセットする

そして実行します.



図25
実行結果
図26
Enabled プロパティの値を False にした状態

リスト4
TImageList を使用して Glyph 用ビットマップを設定する
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, ExtCtrls, ImgList;

type
  TForm1 = class(TForm)
    CheckBox1: TCheckBox;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    ImageList1: TImageList;
    ImageList2: TImageList;
    procedure FormCreate(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    procedure SetGlyphFromTImageList(AImgListUp, AImgListDisable, AImgListClick,
      AImgListDown: TImageList; AButton: TObject; AImgIndex: Integer);
  public
    { Public 宣言 }
  end;

  procedure SetBitmapAlpha(ABitmap: TBitMap);

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//  TImageListに格納したビットマップをGlyph用のビットマップして使用する方法
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  CheckBox1.Checked := True;

  //背景色は元のビットマップにはない色にする
  ImageList1.BkColor := clBlue;
  ImageList2.BkColor := clBlue;

  SetGlyphFromTImageList(ImageList1, ImageList2, nil, nil, SpeedButton1, 0);
  SetGlyphFromTImageList(ImageList1, ImageList2, nil, nil, SpeedButton2, 1);
end;

//=============================================================================
//  TSpeedButtonのEnabledプロパティの切り換え
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  SpeedButton1.Enabled := CheckBox1.Checked;
  SpeedButton2.Enabled := CheckBox1.Checked;
end;

//-----------------------------------------------------------------------------
//  TImageListに格納されている画像からGlyph用のビットマップを作成してセットする
//
//  AImgListUp      : 通常表示する画像を格納したTImageList
//  AImgListDisable : Disableな状態を表す画像を格納したTImageList
//  AImgListClick   : マウスの左ボタン押下時に表示示する画像を格納したTImageList
//  AImgListDown    : DownプロパティがTrueの時に表示する画像を格納したTImageList
//  AButtton        : TSpeedButtonまたはTBitBtn
//  AImgIndex       : TimageListの何番目の画像を使用するかの指定(0ベース)
//-----------------------------------------------------------------------------
procedure TForm1.SetGlyphFromTImageList(AImgListUp, AImgListDisable, AImgListClick,
  AImgListDown: TImageList; AButton: TObject; AImgIndex: Integer);

  procedure CopyTImgListBmpToGlyphCanvas(AImgList : TImageList; AGlyph: TBitmap;
    ANo: Integer);
  var
    LBitmap : TBitmap;
    LRect   : TRect;
  begin
    if AImgList = nil then exit;

    LBitmap := TBitmap.Create;
    try
      // GetBitmap は 透明ピクセルを処理しない (TBitmap として取得する場合)
      // そこで TBitmap を完全に透明なビットマップにしておく
      if AImgList.ColorDepth = cd32Bit then begin
        LBitmap.AlphaFormat := afDefined;
        LBitmap.SetSize(AImgList.Width, AImgList.Height);
        SetBitmapAlpha(LBitmap);
      end;

      if AImgList.GetBitmap(AImgIndex, LBitmap) then begin
        //Glyph用ビットマップのCanvasにビットマップとして取得した画像を描画する
        LRect := Rect(LBitmap.Width * ANo, 0, LBitmap.Width * (ANo + 1), LBitmap.Height);
        AGlyph.Canvas.CopyRect(LRect,
                               LBitmap.Canvas,
                               Rect(0, 0, LBitmap.Width, LBitmap.Height));
      end;
    finally
      FreeAndNil(LBitmap);
    end;
  end;

var
  LGlphBmp  : TBitmap;
  LImgCount : Integer;
begin
  if AImgListUp = nil then exit;

  LGlphBmp := TBitmap.Create;
  try
    //Glyph用ビットマップのサイズを設定
    LImgCount := 1;
    if AImgListDisable <> nil then LImgCount := 2;
    if AImgListClick <> nil   then LImgCount := 3;
    if AImgListDown <> nil    then LImgCount := 4;
    LGlphBmp.SetSize(AImgListUp.Width * LImgCount, AImgListUp.Height);
    LGlphBmp.Canvas.FillRect(LGlphBmp.Canvas.ClipRect);

    //TImageListの画像をビットマップとして取得して,GlyphのCanvasに追加していく
    CopyTImgListBmpToGlyphCanvas(AImgListUp,      LGlphBmp, 0);
    CopyTImgListBmpToGlyphCanvas(AImgListDisable, LGlphBmp, 1);
    CopyTImgListBmpToGlyphCanvas(AImgListClick,   LGlphBmp, 2);
    CopyTImgListBmpToGlyphCanvas(AImgListDown,    LGlphBmp, 3);

    //Glyphプロパティを設定
    if AButton is TSpeedButton then begin
     TSpeedButton(AButton).Glyph :=  LGlphBmp;
    end;
    if AButton is TBitBtn then begin
     TBitBtn(AButton).Glyph :=  LGlphBmp;
    end;
  finally
    FreeAndNil(LGlphBmp);
  end;
end;

//----------------------------------------------------------------------------
//  透明なビットマップ画像の作成
//  アルファチャンネルのビットマップのピクセル値のクリア
//  Red,Gree, Blue, Alphi 値を全て0 にしたビットマップにする
//----------------------------------------------------------------------------
procedure SetBitmapAlpha(ABitmap: TBitMap);
type
  TRGBQArray = array [0..High(Integer) div 4 - 1] of RGBQUAD;
  PRGBQArray = ^TRGBQArray;
var
  LPosY      : Integer;
  LPosX      : Integer;
  LScanArray : PRGBQArray;
begin
  ABitmap.Canvas.Lock;
  ABitmap.PixelFormat := pf32bit;

  for LPosY := 0 to ABitmap.Height - 1 do begin
    LScanArray := ABitmap.ScanLine[LPosY];
    for LPosX  := 0 to ABitmap.Width - 1 do begin
      LScanArray[LPosX].rgbRed      := 0;
      LScanArray[LPosX].rgbGreen    := 0;
      LScanArray[LPosX].rgbBlue     := 0;
      LScanArray[LPosX].rgbReserved := 0;
    end;
  end;

  ABitmap.Canvas.Unlock;
end;

end.




07_単独の TSpeedButton で Down 状態のトグル機能


TSpeedButton の Down プロパティは GroupIndex の値が 1 以上の時に有効となります.
複数の TSpeedButton をグループ化して,その 1 だけを Down プロパティを True に,つまり押したままの状態にすることができます.以下のサンプルは,複数の TSpeedButton を配置している状態で各々の TSpeedButton 単独で Down プロパティを有効にする方法です.この動作は TCheckBox と同じように,クリックする度に状態が入れ換わるトグル機能です.

配置した各々の GroupIndex のそれぞれ異なる値にします.そして AllowAllUp の値を True にします.これらのプロパティの値はオブジェクト・インスペクタで設定できます.



[備考]
本サンプルでの TSpeedButton は,クリックする度に Down プロパティの値が自動で変わります.
TCheckBox はクリックする度にチェックマークを表示したり非表示にしますが,TSpeedButton では下がった状態と上がった状態で色が変わります.
上の方に図を掲載していますが,昔は本当に下がった状態になりました.


図27
サンプルの EXE を実行した画面<
  • 全ての TSpeedButto.Dow が False の状態
図28
全ての TSpeedButton を押した状態
  • 各々の TSpeedButton をクリックした結果
  • 全ての TSpeedButton.Down の値が True の状態
  • 右のボタンでも Down 状態のトグル操作が可能
図29
上の図の状態で真ん中の TSpeedButton をクリック

リスト5
TSpeedButton 単独で Down 状態のトグル機能を実現する
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Buttons, StdCtrls;

type
  TForm1 = class(TForm)
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  SpeedButton1.GroupIndex := 1;
  SpeedButton2.GroupIndex := 2;
  SpeedButton3.GroupIndex := 3;

  SpeedButton1.AllowAllUp := True;
  SpeedButton2.AllowAllUp := True;
  SpeedButton3.AllowAllUp := True;
end;

//=============================================================================
//  TSpeedButtonのクリックイベント
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  if TSpeedButton(Sender).Down then begin
    //DownプロパティがTrueになった時の処理
    //ボタンが下がった状態になった時の処理
  end else begin
    //DownプロパティがFalseになった時の処理
    //ボタンが上がった状態になった時の処理
  end;
end;

//=============================================================================
//  SpeedButton1のDown状態の変更
//  トグル操作
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  SpeedButton1.Down := not SpeedButton1.Down;
end;

//=============================================================================
//  SpeedButton2のDown状態の変更
//  トグル操作
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  SpeedButton2.Down := not SpeedButton2.Down;
end;

//=============================================================================
//  SpeedButton3のDown状態の変更
//  トグル操作
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
  SpeedButton3.Down := not SpeedButton3.Down;
end;

end.