开发者

Programmatically swap colors from a small bitmap (original), pixel by pixel

Download the source code with compiled executable here (Size: 161 KB (165,230 bytes)): http://www.eyeClaxton.com/download/delphi/ColorSwap.zip

Original bitmap size is just 28x15 pixels, and the color is light blue. I would like to be able to click on any of the colored panels to the right and change the original bitmap color from light blue to the color of the panel.

If you click on the gray panel you can see this in action, I just can't figure out how to do this correctly with the other colors. Any help would be greatly appreciated. If more information is needed, please feel free to ask.

I have asked this question before but I was unable to make clear what I was trying to do, so I hope this one is a little bit more clear.

Programmatically swap colors from a small bitmap (original), pixel by pixel

unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classe开发者_高级运维s, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls;

type
  TMainFrm = class(TForm)
    Panel1: TPanel;
    Label1: TLabel;
    Panel2: TPanel;
    Label2: TLabel;
    BeforeImage1: TImage;
    AfterImage1: TImage;
    Panel3: TPanel;
    Panel4: TPanel;
    Panel5: TPanel;
    Panel6: TPanel;
    Panel7: TPanel;
    Panel8: TPanel;
    Panel9: TPanel;
    Image1: TImage;
    Label3: TLabel;
    Panel10: TPanel;
    Memo1: TMemo;
    Label4: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Panel4Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainFrm: TMainFrm;

implementation

uses
  Math;

{$R *.DFM}
function Min(const A, B, C: Integer): Integer;
begin
  Result := Math.Min(A, Math.Min(B, C));
end;

function Max(const A, B, C: Integer): Integer;
begin
  Result := Math.Max(A, Math.Max(B, C));
end;

function RGBToGray(theRed, theGreen, theBlue: Byte): Byte;
begin
  Result := (Max(theRed, theGreen, theBlue) + Min(theRed, theGreen, theBlue)) div 2;
end;

function BlueToGray(theColor: TColor): TColor;
var
  R, G, B, X: Byte;
begin
  R := (theColor and $FF);
  G := (theColor and $FF00) shr 8;
  B := (theColor and $FF0000) shr 16;

  X := RGBToGray(R, G, B);
  Result := TColor(RGB(X, X, X));
end;

procedure TMainFrm.FormCreate(Sender: TObject);
begin
  Image1.Picture.Graphic := BeforeImage1.Picture.Bitmap;
end;

procedure TMainFrm.Panel4Click(Sender: TObject);
var
  Bitmap: TBitmap;
  I, X: Integer;
  Color: Integer;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.Assign(BeforeImage1.Picture.Bitmap);
    Panel4.Caption := '';
    Panel5.Caption := '';
    Panel6.Caption := '';
    Panel7.Caption := '';
    Panel8.Caption := '';
    Panel9.Caption := '';
    (Sender as TPanel).Caption := 'X';

    for X := 0 to (Bitmap.Height - 1) do
    begin
      for I := 0 to (Bitmap.Width - 1) do
      begin
        Color := Bitmap.Canvas.Pixels[I, X];

        case (Sender as TPanel).Tag of
          1: ; // I need a function something like BlueToRed(Color);
          2: ; // I need a function something like BlueToGreen(Color);
          3: ; // I need a function something like BlueToYellow(Color);
          4: ; // I need a function something like BlueToFuchsia(Color);
          5: ; // I need a function something like BlueToCyan(Color);
          6: Bitmap.Canvas.Pixels[I, X] := BlueToGray(Color);
        end;
        Image1.Picture.Graphic := Bitmap;
      end;
      Application.ProcessMessages();
      Sleep(100);
    end;
    AfterImage1.Picture.Graphic := Bitmap;
  finally
    Bitmap.Free;
  end;
end;

end.


The question is still not well-defined, because grey doesn't work like any other colour (neither in the RGB model, nor in the HSV model), so there is no single, obvious way to implement the other colour buttons.

However, one natural way (maybe the most natural way) is to do as I suggested in my answer to the previous question, namely to convert each pixel from HSV(h, s, v) to HSV(0, s, v) in the red case, HSV(120, s, v) in the green case, and HSV(240, s, v) in the blue case. The numbers 0, 120, and 240 are the angles of the hue.

To do this, you need only functions to convert between RGB and HSV (and I did give you those in the last question).

I see in your code that you have named the functions BlueToRed(Color) etc., which is inappropriate, because any colour will become red etc., so better names would be ColorToRed etc.

To make this as clear as possible, I have added the code for the red and green buttons in your program. See the updated version at

http://privat.rejbrand.se/ColorSwap.zip

(Also, please notice that "ColorSwap" is an inappropriate name. A better name would be "FixHue".)

Performance

Also, as you might have noticed, performance is terrible! It takes several seconds to colour the image!

This is not because the CPU is slow (indeed, it is extermely fast), but due to mainly two design bugs:

  1. Never update a pixmap on-screen. Instead, update the pixmap in memory, and then, when done, copy the bitmap to screen.

  2. Don't use the Pixels property. This is awkwardly slow. Use the Scanline instead.

You should be able to do a few hundred updates per second if you do it right...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜