简体   繁体   中英

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.

替代文字

unit MainUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, 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...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM