FineView Software Labs




コード断片、備忘録、etc..

Paint Shop Proの透過部分を描く

psp_clip.png
参考)Paint Shop Pro のスクリーンショット

// 透過部分のクロスストライプを描くコード
procedure CreateCrossStripesPSP(SrcBmp: TBitmap; Width, Height: Integer);
var
  i,j: Integer;
  Row: PByte;
begin
  SrcBmp.PixelFormat := pf24bit;
  SrcBmp.Width := Width;
  SrcBmp.Height := Height;

  for i:=0 to SrcBmp.Height-1 do
  begin
    Row := SrcBmp.ScanLine[i];
    for j:=0 to SrcBmp.Width-1 do
    begin
      if ((i xor j) and 16) = 0 then
      begin
        Row^ := 253; Inc(Row);
        Row^ := 253; Inc(Row);
        Row^ := 253; Inc(Row);
      end else begin
        Row^ := 203; Inc(Row);
        Row^ := 204; Inc(Row);
        Row^ := 203; Inc(Row);
      end;
    end;
  end;
end;

Paint Shop Proの背景を描く

// 背景(編集対称でないエリア)を描くコード
procedure CreateDiagCrossPSP(SrcBmp: TBitmap; Width, Height: Integer);
var
  i,j: Integer;
  Row: PByte;
begin
  SrcBmp.PixelFormat := pf24bit;
  SrcBmp.Width := Width;
  SrcBmp.Height := Height;

  for i:=0 to SrcBmp.Height-1 do
  begin
    Row := SrcBmp.ScanLine[i];
    for j:=0 to SrcBmp.Width-1 do
    begin
      if ((i + j) and $7=5) or ((i xor j) and $7=0) then
      begin
        Row^ := $C0; Inc(Row);
        Row^ := $C0; Inc(Row);
        Row^ := $C0; Inc(Row);
      end else begin
        Row^ := $FF; Inc(Row);
        Row^ := $FF; Inc(Row);
        Row^ := $FF; Inc(Row);
      end;
    end;
  end;
end;


DCT関連処理の高速化

2次元のデータ(画像)に対して、DCT係数F(u,v)を求める式は・・

dct.png

各係数の意味:
x,y は画素のインデックス、u,v はDCT係数のインデックス
Nx,Ny は扱う画像の大きさ。8x8ピクセルに対してDCT係数を求めるならNx=8,Ny=8
(JPEGの場合画像サイズに関係なく8x8, MCUが16x16であっても8x8)
f(x,y) は画素値(0〜255)
c(u), c(v) は次式

dct_cucv.png


F(u,v)の式で、c(u)とc(v)を掛け合わせている部分を 条件文や掛け算を使用せずに実現したのが以下のコード

// c(u) と c(v) を掛け合わせた値を求める処理
// 論理式とルックアップテーブルでc(u)*c(v)を求める
const
  uv_tbl: array[0..2] of double=(0.500000000, 0.707106781, 1.000000000);
var
  uv: double;
begin
  uv := uv_tbl[ ((u shr 31) or (-u shr 31)) + ((v shr 31) or (-v shr 31)) ];
end;


補足)
u,v ともに0なら c(u)*c(v) = 1/√2 * 1/√2 = 1/2 = 0.5
u,v のどちらか一方が0なら c(u)*c(v) = 1/√2 * 1 = 1/√2 = 0.707106781
u,v いずれも0以外なら c(u)*c(v) = 1

hoge = ((x shr 31) or (-x shr 31))

x=0 なら hoge=0
x<>0 なら hoge=1 (x>0 という条件のもとで)

よって、uv_tblへのアクセスパターンは次のとおり
uv_tbl[0 + 0]
uv_tbl[0 + 1] or uv_tbl[1 + 0]
uv_tbl[1 + 1]


サムネイル表示部などに収まりきらない長いファイル名を切り詰めるコード

// 長いファイル名を短く切り詰める関数
// ※ パスを含む場合に短く切り詰める場合は、MinimizeName()を使用すること
function MinceJPStr(filename: string; Canvas: TCanvas;
 canvas_width: Integer): string;
var
  org_filename: string;
  dot_width: Integer;
  len: Integer;
  cutpos: Integer;
begin
  Result := filename;

  if Canvas.TextWidth(filename) <= canvas_width then
    exit;

  org_filename := filename;

  dot_width := Canvas.TextWidth('...');

  while dot_width + Canvas.TextWidth(filename) > canvas_width do
  begin
    len := Length(filename);
    cutpos := len div 2;
    // 日本語の文字化け防止
    // マルチバイトの1バイト目、2バイト目をチェック
    case ByteType(filename, cutpos) of
      mbSingleByte:
        Delete(filename, cutpos, 1);
      mbLeadByte:
        Delete(filename, cutpos, 2);
      mbTrailByte:
        begin
          Dec(cutpos);
          Delete(filename, cutpos, 2);
        end;
    end;
  end;

  Result := Copy(filename, 1, cutpos-1) + '...' + Copy(filename,
 cutpos, Length(filename));
end;
補足)
中央から切り詰めるので[2010...の景色.jpg] といったように 拡張子を確認できるメリットがある。
後ろを切る詰めるタイプだと、[2010XXYY○○で...] と拡張子がわからない。

FFT関連処理の高速化

ビット反転テーブルを求める処理の高速化。

MakeBitRevTable() では、条件式(if文)を用いずにビット反転テーブルを作成。
ビット反転、そのものは、ビット逆転とビットシフトの合わせ技で実現。
type
  PIntArray = ^TIntArray;
  TIntArray = array[0..MaxInt div SizeOf(Integer)-1] of Integer;

// 2を基底とした対数を返却(= 引数が2の何乗かを調べる)
// 注)引数xが、2のべき乗の数であることを想定している。
function _log2(x: Integer): Integer;
begin
  Result := 0;
  while x>1 do
  begin
    x := x shr 1;
    Inc(Result);
  end;
end;

// ビット反転テーブルの作成
// bitTable: ビット反転テーブル
// n: テーブル長
procedure MakeBitRevTable(var bitTable: PIntArray; n: Integer);
var
  i: Integer;
  x: Cardinal;
  lx: Cardinal; // 2を基底とした対数
  maxbit: Cardinal;
begin
  // n-1 が最大何ビット使用するか?
  lx := _log2(n);
  maxbit := 32-lx;

  for i:=0 to n-1 do
  begin
    x := i;
    // ビット逆転
    x := ((x and $55555555) shl 1) or ((x and $AAAAAAAA) shr 1);
    x := ((x and $33333333) shl 2) or ((x and $CCCCCCCC) shr 2);
    x := ((x and $0F0F0F0F) shl 4) or ((x and $F0F0F0F0) shr 4);
    x := ((x shl 24) or ((x and $FF00) shl 8)) or
            (((x shr 8) and $FF00) or (x shr 24));
//    x := ((x and $00FF00FF) shl 8) or ((x and $FF00FF00) shr 8);
//    x := ((x and $0000FFFF) shl 16) or ((x and $FFFF0000) shr 16);

    // ビット反転(ビット逆転+シフトで実現)
    bitTable[i] := x shr maxbit;
  end;
end;

// 呼び出し側のサンプル
var
  N: Integer;
  bitTable: PIntArray;
begin
  N := 1024;

  // メモリ確保
  GetMem(bitTable, N*SizeOf(Integer));

  // ビット反転テーブルを作成
  MakeBitRevTable(bitTable, N);

         ・
         ・
         ・
end;

MakeBitRevTable() コメント部分は、以下のように変更しても同様の結果を得ることできる。
(やっていることは同じ。上で紹介したほうが高速。)
//    x := ((x shl 24) or ((x and $FF00) shl 8)) or
//            (((x shr 8) and $FF00) or (x shr 24));
    x := ((x and $00FF00FF) shl 8) or ((x and $FF00FF00) shr 8);
    x := ((x and $0000FFFF) shl 16) or ((x and $FFFF0000) shr 16);


数のループ − warp divergent 回避策?

やっていることは、Inc(x); if x>=hoge then x:=0; または、x:=(x+1) mod hoge; と同じこと。
// 0→1→0→1→0→1→0
    x := x xor 1;

// 0→1→2→0→1→2→0→1→2→0
    x := (x + 1 + (x shr 1)) and 3;

// 0→1→2→3→0→1→2→3→0→1→2→3→0
    x := (x + 1) and 3;

// 0→1→2→3→4→0→1→2→3→4→0
    x := (x + 1 + (x shr 2) * 3) and 7;

// 0→1→2→3→4→5→0→1→2→3→4→5→0
    x := ((x + 3) and 7) - 2;
    x := x * ((x shr 31) xor 1);

// 0→1→2→3→4→5→6→0→1→2→3→4→5→6→0
    x := ((x + 2) and 7) - 1;
    x := x * ((x shr 31) xor 1);

// 0→1→2→3→4→5→6→7→0→1→2→3→4→5→6→7→0
    x := (x + 1) and 7;

// 0→1→2→3→4→5→6→7→8→0→1→2→3→4→5→6→7→8→0
    x := (x + 1 + (x shr 3) * 7) and 15

            ・
            ・
            ・

// 0→1→ ・・ →98→99→0→1→ ・・ →98→99→0
    x := ((x + 29) and 127) - 28;
    x := x * ((x shr 31) xor 1);
CPUでは、条件式を敢えて論理式にするメリットはない。 ちなみに、剰余でやると7〜10倍遅くなる。

一応 C言語でも

0→1→2→3→4→5→0→1→2→3→4→5→0
// 0→1→2→3→4→5→0→1→2→3→4→5→0
#include <stdio.h>
#define LoopCnt 20

int main(void)
{
  int x=0;
  int i;

  for (i=0; i<LoopCnt; i++) {
    printf("%d\n", x);
    x = ((x+3) & 7)-2;
    x = x * (((unsigned long)x >> 31) ^ 1);
  }
  return 0;
}