raff
raff

Reputation: 359

Make darker each pixel in a TImage

I have a TImage placed on my Form and set a PNG image into it.

Is it possible to change the opacity of each pixel of the PNG image in runtime? I want to change the opacity according to specific actions in my application.

I'm using the following code to make a pixel darker, the idea is to apply that function to each pixel in the TImage:

function DarkerColor(thisColor: TColor; thePercent: Byte): TColor;
var
  (* a TColor is made out of Red, Green and blue *)
  cRed,
  cGreen,
  cBlue: Byte;
begin
  (* get them individually *)
  cRed := GetRValue(thisColor);
  cGreen := GetGValue(thisColor);
  cBlue := GetBValue(thisColor);
  (* make them darker thePercent *)
  (* we need a byte value but the "/" operator
     returns a float value so we use Round function
     because type mismatch *)
  cRed := Round(cRed * thePercent / 100);
  cGreen := Round(cGreen * thePercent / 100);
  cBlue := Round(cBlue * thePercent / 100);
  (* return them as TColor *)
  Result := RGB(cRed, cGreen, cBlue);
end;

I have found how to access to each pixel in a BMP image. The thing is that I'm using a TImage with a PNG loaded in it.

Thanks!

Upvotes: 2

Views: 676

Answers (1)

Josef Švejk
Josef Švejk

Reputation: 1068

Just use ScanLine of your PNG-image to make it darker before assign it to TImagecomponent. It should be faster then convert TBitmap from TImage to PNG, perform actions and convert it back to TBitmap and assign it to TImage component again. Also, once you assigned PNG to TBitmap there is no way to get partial transparency of PNG back. At least, all my trying in past weren't successful, that's why I prefer to store PNG-files inside my small programs and change (sizing, blending etc.) their copy during run-time.

I changed your source code to exclude operations with floating point. It was replaced with MulDiv function.

function DarkerColor(thisColor: TColor; thePercent: Byte): TColor;
var
  (* a TColor is made out of Red, Green and blue *)
  cRed,
  cGreen,
  cBlue: Byte;
begin
  (* get them individually *)
  cRed := GetRValue(thisColor);
  cGreen := GetGValue(thisColor);
  cBlue := GetBValue(thisColor);

  (* make them darker thePercent *)
  cRed := MulDiv(cRed, thePercent, 100);
  cGreen := MulDiv(cGreen, thePercent, 100);
  cBlue := MulDiv(cBlue, thePercent, 100);

  (* return them as TColor *)
  Result := RGB(cRed, cGreen, cBlue);
end;  

Now you can go further and use ScanLine of your PNG-image:

procedure MakePNGDarker(APNGInOut: TPNGImage; AValue: Integer);
type
  TRGBArray = Array [0..65535 - 1] of WinAPI.Windows.tagRGBTRIPLE;
  PRGBArray = ^TRGBArray;
var
  RowInOut: PRGBArray;
  SourceColor: TColor;
  ResultColor: TColor;
  X: Integer;
  Y: Integer;
begin
  if not Assigned(APNGInOut) or (AValue < 0) then
    Exit;

  for Y:=0 to APNGInOut.Height - 1 do
    begin
      RowInOut := APNGInOut.ScanLine[Y];
      for X:=0 to APNGInOut.Width - 1 do
        begin
          SourceColor := RGB(RowInOut[X].rgbtRed, RowInOut[X].rgbtGreen, RowInOut[X].rgbtBlue);
          ResultColor := DarkerColor(SourceColor, AValue);

          RowInOut[X].rgbtRed := GetRValue(ResultColor);
          RowInOut[X].rgbtGreen := GetGValue(ResultColor);
          RowInOut[X].rgbtBlue := GetBValue(ResultColor);
        end;
    end;
end;  

Here is a result of the job that function done.
Fig. A stands for original image; Figures from B to F are present gradation of a modified images whose darkness was set with step of 25 through 0 to 100.

Note
Edges of colored images are blurred as these images were made with blurred edges. This is not a result of function work!

example

Useful resources

Upvotes: 4

Related Questions