How to create a solid mask from a semi-transparent Bitmap?

Asked

Viewed 1,792 times

13

I want to create the image on the right from the one on the left.

inserir a descrição da imagem aqui inserir a descrição da imagem aqui

And I have the following structure:

var
  Image, Mask : TBitmap;
begin
  Mask := TBitmap.Create;
  Image := TBitmap.Create;
  Image.LoadFromFile('Yoshi.png');

// Criação da máscara aqui

end; 

The result I seek must transform Mask in the image on the right to be shown in a form. Does anyone know what kind of method should I use? Mess directly with pixels? Or maybe there’s another way through canvas? Whoever has any idea, send it down.

  • 2

    I know very little about it, but semi-transparent images are images without background? I don’t know how Delphi recognizes that kind of pixel, and I haven’t even tested it here. But any pixel color other than the value recognized as background color (in the case of the transparent pixel) you substitute by the color you want the mask. Wouldn’t that be the way.. ?

  • I think if it is to manipulate at the pixel level, it would be there. But I also never dealt directly with pixels in Delphi. I expect some direction with this question. And if by chance someone has a less "coded" way of doing. I would like to know.

  • I tested here and soon the first pixel (0,0) was already recognized as white (#ffffff), so it would not be a perfect formula to try to change the color of everything other than clWhite.

  • You would need to get the Alpha value of each pixel. And preserve it if it is 0. But I don’t know how to access this value.

  • Well, it’s really not my thing. See if you can find something in this link. Good luck!

  • Hi. It’s been (a long time since I programmed in Delphi, so I won’t even try to prepare an answer. But @Tiago’s suggestion is the easiest way to do it. It’s called Limiarisation (Thresholding), because in practice you set a threshold value and turn the original image into binary making each pixel 0 if the original pixel is below the threshold and 255 if it is above. Here are some ways to access (bmp.Canvas.Pixels[x,y] seems the easiest): http://ksymeon.blogspot.com.br/2010/02/getdibits-vs-scanline-vs-pixels-in.html

  • I hope the links will help you, but if you can solve your question please don’t forget to add an answer yourself to serve as a future reference to those who need it most. :)

  • @Guill whatever the path used, try not to use 0 and 1 if possible, but the full alpha, otherwise the edges will be serrated.

  • @Guill, will the mask be used to make custom window format? Then you will probably have to convert into regions.

  • @Bacco No. The mask will simply be displayed.

Show 5 more comments

3 answers

10

Sorry for the delay to attend, I’ve been very busy. It seems that we have lacked a little research.

If the ideal is to work with PNG for the sake of transparency, then work with Bitmap is not correct.

In fact, from the Delphi 2009 with the introduction of Unicode it has become possible to work with several other types of images, including the PNG.

Here’s a question on Soen about the TPNGImage: how-do-i-get-pngs-to-work-in-d2009

@Guill, you yourself dealt with the Tpngimage type in your question:How to load semi-transparent PNG via a memory stream?.

Anyway, my humble method of treating that image:

procedure TMainForm.btnCriarMascaraClick(Sender: TObject);
var
  png: TPNGImage;
  x,y: TColor;
begin
  png := TPNGImage.Create;
  try
    png.Assign(imgOrig.Picture);

    for x := 0 to pred(png.Width) do
      for y := 0 to pred(png.Height) do
      begin
        if png.Pixels[x,y] <> png.TransparentColor then
        begin
          png.Pixels[x,y] := clLime;
        end;
      end;

    ImgDest.Picture.Assign(png);
  finally
    png.Free;
  end;
end;

Next up, the result!

Imagem tratada

It is necessary to have the unit pngimage. In XE3 is in Vcl.Imaging.pngimage. However, if you load the image at desing time, the Delphi IDE itself already adds a reference to it.

5


A possible solution:

var
  Image, Mask : TBitmap;
  i,j,Color   : LongWord;
  rowI, rowM  : pRGBQuadArray;

begin
   Color := $FF0000;

   Image := TBitmap.Create;
   Image.LoadFromFile('Yoshi.png');

   Mask := TBitmap.Create;
   Mask.PixelFormat := pf32bit;
   Mask.Width  := Image.Width;
   Mask.Height := Image.Height;
   FOR j := 0 TO Mask.Height-1 DO
   begin
      rowM := Mask.Scanline[j];
      rowI := Mask.Scanline[j];
      FOR i := 0 TO Mask.Width-1 DO
      begin       
         rowM[i] := ( rowI[i] and $FF000000 ) + Color;
      end;
   end;
   // ...use a máscara...
end;

It pays to include a test to make sure that Image is also pf32bit before creating the mask.

  • A caution: as I do not have the Xe here, I do not know if you have pRGBQuadArray. Anyway, it has to be an unsigned 32-bit type, test and tell me, I adjust the answer. If someone can test and give feedback, it will help.

  • I copied the structure pRGBQuadArray of this link in the Soen. However, the line rowM[i] := ( rowI[i] and $FF000000 ) + Color; does not compile and returns the message: "Operator not applicable to this operand type".

  • @James tries to change the INTEGER pro Rgbquad. Anything, if it doesn’t work, I adapt the line to treat the R G B A separately. The advantage if we manage to work this way is speed. It is much simpler to exchange for pixel operation, but the performance is low. Another thing is to avoid serration, because solutions with transparency 0 and 1 only have a bad finish.

  • sorry, I did not understand where to make this exchange of INTEGER pro RGBQuad.

  • @James misspelled, I used Longword in place of Integer, for being unsigned.

  • @Bacco sorry the absence. I will be able to test only tomorrow.

  • @Bacco, same message of James. I didn’t understand how to solve.

Show 2 more comments

-1

Try using the Canvas and Fillrect property.

Something like:

procedure CreateBitmapSolidColor(Width,Height:Word;Color:TColor;const FileName : TFileName);
var
 bmp : TBitmap;
begin
 bmp:=TBitmap.Create;
 try
   bmp.PixelFormat:=pf24bit;
   bmp.Width:=Width;
   bmp.Height:=Height;
   bmp.Canvas.Brush.Color := Color;
   bmp.Canvas.FillRect(Rect(0,0,Height, Width));
   bmp.SaveToFile(FileName);
 finally
   bmp.Free;
 end;
end;

https://stackoverflow.com/questions/5414929/how-i-create-bmp-files-bitmaps-of-a-single-color-using-delphi

  • 4

    Marcelo it is good practice to reference the answer that linked, can bring here (translate) part of it too?

  • 1

    @Marcelodeandrade, unfortunately, the only thing this Procedure does is create a square/rectangle with the color passed.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.