画像の透過処理・合成

画像を背景に重ねて描画する場合、透過処理を行うと背景に画像が馴染み見栄えが良くなります。

透過処理を行わない場合の例

without_alpha.png

透過処理を行った場合の例

with_alpha.png

透過処理の仕組み


上の例では文字Bの画像を透過処理させて背景画像の上に重ね合わせています。
文字B画像は描画データとは別にアルファチャンネルという透過処理に必要な情報を持っています。

アルファチャンネル


アルファチャンネルを使うことで描画したい画像の余分な部分をマスクしたり、部分的に透明度を変えて描画したりといったことができます。

文字B画像
letterB.jpg

文字B画像のアルファチャンネル
LetterB_alpha.jpg

アルファチャンネルは画像の各ピクセルに対して白から黒まで明暗だけで表現(グレースケール)しています。
上の例だと黒で全消去、白で全表示、灰色の部分は背景画像と合成されています。

透過処理のピクセル合成


画像の赤い四角で囲ったところを拡大していき、
with_alpha_spotted.png

各ピクセルがクッキリ見えるまで拡大したところです。
zoom_spotted.jpg

さらに四角で囲ったところのピクセルを例にして合成を考えてみます。
このピクセルは文字B画像の影の部分に相当しており、文字B画像の影の黒と背景のオレンジが混ざった色をしているのが分かります。

四角で囲ったピクセル位置の文字B画像の色をFg(フォアグラウンド)、背景画像の色をBg(バックグラウンド)とすると二つを合成した色Pは次の式で求まります。

P = Fg * α + Bg * (1 - α)

αはこのピクセルに該当するアルファ率です。
アルファ率とはたとえばアルファチャンネルの数値の濃淡が8bitで表現されているとするとその数値を255で割ったものがアルファ率となります。

下はFgを黒、Bgをオレンジとし、アルファ率を0.5としたとき、上式からRGB各成分について計算した色Pです。

  R成分 G成分 B成分
Fg black.jpg 0 0 0
Bg orange.jpg 255 150 0
P orange-black.jpg 127 75 0


マイコン用LCD画像表示バイナリをつくる


BMP画像をLCD画像表示用バイナリに変換するコマンド(bmp2lcd_rgb565)をつくりました。
入力が24bit BMPの場合はRGB565に変換し、8bitの場合はアルファチャンネルのグレースケールに変換します。

またNetpbmと組み合わせることによってPNG画像からLCD画像表示バイナリ、アルファチャンネルを出力できます。

  • PNG画像からRGB565のバイナリデータを出力する
pngtopnm infile.png | ppmtobmp -windows -bpp 24 | bmp2lcd_rgb565 > outfile.bin

  • PNG画像のアルファチャンネルから8bitグレースケールのバイナリデータを出力する
pngtopnm -alpha infile.png | ppmtobmp -windows -bpp 8 | bmp2lcd_rgb565 > outfile_alpha.bin


バイナリデータをアセンブリソースに埋め込む


出力したバイナリは.incbin疑似命令でアセンブリソースに埋め込む方法があります。
ChaNさんのサイトの32ビットへの誘いを参考にさせていただきました m(_ _)m

.section ".rodata"

.balign 2
.global play_icon_40x40
play_icon_40x40:
.incbin "play_icon_40x40.bin"
.global _sizeof_play_icon_40x40
.set _sizeof_play_icon_40x40, . - play_icon_40x40

.balign 2
.global play_icon_40x40_alpha
play_icon_40x40_alpha:
.incbin "play_icon_40x40_alpha.bin"
.global _sizeof_play_icon_40x40_alpha
.set _sizeof_play_icon_40x40_alpha, . - play_icon_40x40_alpha

.section ".text"


バイナリデータをLCDに出力する


16bit RGB565のピクセルフォーマット構造体


typedef struct{
  union{
       uint16_t d16;
       struct {
           uint16_t B : 5;
           uint16_t G : 6;
           uint16_t R : 5;
       };
   }color;
}pixel_fmt_typedef;


描画関数


/*
int startPosX: 描画開始X座標位置
int startPosY: 描画開始Y座標位置 
int width: 画像の幅 
int height: 画像の高さ
const uint16_t *d: 描画する画像へのポインタ
const uint8_t *a: 描画する画像のアルファチャンネルへのポインタ
*/
void LCDPutIcon(int startPosX, int startPosY, int width, int height, const uint16_t *d, const uint8_t *a)
{
   int i, j, x, y = startPosY;
   float alpha_ratio;
   pixel_fmt_typedef pixel_fg, pixel_bg;

#define F_INV_255 (1.0f / 255.0f) // アルファ率を求める係数

  for(i = 0;i < height;i++){
       x = startPosX;
       for(j = 0;j < width;j++){
           LCDSetGramAddr(x, y); // ピクセル描画位置指定
           LCDPutCmd(0x0022); // 読み書きコマンド発行
           LCD->RAM; // ダミーリード
           pixel_bg.color.d16 = LCD->RAM; // バックグラウンドカラーを取得
           pixel_fg.color.d16 = *d++; // フォアグラウンドカラーを取得
           alpha_ratio = *a++ * F_INV_255; // アルファチャンネルからアルファ率を算出

          // フォアグラウンドカラーの各色成分にアルファ率掛ける
           pixel_fg.color.R *= alpha_ratio;
           pixel_fg.color.G *= alpha_ratio;
           pixel_fg.color.B *= alpha_ratio;

          // バックグラウンドカラーの各色成分に(1 - アルファ率)掛ける
           pixel_bg.color.R *= (1.0f - alpha_ratio);
           pixel_bg.color.G *= (1.0f - alpha_ratio);
           pixel_bg.color.B *= (1.0f - alpha_ratio);

          // フォアグラウンドとバックグラウンドを合成
           pixel_fg.color.R += pixel_bg.color.R;
           pixel_fg.color.G += pixel_bg.color.G;
           pixel_fg.color.B += pixel_bg.color.B;

           LCDSetGramAddr(x, y); // ピクセル描画位置指定
           LCDPutCmd(0x0022); // 読み書きコマンド発行
           LCDPutData(pixel_fg.color.d16); // 合成した色を16bit出力
           x++;
       }
       y++;
   }
}

使用例


チェックパターンの背景に透過処理でミクを合成

extern const uint16_t miku_100x154[];
extern const uint8_t miku_100x154_alpha[];

LCDCheckPattern();
LCDPutIcon(100, 35, 100, 154, miku_100x154, miku_100x154_alpha);

miku_alpha.jpg

  • 最終更新:2013-06-17 00:13:48

このWIKIを編集するにはパスワード入力が必要です

認証パスワード