DIB画像処理で使用する関数や型定義(2)

ここまでコードを書いてきてひとつ思ったのですが、テーブルを使った簡単な処理は分割できますね。というわけで以下の型と関数を新しく定義しましょう。

type
  TByteTable = array[Byte]of Byte;

  procedure StdTableFilter(Bitmap: TBitmap; Table: TByteTable);

implementation

procedure StdTableFilter(Bitmap: TBitmap; Table: TByteTable);
//単純なテーブル変換を行う関数
var
  X, Y: Integer;
  pLine: PLine24;
begin
  Bitmap.PixelFormat := pf24bit;

  { ピクセルの変換処理 }
  for Y := 0 to Bitmap.Height -1 do
  begin
    pLine := Bitmap.ScanLine[Y];
    for X := 0 to Bitmap.Width -1 do
      with pLine^[X] do
      begin
        R := Table[R];
        G := Table[G];
        B := Table[B];
      end;
  end;
end;

ここまでやってきたことを少しまとめておきましょう。テーブル変換が使えるフィルタを書き換えるとユニットはこんな感じになっているはずです。

unit Filters;

interface

uses Windows, SysUtils, Graphics;

type
  PRGB24 = ^TRGB24;
  TRGB24 = packed record
    B: Byte;
    G: Byte;
    R: Byte;
  end;

  TLine24 = array[0..MaxInt div SizeOf(TRGB24) -1]of TRGB24;
  PLine24 = ^TLine24;

  TCacheLines = array[0..MaxInt div SizeOf(Pointer) -1]of Pointer;
  PCacheLines = ^TCacheLines;

  TOperator3   = array[-1..1, -1..1]of Integer;
  TMatrix3     = array[-1..1, -1..1]of TRGB24;
  TLineMatrix3 = array[-1..1]of PLine24;

  TByteTable = array[Byte]of Byte;

  //関数
  function MakeOperator3(X: array of Integer): TOperator3;
  function GetCacheLines(Source: TBitmap): PCacheLines;
  procedure CopyDIB(Source, Dest: TBitmap);
  procedure StdTableFilter(Bitmap: TBitmap; Table: TByteTable);

  procedure NegaPosi(Bitmap: TBitmap);
  procedure Solarization(Bitmap: TBitmap);
  procedure Grayscale(Bitmap: TBitmap);
  procedure Gamma(Bitmap: TBitmap; Value: Double);
  procedure Brightness(Bitmap: TBitmap; Value: Integer);

implementation

uses Math;

resourcestring
  EMes_InvalidGraphic = 'ビットマップが不正です。';

function MakeOperator3(X: array of Integer): TOperator3;
// オペレーターを作成
var
  I, MX, MY, Count: Integer;
begin
  FillChar(Result, SizeOf(Result), 0);
  Count := High(X);
  if Count = -1 then Exit;
  I := 0;
  for MX := -1 to 1 do
    for MY := -1 to 1 do
    begin
      Result[MX, MY] := X[I];
      Inc(I);
      if I > Count then Break;
    end;
end;

function GetCacheLines(Source: TBitmap): PCacheLines;
// ビットマップのスキャンラインをキャッシュする
var
  Y: Integer;
begin
  if (Source = nil) or Source.Empty then
    raise EInvalidGraphicOperation.CreateRes(@EMes_InvalidGraphic);
  with Source do
  begin
    GetMem(Result, SizeOf(Pointer) * Height);
    try
      for Y := 0 to Height -1 do
        Result^[Y] := ScanLine[Y];
    except
      FreeMem(Result);
      raise;
    end;
  end;
end;

procedure CopyDIB(Source, Dest: TBitmap);
// DIBをコピーする
var
  Y, Bytes: Integer;
begin
  if (Source = nil) or Source.Empty or (Dest = nil) then
    raise EInvalidGraphicOperation.CreateRes(@EMes_InvalidGraphic);
  Source.PixelFormat := pf24bit;
  Dest.PixelFormat   := pf24bit;
  Dest.Width  := Source.Width;
  Dest.Height := Source.Height;
  Bytes := BytesPerScanline(Source.Width, 24, 32);
  for Y := 0 to Source.Height -1 do
    Move(Source.ScanLine[Y]^, Dest.ScanLine[Y]^, Bytes);
end;

procedure StdTableFilter(Bitmap: TBitmap; Table: TByteTable);
//単純なテーブル変換を行う関数
var
  X, Y: Integer;
  pLine: PLine24;
begin
  Bitmap.PixelFormat := pf24bit;

  { ピクセルの変換処理 }
  for Y := 0 to Bitmap.Height -1 do
  begin
    pLine := Bitmap.ScanLine[Y];
    for X := 0 to Bitmap.Width -1 do
      with pLine^[X] do
      begin
        R := Table[R];
        G := Table[G];
        B := Table[B];
      end;
  end;
end;

procedure NegaPosi(Bitmap: TBitmap);
// ネガポジ反転
var
  X, Y: Integer;
  pLine: PLine24;
begin
  Bitmap.PixelFormat := pf24bit;
  for Y := 0 to Bitmap.Height -1 do
  begin
    pLine := Bitmap.ScanLine[Y];
    for X := 0 to Bitmap.Width -1 do
      with pLine^[X] do
      begin
        R := R xor $FF;
        G := G xor $FF;
        B := B xor $FF;
      end;
  end;
  if Assigned(Bitmap.OnChange) then
    Bitmap.OnChange(Bitmap);
end;

procedure Solarization(Bitmap: TBitmap);
// ソラリゼーション
var
  X, Y: Integer;
  Table: TByteTable;
begin
  { 変換テーブルを生成 }
  for X := 0 to 255 do
  begin
    //Table[X] := Min(X, X xor $FF);
    Y := X xor $FF;
    if X < Y then Y := X;
    Table[X] := Y;
  end;

  //ピクセルの変換処理
  StdTableFilter(Bitmap, Table);

  if Assigned(Bitmap.OnChange) then
    Bitmap.OnChange(Bitmap);
end;

procedure Grayscale(Bitmap: TBitmap);
//グレイスケール
var
  X, Y, Gray: Integer;
  pLine: PLine24;
begin
  Bitmap.PixelFormat := pf24bit;
  { ピクセルの変換処理 }
  for Y := 0 to Bitmap.Height -1 do
  begin
    pLine := Bitmap.ScanLine[Y];
    for X := 0 to Bitmap.Width -1 do
      with pLine^[X] do
      begin
        Gray := Round((R * 30 + G * 59 + B * 11) / 100);
        { 0..255の範囲に飽和(もしかすると必要ないかも(^^;) }
        if Gray > 255 then Gray := 255
        else if Gray < 0 then Gray := 0;
        R := Gray;
        G := Gray;
        B := Gray;
      end;
  end;
  if Assigned(Bitmap.OnChange) then
    Bitmap.OnChange(Bitmap);
end;

procedure Gamma(Bitmap: TBitmap; Value: Double);
//ガンマ補正
var
  X, Y: Integer;
  Table: TByteTable;
begin
  { 変換テーブルの作成 }
  Value := Value / 2.2;
  for Y := 0 to 255 do
  begin
    X := Round(Power(Y / 255, Value) * 255);
    if X > 255 then X := 255 else if X < 0 then X := 0;
    Table[Y] := X;
  end;

  //ピクセルの変換処理
  StdTableFilter(Bitmap, Table);

  if Assigned(Bitmap.OnChange) then
    Bitmap.OnChange(Bitmap);
end;

procedure Brightness(Bitmap: TBitmap; Value: Integer);
//明るさ補正
var
  X, Y: Integer;
  Table: TByteTable;
begin
  if (Value = 0) or (Value > 255) or (Value < -255) then Exit;
  { 変換テーブルの作成 }
  for Y := 0 to 255 do
  begin
    X := Y + Value;
    if X > 255 then X := 255 else if X < 0 then X := 0;
    Table[Y] := X;
  end;

  //ピクセルの変換処理
  StdTableFilter(Bitmap, Table);

  if Assigned(Bitmap.OnChange) then
    Bitmap.OnChange(Bitmap);
end;

end.


Copyright 2001 Rinka Kouzuki All Rights Reserved.