I want to draw quite complex images with alpha-transparent rectangles and alpha-transparent images.
There is GDI+ wrapper from Mitov, but it doesn't seem to support 32bit BMP files plus it rescales them and the documentation is terrible. BMPs are way faster than PNGs so I want to use them.
There is SynGDI wrapper of GDI+, but it seems very basic and there is no documentation for it.
There is also this trick for GDI:
procedure DrawAlphaAPI(Source: TBitmap; Destination: TCanvas;
const X, Y: Integer; const Opacity: Byte = 255);
var BlendFunc: TBlendFunction;
begin
BlendFunc.BlendOp := AC_SRC_OVER;
BlendFunc.BlendFlags := 0;
BlendFunc.SourceConstantAlpha := Opacity;
if Source.PixelFormat = pf32bit then
BlendFunc.AlphaFormat := AC_SRC_ALPHA
else
BlendFunc.AlphaFormat := 0;
Windows.AlphaBlend(Destination.Handle, X, Y, Source.Width, Source.Height,
Source.Canvas.Handle, 0, 0, Source.Width, Source.Height, BlendFunc);
end;
But when I call it with Opacity = 255 it draws 32bit bitmaps not properly (something like they are half transparent where they should be fully).
I don't want to use Scanline to make pixels transparent as this will be too complicated to draw all the transparent rectangles this way.
Also I thin GDI+ should be faster on modern computers, am I right?
So the question is: how to draw an alpha transparent rectangle and bitmap easily (without tons of code)?
Preferred Delphi: 7. I also have 2005 and XE3 but since 7 is a speed demon I would most want something to work from 7 up.
If you prepare the ordinary TBitmap, any of the GDI+ implementations can be used just assigning bmp.Canvas.Handle.
Your problem in compiling might be caused by an old DirctDraw-Version in the Folder, just remove it.
implementation
uses GDIPAPI, GDIPOBJ;
{$R *.dfm}
Procedure PrepareBMP(bmp: TBitmap; Width, Height: Integer);
var
p: Pointer;
begin
bmp.PixelFormat := pf32Bit;
bmp.Width := Width;
bmp.Height := Height;
bmp.HandleType := bmDIB;
bmp.ignorepalette := true;
bmp.alphaformat := afPremultiplied;
// clear all Scanlines
p := bmp.ScanLine[Height - 1];
ZeroMemory(p, Width * Height * 4);
end;
procedure TForm2.Button1Click(Sender: TObject);
var
bmp: TBitmap;
G: TGPGRaphics;
B: TGPSolidBrush;
begin
bmp := TBitmap.Create;
try
PrepareBMP(bmp, 300, 300);
G := TGPGRaphics.Create(bmp.Canvas.Handle);
B := TGPSolidBrush.Create(MakeColor(100, 255, 0, 0));
try
G.SetSmoothingMode(SmoothingModeHighQuality);
G.FillEllipse(B, MakeRect(0.0, 0, 300, 300));
B.SetColor(MakeColor(100, 0, 255, 128));
G.FillEllipse(B, MakeRect(40.0, 40, 260, 260));
finally
B.Free;
G.Free;
end;
// draw overlapping in Form Canvas to display transparency
Canvas.Draw(0, 0, bmp);
Canvas.Draw(100, 100, bmp);
finally
bmp.Free;
end;
end;
Related
I'm doing some code in pascal using lazarus IDE v1.8.4, as the question says I need to be able to edit the console size in the code, I also preferably need to get the max possible console width they can have. If you do know how please also let me know the uses you.. used. Thanks!
Assuming you're targeting Windows:
Use GetLargestConsoleWindowSize to retrieve the largest possible console size depending on the console font and display settings,
Use SetConsoleScreenBufferSize to set the console screen buffer to the largest possible size,
Use SetConsoleWindowInfo to set the size and position of the console's window, so that no scrollbars would be visible by default etc..
At this point the console's window should be positioned as you've set. With my tests, however, while the window complies with the sizing request, the position is ignored.
In that case use any API function to move the window, the below examples uses SetWindowPos. I had to declare GetConsoleWindow as it was not declared in Lazarus 1.6.
program Project1;
{$APPTYPE CONSOLE}
uses
windows;
function GetConsoleWindow: HWND; stdcall external 'kernel32';
var
Con: THandle;
Size: TCoord;
Rect: TSmallRect;
Wnd: HWND;
begin
Con := GetStdHandle(STD_OUTPUT_HANDLE);
Size := GetLargestConsoleWindowSize(Con);
SetConsoleScreenBufferSize(Con, Size);
Rect.Left := -10;
Rect.Top := -10;
Rect.Right := Size.X - 11;
Rect.Bottom := Size.Y - 11;
SetConsoleWindowInfo(Con, True, Rect);
Wnd := GetConsoleWindow;
SetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOZORDER);
Readln;
end.
And don't forget to add error checking.
This seems to work fine in Lazarus for me on Win10Pro.
program ResizeConsoleWin;
{$APPTYPE CONSOLE}
uses
SysUtils,
Windows;
procedure SetConsoleWindowSize;
var
Rect: TSmallRect;
Coord: TCoord;
begin
Rect.Left := 1;
Rect.Top := 1;
Rect.Right := 300; // notice horiz scroll bar once the following executes
Rect.Bottom := 30;
Coord.X := Rect.Right + 1 - Rect.Left;
Coord.y := Rect.Bottom + 1 - Rect.Top;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), Coord);
SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), True, Rect);
end;
begin
SetConsoleWindowSize;
readln;
end.
It's copied from this answer with only the window dimensions changed.
I need to resize (png) images depending on the size of the screen. Currently I use the procedure below, but transparency is lost in conversion.
I'm sure there must be a more efficient way to achieve the same result, but I'm not that proficient with graphics.
procedure TFormMain.LoadAndSizeImagesForScreen(ImageFilename: string;
PngImage: TPngImage);
var
PngObject: TPngObject;
TempPngImage: TPngImage;
Bitmap: TBitmap;
Ratio: real;
begin
try
TempPngImage := TPngImage.Create;
TempPngImage.Transparent := True;
TempPngImage.TransparentColor := clWhite;
TempPngImage.LoadFromFile(ImageFilename);
//Determine the ration with which to increase/decrease image size
//BigScreenHeight is the base-line: 1050 currently.
Ratio := Screen.Height / BigScreenHeight;
//Create a bitmap to resize
Bitmap := TBitmap.Create;
//Resize the image to fit the target screen
Bitmap.Width := Round(TempPngImage.Width * Ratio);
Bitmap.Height := Round(TempPngImage.Height * Ratio);
Bitmap.Canvas.StretchDraw(Rect(0, 0, Bitmap.Width, Bitmap.Height), TempPngImage);
//Create a temporary object
PngObject := TPngObject.Create;
PngObject.Assign(Bitmap);
PngImage.Assign(PngObject);
finally
PngObject.Free;
TempPngImage.Free;
Bitmap.Free;
end;
end;
Is it possible to merge two or more different bmp-pictures of the same size into one by overlaying on top of each other? The same way it was done in Windows XP MS Paint: pasting one picture in another, with secondary color being transparent.
You can use Transparent property of TBitmap to that effect. Since your bitmaps have a black border, automatic transparent color (first pixel of image data) wouldn't work and you need to also set the TransparentColor property to 'clWhite'.
var
bmp1, bmp2: TBitmap;
begin
bmp1 := TBitmap.Create;
bmp1.LoadFromFile('...\test1.bmp');
bmp2 := TBitmap.Create;
bmp2.LoadFromFile('...\test2.bmp');
// bmp2.PixelFormat := pf24bit; // with 32 bit images I need this, don't know why
bmp2.Transparent := True;
bmp2.TransparentColor := clWhite;
bmp1.Canvas.Draw(0, 0, bmp2); // draw bmp2 over bmp1
// this is how the merged image looks like
Canvas.Draw(0, 0, bmp1);
..
In case of the second bitmap is black-and-white, you can use it as a mask in a raster operation with BitBlt ( bit-block transfer), as follows:
Windows.BitBlt(Bmp3.Canvas.Handle, 0, 0, Bmp3.Width, Bmp3.Height,
Bmp1.Canvas.Handle, 0, 0, SRCCOPY);
Windows.BitBlt(Bmp3.Canvas.Handle, 0, 0, Bmp3.Width, Bmp3.Height,
Bmp2.Canvas.Handle, 0, 0, SRCAND);
Question
What's the best way to draw a bitmap with a per-pixel alpha onto a control's Canvas?
My bitmap data is stored in a 2d array of 32-bit pixel values.
T32BitPixel = packed record
Blue : byte;
Green : byte;
Red : byte;
Alpha : byte;
end;
My control is a descendent of TCustomTransparentControl.
Background
For a GUI I'm building I need to draw semi-transparent controls over other controls and textured backgrounds. The control graphics are created using AggPasMod (a port of Anti-Grain Geometry).
TCustomTransparentControl.Canvas.Handle provides access to the device context for drawing but I'm not sure how to blit the pixel data from there.
Assuming, you have your pixel array composed like the image rows and pixels in them, I would do it this way. The Canvas parameter is a target canvas, the X and Y are coordinates, where the bitmap will be rendered in the target canvas and the Pixels is the pixel array:
type
TPixel = packed record
B: Byte;
G: Byte;
R: Byte;
A: Byte;
end;
TPixelArray = array of array of TPixel;
procedure RenderBitmap(Canvas: TCanvas; X, Y: Integer; Pixels: TPixelArray);
var
I: Integer;
Size: Integer;
Bitmap: TBitmap;
BlendFunction: TBlendFunction;
begin
Bitmap := TBitmap.Create;
try
Bitmap.PixelFormat := pf32bit;
Bitmap.Width := Length(Pixels[0]);
Bitmap.Height := Length(Pixels);
Size := Bitmap.Width * SizeOf(TPixel);
for I := 0 to Bitmap.Height - 1 do
Move(Pixels[I][0], Bitmap.ScanLine[I]^, Size);
BlendFunction.BlendOp := AC_SRC_OVER;
BlendFunction.BlendFlags := 0;
BlendFunction.SourceConstantAlpha := 255;
BlendFunction.AlphaFormat := AC_SRC_ALPHA;
AlphaBlend(Canvas.Handle, X, Y, Bitmap.Width, Bitmap.Height,
Bitmap.Canvas.Handle, 0, 0, Bitmap.Width, Bitmap.Height, BlendFunction);
finally
Bitmap.Free;
end;
end;
My task is:
Create a TBitmap object.
Fill it with transparent color (alpha = 0).
Assign this bitmap to TPngImage.
Save PNG file with alpha transparency.
How can I do it in Delphi XE?
var
Png: TPngImage;
X, Y: Integer;
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create();
Bitmap.PixelFormat := pf32bit;
Png := TPngImage.Create();
try
Bitmap.SetSize(100, 100);
// How to clear background in transparent color correctly?
// I tried to use this, but the image in PNG file has solid white background:
for Y := 0 to Bitmap.Height - 1 do
for X := 0 to Bitmap.Width - 1 do
Bitmap.Canvas.Pixels[X, Y]:= $00FFFFFF;
// Now drawing something on a Bitmap.Canvas...
Bitmap.Canvas.Pen.Color := clRed;
Bitmap.Canvas.Rectangle(20, 20, 60, 60);
// Is this correct?
Png.Assign(Bitmap);
Png.SaveToFile('image.png');
finally
Png.Free();
Bitmap.Free();
end;
end;
More or less a copy of Dorin's answer.
It shows how to make a transparent png image and how to clear the background.
uses
PngImage;
...
var
bmp: TBitmap;
png: TPngImage;
begin
bmp := TBitmap.Create;
bmp.SetSize(200,200);
bmp.Canvas.Brush.Color := clBlack;
bmp.Canvas.Rectangle( 20, 20, 160, 160 );
bmp.Canvas.Brush.Style := bsClear;
bmp.Canvas.Rectangle(1, 1, 199, 199);
bmp.Canvas.Brush.Color := clWhite;
bmp.Canvas.Pen.Color := clRed;
bmp.Canvas.TextOut( 35, 20, 'Hello transparent world');
bmp.TransparentColor := clWhite;
bmp.Transparent := True;
png := TPngImage.Create;
png.Assign( bmp );
png.SaveToFile( 'C:\test.png' );
bmp.Free;
png.Free;
end;