I have a TBitmap, inside the TBitmap there is load a "map image" (image of a map). This map image have a white background and a lot of black line (no antialising only 2 colors white background and black line).
Now I must do this:
Remove the white background from TBitmap (transparent background and black line);
If possible and only if possible replace black line color with another color;
Save the result as trasparent PNG image;
I don't have idea if these are possible.
Suggestions?
NOTE I want avoid to use 3th part of class or VCL if possible. I can use FreeImage library if need because I just use it on my project. I use Delphi XE3.
Change pixelformat to pf1Bit. Create a palette with 2 entries, change the values of the TPaletteEntry to the desired color value (in the shown exaple to red). Create a TPNGImage, assign the bitmap and set the transparency for the PNG.
implementation
uses pngimage;
{$R *.dfm}
Type
TMyPalette = Packed Record
palVersion : Word;
palNumEntries : Word;
palPalEntry : Array [0..1] of TPaletteEntry;
End;
Procedure ChangeBlackColor(bmp:TBitMap);
var
pal:TMyPalette;
begin
bmp.PixelFormat := pf1Bit;
bmp.HandleType := bmDIB;
With pal Do
Begin
palVersion:=$0300;
palNumEntries:=2;
palPalEntry[0].peRed:= $FF;
palPalEntry[0].peGreen:=$00;
palPalEntry[0].peBlue:= $00;
palPalEntry[0].peFlags:=PC_RESERVED;
palPalEntry[1].peRed:= $FF;
palPalEntry[1].peGreen:=$FF;
palPalEntry[1].peBlue:= $FF;
palPalEntry[1].peFlags:=PC_RESERVED;
End;
bmp.Palette := CreatePalette(pLogPalette(#pal)^)
end;
procedure TForm3.Button1Click(Sender: TObject);
var
png:TPngimage;
bmp:TBitmap;
begin
// sample image
Image1.Canvas.Rectangle(0,0,Image1.Width-1,Image1.Height-1);
Image1.Canvas.Ellipse(1,1,Image1.Width,Image1.Height);
bmp := Image1.Picture.Bitmap;
ChangeBlackColor(bmp);
png:=TPngimage.Create;
try
png.Assign(bmp);
png.TransparentColor := clWhite;
png.Transparent := true;
Image2.Picture.Assign(png);
finally
png.Free;
end;
end;
Related
Below is my sample code:
var lBitmap: TBitmap;
begin
lBitmap := TBitmap.Create;
lBitmap.PixelFormat := TPixelFormat.pf32bit;
lBitmap.Transparent := TRUE; // !
lBitmap.LoadFromFile( 'd:\temp\bmp32b_300dpi_transparent_400x250.bmp' );
// Bitmap RGB+Alpha created with GIMP
// modifications on pixels
Canvas.Draw(100, 0, lBitmap);
// Up to this point it is correct, the drawing is painted with transparency
lBitmap.SaveToFile( 'd:\tmp\after.bmp' ); // after this -> I have lost transparency
lBitmap.Free;
end;
How to use correctly TBitmap object to save a file with transparency?
It seems to me like TBitmap doesn't support saving bitmaps with alpha channels. And maybe we shouldn't blame the VCL for this, because BMPs with alpha transparency are uncommon. Many applications don't support transparent BMPs.
This being said, I "reverse-engineered" a BMP with alpha channel created in GIMP and wrote the following Delphi routine to produce the very same bitmap:
procedure SaveTransparentBitmap(ABitmap: TBitmap; const AFileName: string);
var
FS: TFileStream;
BFH: TBitmapFileHeader;
BIH: TBitmapV5Header;
y: Integer;
sl: PUInt64;
begin
// ABitmap MUST have the GIMP BGRA format.
FS := TFileStream.Create(AFileName, fmOpenWrite);
try
// Bitmap file header
FillChar(BFH, SizeOf(BFH), 0);
BFH.bfType := $4D42; // BM
BFH.bfSize := 4 * ABitmap.Width * ABitmap.Height + SizeOf(BFH) + SizeOf(BIH);
BFH.bfOffBits := SizeOf(BFH) + SizeOf(BIH);
FS.Write(BFH, SizeOf(BFH));
// Bitmap info header
FillChar(BIH, SizeOf(BIH), 0);
BIH.bV5Size := SizeOf(BIH);
BIH.bV5Width := ABitmap.Width;
BIH.bV5Height := ABitmap.Height;
BIH.bV5Planes := 1;
BIH.bV5BitCount := 32;
BIH.bV5Compression := BI_BITFIELDS;
BIH.bV5SizeImage := 4 * ABitmap.Width * ABitmap.Height;
BIH.bV5XPelsPerMeter := 11811;
BIH.bV5YPelsPerMeter := 11811;
BIH.bV5ClrUsed := 0;
BIH.bV5ClrImportant := 0;
BIH.bV5RedMask := $00FF0000;
BIH.bV5GreenMask := $0000FF00;
BIH.bV5BlueMask := $000000FF;
BIH.bV5AlphaMask := $FF000000;
BIH.bV5CSType := $73524742; // BGRs
BIH.bV5Intent := LCS_GM_GRAPHICS;
FS.Write(BIH, SizeOf(BIH));
// Pixels
for y := ABitmap.Height - 1 downto 0 do
begin
sl := ABitmap.ScanLine[y];
FS.Write(sl^, 4 * ABitmap.Width);
end;
finally
FS.Free;
end;
end;
This write a BITMAPFILEHEADER followed by a BITMAPV5HEADER and the pixel data in BGRA format.
I omit all kinds of error checking. For instance, I don't verify that ABitmap actually has the required format.
Test:
procedure TForm1.FormCreate(Sender: TObject);
var
bm: TBitmap;
begin
bm := TBitmap.Create;
try
bm.LoadFromFile('C:\Users\Andreas Rejbrand\Desktop\Test.bmp');
SaveTransparentBitmap(bm, 'C:\Users\Andreas Rejbrand\Desktop\Test2.bmp');
finally
bm.Free;
end;
end;
After this, Test.bmp and Test2.bmp are binary equal.
Saving 32-bit bitmaps with alpha channels needs a workaround as #Andreas Rejbrand has pointed out. There also seems to be some more confusion about the BMP file format, what the TBitmap.Transparent property does, and how you draw bitmaps transparently with the VCL.
32-bit bitmaps are the only bitmaps that carry information about transparency in the files. They have that information in the alpha channel and nowhere else. In the alpha channel, every pixel has its own 0-255 alpha value in the RGBA structure. This is often referred to as partial transparency.
When you draw/display 32-bit bitmaps, you have to pay attention to the TBitmap.AlphaFormat property. It defaults to afIgnore, which means that the bitmap is drawn without transparency. Use afPremultiplied or afDefined to draw with transparency. The latter is probably what you want.
The TBitmap.Transparent property is specific to the VCL TBitmap, and there is nothing in the BMP file format that corresponds to it. It's just a simple way to display bitmaps transparently, where a color defines which pixels should be fully transparent. The application must be familiar with the bitmaps to be able to use this method. It's also important to be aware of how the TBitmap.TransparentMode property works. It defaults to tmAuto, which sets the color of the bottom-leftmost pixel of the bitmap as TBitmap.TransparentColor. When TransparentMode is set to tmFixed, the TBitmap.TransparentColor you have specified is used. This method can also be used on 32-bit bitmaps.
Note that when you draw with the standard VCL TCanvas drawing routines on a 32-bit bitmap with transparency in the alpha channel, the transparency will be lost where you have drawn.
It seems that in your sample code, you have ignored AlphaFormat and TransparentMode. You should also decide if you want to use the transparency in the alpha channel or the TBitmap.Transparent method. But we have no bitmap in order to check if that is the real problem.
I have a TImageList which contains transparent icons (32bit, with alpha channel). What I want to do is to save individual icons based on image index as PNG file(s), while preserving alpha channel transparency. Using RAD Studio 2010 so it has TPngImage support, no need for third party libraries. Images are loaded into TImageList from PNG "sprite" image using the method here - Add a png image to a imagelist in runtime using Delphi XE - so the transparency is preserved upon loading. Now I need to save them out individually, in other words, extract individual images from sprite images which is already loaded into TImageList.
My code so far:
int imageindex = 123;
boost::scoped_ptr<TPngImage> png(new TPngImage);
boost::scoped_ptr<Graphics::TBitmap> bmp(new Graphics::TBitmap);
MyImageList->GetBitmap(imageindex, bmp.get()); // Using GetBitmap to copy TImageList image into separate TBitmap
png->Assign(bmp.get()); // Assign that bitmap to TPngImage
png->SaveToFile("C:\\filename.png");
The above works but it saves with the white background (transparency is not preserved after saving). I am probably missing a simple step but can't figure it out.
Delphi code is also welcome, shouldn't be hard to translate.
Yes, you can obtain PNG-image from TImageList where it was added. Code below allows you to do this!
Firstly, add PngImage to your uses clause.
procedure LoadPNGFromImageList(AImageList: TCustomImageList; AIndex: Integer; var ADestPNG: TPngImage);
const
PixelsQuad = MaxInt div SizeOf(TRGBQuad) - 1;
type
TRGBAArray = Array [0..PixelsQuad - 1] of TRGBQuad;
PRGBAArray = ^TRGBAArray;
var
ContentBmp: TBitmap;
RowInOut: PRGBAArray;
RowAlpha: PByteArray;
X: Integer;
Y: Integer;
begin
if not Assigned(AImageList) or (AIndex < 0) or
(AIndex > AImageList.Count - 1) or not Assigned(ADestPNG)
then
Exit;
ContentBmp := TBitmap.Create;
try
ContentBmp.SetSize(ADestPNG.Width, ADestPNG.Height);
ContentBmp.PixelFormat := pf32bit;
// Allocate zero alpha-channel
for Y:=0 to ContentBmp.Height - 1 do
begin
RowInOut := ContentBmp.ScanLine[Y];
for X:=0 to ContentBmp.Width - 1 do
RowInOut[X].rgbReserved := 0;
end;
ContentBmp.AlphaFormat := afDefined;
// Copy image
AImageList.Draw(ContentBmp.Canvas, 0, 0, AIndex, true);
// Now ContentBmp has premultiplied alpha value, but it will
// make bitmap too dark after converting it to PNG. Setting
// AlphaFormat property to afIgnored helps to unpremultiply
// alpha value of each pixel in bitmap.
ContentBmp.AlphaFormat := afIgnored;
// Copy graphical data and alpha-channel values
ADestPNG.Assign(ContentBmp);
ADestPNG.CreateAlpha;
for Y:=0 to ContentBmp.Height - 1 do
begin
RowInOut := ContentBmp.ScanLine[Y];
RowAlpha := ADestPNG.AlphaScanline[Y];
for X:=0 to ContentBmp.Width - 1 do
RowAlpha[X] := RowInOut[X].rgbReserved;
end;
finally
ContentBmp.Free;
end;
end;
Look at the picture. It is depicts what will happen if we set or not set such line of code:
ContentBmp.AlphaFormat := afIgnored;
Figure 1 is a result of setting afIgnored and the second one figure is a result of not setting afIgnored, allowing to use previously set afDefined.
Original image is an image named Figure 1
Using of code above in application:
procedure TForm1.aButton1Click(Sender: TObject);
var
DestPNG: TPngImage;
begin
DestPNG := TPNGImage.Create;
try
// Initialize PNG
DestPNG.CreateBlank(COLOR_RGBALPHA, 8, 60, 60);
// Obtain PNG from image list
LoadPNGFromImageList(ImageList1, 0, DestPNG);
// Output PNG onto Canvas
DestPNG.Draw(Canvas, Rect(0, 0, 60, 60));
DestPNG.SaveToFile('C:\MyPNGIcon.png');
finally
DestPNG.Free;
end;
end;
How to clear timage canvas to avoid duplicate image when changing input size? Why nil command doesn't work?
This is my code
begin
image1.Canvas := nil;
image1.Canvas.Pen.Color := clRed;
image1.Canvas.Brush.Color := clBlue;
image1.canvas.rectangle(10,10,vwpj,vwlb);
end;
You can't assign Nil or any value to Canvas, Canvas is a property for read only, so you need to remove the first line and then draw on the TImage canvas:
Image1.Canvas.Pen.Color := clRed;
Image1.Canvas.Brush.Color := clBlue;
Image1.canvas.rectangle(0,0,Image1.Height,Image1.Width);
Edit:
You have to set the image to default every time you draw on it's canvas:
Procedure:
Procedure TForm1.Default(Image: TImage);
begin
Image.Canvas.Pen.Color := clBtnFace;
Image.Canvas.Brush.Color := clBtnFace;
Image.Canvas.FillRect(Rect(0,0,Image.Height,Image.Width));
end;
Then call it as:
procedure TForm1.Button1Click(Sender: TObject);
begin
Default(Image1);
Image1.Canvas.Pen.Color := clRed;
Image1.Canvas.Brush.Color := clBlue;
Image1.canvas.rectangle(0,0,Image1.Height,Image1.Width);
end;
From what you have written and tried to explain in your many previous edits.
This is a possible solution to your problem.
Requirements:
Add four TEdit components to your form.
Add one TButton.
And one TImage.
Code:
var
Xorigin,Yorigin,vwpj,vwlb:integer;
....
begin
vwpj := strtoint(vwpjEdit.text);
vwlb := strtoint(vwlbEdit.text);
Xorigin := strtoint(XoriginEdit.Text);
Yorigin := strtoint(YoriginEdit.Text);
// You have to wipe the canvas with a base color,
image1.Canvas.Brush.Color := clwhite;
image1.Canvas.FillRect(rect(0,0,image1.Width,image1.height));
image1.Canvas.Pen.Color := clRed;
image1.Canvas.Brush.Color := clBlue;
image1.Canvas.rectangle(Xorigin,Yorigin,vwlb,vwpj);
end;
Explanation: I understand that you want to draw a rectangle on the Canvas property of a TImage. With the condition of each time you resize the rectangle you want to clear the Canvas (you implied this by assigning nil to canvas which is wrong considering that Canvas is a read only property).
Now the above code does this by filling the canvas with a base color (I chose clwhite) by using the Fillrect() method.
From this you need to understand that there is no such thing as clearing the image, either you delete it (using the free command as you say) and it will be gone and if you want to draw on it again you will need to create it.
the second option is that you fill it with a background color (the base clwhite I chose) or as a third option, resize the image as well.
All what maters is that as long as that image is still there the canvas and what you have drawn on it will remain.
Results of the code above
How to completely disable transparency of given PNGObject? By the way I am using PNGImage unit of Version 1.564.
I don't think it's possible to permanently disable TPNGObject image transparency. Or at least I couldn't find a property for doing this. And it should have been controlled by a property since when you assign or load an image, the TPNGObject takes the image parameters (including transparency) from the image file assigned.
So as a workaround I would prefer to use the RemoveTransparency procedure after when you load or assign the image:
uses
PNGImage;
procedure TForm1.Button1Click(Sender: TObject);
var
PNGObject: TPNGObject;
begin
PNGObject := TPNGObject.Create;
try
PNGObject.LoadFromFile('C:\Image.png');
PNGObject.RemoveTransparency;
PNGObject.Draw(Canvas, Rect(0, 0, PNGObject.Width, PNGObject.Height));
finally
PNGObject.Free;
end;
end;
For just drawing a TPNGObject (Delphi PNGComponents library) to some background color (in example: white) with alpha blending, try this:
uses
PNGImage, PNGFunctions;
procedure TForm1.Button1Click(Sender: TObject);
var png: TPNGObject;
bmp: TBitmap;
begin
try
// load PNG
png := TPNGObject.Create;
png.LoadFromFile('MyPNG.png');
// create Bitmap
bmp := TBitmap.Create;
bmp.Width := png.Width;
bmp.Height := png.Height;
// set background color to whatever you want
bmp.Canvas.Brush.Color := clWhite;
bmp.Canvas.FillRect(Rect(0, 0, png.Width, png.Height));
// draw PNG on Bitmap with alpha blending
DrawPNG(png, bmp.Canvas, Rect(0, 0, png.Width, png.Height), []);
// save Bitmap
bmp.SaveToFile('MyBMP.bmp');
finally
FreeAndNil(png);
FreeAndNil(bmp);
end;
end;
To use the DrawPNG procedure you have to include the PNGFunctions unit.
I have a requirement to draw a number to a image.That number will changes automatically.how can we create an image dynamically in Delphi 7 ?
.If any one knows please suggest me.
Yours Rakesh.
You can use the Canvas property of a TBitmap to draw a text in a image
check this procedure
procedure GenerateImageFromNumber(ANumber:Integer;Const FileName:string);
Var
Bmp : TBitmap;
begin
Bmp:=TBitmap.Create;
try
Bmp.PixelFormat:=pf24bit;
Bmp.Canvas.Font.Name :='Arial';// set the font to use
Bmp.Canvas.Font.Size :=20;//set the size of the font
Bmp.Canvas.Font.Color:=clWhite;//set the color of the text
Bmp.Width :=Bmp.Canvas.TextWidth(IntToStr(ANumber));//calculate the width of the image
Bmp.Height :=Bmp.Canvas.TextHeight(IntToStr(ANumber));//calculate the height of the image
Bmp.Canvas.Brush.Color := clBlue;//set the background
Bmp.Canvas.FillRect(Rect(0,0, Bmp.Width, Bmp.Height));//paint the background
Bmp.Canvas.TextOut(0, 0, IntToStr(ANumber));//draw the number
Bmp.SaveToFile(FileName);//save to a file
finally
Bmp.Free;
end;
end;
And use like this
procedure TForm1.Button1Click(Sender: TObject);
begin
GenerateImageFromNumber(10000,'Foo.bmp');
Image1.Picture.LoadFromFile('Foo.Bmp');//Image1 is a TImage component
end;