Writing Transparent Text on Image
Solution 1
I presume what you're trying to accomplish is a little more complicated than simply writing text with a transparent background; i.e. you are trying to get some form of alpha-blended text written on the image.
The simplest method would be to make use of the GDI+ routines. They are encapsulated for delphi and available for download from http://www.progdigy.com/. There are many examples there which should be usable as an example.
Solution 2
One option is to use the AlphaBlend function in Windows.pas unit. Something like this will produce semi-transparent text (with a drop shadow - building on Jim McKeeth's response) overlayed on an image:
uses Windows, Graphics;
.
.
.
var
BackgroundImage: Graphics.TBitmap; { need to call out specifically for Graphics.TBitmap
because the Windows unit also has a TBitmap
declaration }
TextImage: Graphics.TBitmap;
BlendFunc: BLENDFUNCTION;
begin
BlendFunc.BlendOp := AC_SRC_OVER;
BlendFunc.BlendFlags := 0;
BlendFunc.SourceConstantAlpha := $C0; { a hex value from $00-$FF (0-255).
Represents the percent of opaqueness:
$00 is completely transparent,
$FF is completely opaque.
$C0 is 75% opaque }
BlendFunc.AlphaFormat := AC_SRC_ALPHA;
{ BackgroundImage is for holding the image you want to overlay text onto }
BackgroundImage := Graphics.TBitmap.Create;
try
BackgroundImage.LoadFromFile('yourimagehere.bmp');
{ Create another TBitmap to hold the text you want to overlay }
TextImage := Graphics.TBitmap.Create;
try
{ Set this bitmap to have the same dimensions as the
background image you want the text to appear on. }
TextImage.Height := BackgroundImage.Height;
TextImage.Width := BackgroundImage.Width;
{ In my limited experience with AlphaBlend, Black is always 100%
transparent. So, paint TextImage completely Black. Play around
with this to see the effect it has on the final outcome. }
TextImage.Canvas.Brush.Color := clBlack;
TextImage.Canvas.FloodFill(0, 0, clNone, fsBorder);
TextImage.Canvas.Font.Style := [fsBold];
{ Write the shadow first }
TextImage.Canvas.Brush.Style := bsClear;
TextImage.Canvas.Font.Color := clDkGray;
TextImage.Canvas.TextOut(11, 11, 'Test');
{ Then put the text on top (slightly offset) }
TextImage.Canvas.Brush.Style := bsClear;
TextImage.Canvas.Font.Color := clMaroon;
TextImage.Canvas.TextOut(10, 10, 'Test');
{ Use the AlphaBlend function to overlay the bitmap holding the text
on top of the bitmap holding the original image. }
Windows.AlphaBlend(BackgroundImage.Canvas.Handle, 0, 0,
TextImage.Width, TextImage.Height,
TextImage.Canvas.Handle, 0, 0, TextImage.Width,
TextImage.Height, BlendFunc);
{ Assign the now updated BackgroundImage to a TImage control for display }
Image1.Picture.Bitmap.Assign(BackgroundImage);
finally
TextImage.Free;
end;
finally
BackgroundImage.Free;
end;
end;
Solution 3
The shadow is easy:
// Bold shows up better when over an image
image1.Canvas.Font.Style := [fsBold];
// Write the shadow first
image1.Canvas.Brush.Style:=bsClear;
image1.Canvas.Font.Color := clGrayText;
image1.Canvas.TextOut(1, 1, 'hi there');
// Then put the text on top (slightly offset)
image1.Canvas.Brush.Style:=bsClear;
image1.Canvas.Font.Color :=clBlack;
image1.Canvas.TextOut(0, 0, 'hi there');
This is text with a transparent background. Or did you want the text itself to be simi-transparent? That is a little trickier. You would need to draw it manually. An easy way to do it instead would be to sample the average of the color of the area you are writing on the image. Then set your font color to be a little lighter and your shadow to be a little darker. Then it kind of blends in.
Solution 4
i haven't tested it but it'll give you some idea where to go. the key is the brush style.
something like this:
img.Canvas.Brush.Style:=bsClear;
img.Canvas.Font.Color:=clBlack;
img.Canvas.TextOut(0, 0, 'hi there');
Solution 5
This function is based on Dave Elsberry's idea.
What's different:
- Draws only the shadow transparently
- It uses almost 2 times less RAM
- Parameters
{-------------------------------------------------------------------------------------------------------------
DrawTextShadowBox
Draws text in a semi-transparent rectangle with shadow text.
The shadow text is blended to the background and then blurred.
Variant:
1: Draws text in a box that is as wide as the BMP and can be aligned to top or bottom
2: Draws text in a box that is as wide as text and is placed into the image at coordinates x,y
Parameters:
Opacity a value from 0-255. 0 => Shadow is completelly transparent
To set the Font color/size, the caller should do: aCanvas.Font.Size:= x
Issues:
The blurring function cuts too suddenly. The rectangle that was blurred is too visible. Do a blur that slowly fades at the edges.
Might be slow becuase of the alpha blending and because of the blur.
Important!
The input img must be pf24bit.
When the AlphaFormat member is AC_SRC_ALPHA, the source bitmap must be 32 bpp. If it is not, the AlphaBlend function will fail.
-------------------------------------------------------------------------------------------------------------}
procedure DrawTextShadowBox(BMP: TBitmap; CONST Text: string; AlignTop: Boolean; ShadowColor: TColor= clTextShadow; ShadowOpacity: Byte= 20; Blur: Byte= 2);
VAR
Shadow: Vcl.Graphics.TBitmap;
BlendFunc: BLENDFUNCTION;
x, y: Integer;
BmpRect: TRect; { Rectangle in the original bitmap where we want to draw the shadowed text }
ShadowRect: TRect;
TextWidth, TextHeight: Integer;
OriginalColor: TColor;
begin
Assert(BMP.PixelFormat= pf24bit, 'Wrong pixel format!!');
OriginalColor:= bmp.Canvas.Font.Color;
TextWidth := BMP.Canvas.TextWidth (Text);
TextHeight:= BMP.Canvas.TextHeight(Text);
{ Write the shadow on a separate bitmap (overlay) }
Shadow := TBitmap.Create;
TRY
{ Bitmap setup }
Shadow.Canvas.Font.Assign(BMP.Canvas.Font);
Shadow.PixelFormat:= pf24bit;
Shadow.SetSize(BMP.Width, TextHeight);
{ Bitmap rectangle as big as ShadowBMP }
ShadowRect.Left:= 0;
ShadowRect.Top := 0;
ShadowRect.Right := Shadow.Width;
ShadowRect.Bottom:= Shadow.Height;
{ Fill shadow rectangle }
Shadow.Canvas.Brush.Color := clBlack; { In AlphaBlend, Black is always 100% transparent. So, paint Shadow completely Black. }
Shadow.Canvas.FillRect(ShadowRect);
BmpRect.Left := 0;
BmpRect.Right := Shadow.Width;
if AlignTop
then BmpRect.Top := 0
else BmpRect.Top := BMP.Height- TextHeight;
BmpRect.Bottom:= BmpRect.Top+ TextHeight;
{ Blend rectangle with orig image } { Use the AlphaBlend function to overlay the bitmap holding the text on top of the bitmap holding the original image. }
BlendFunc.BlendOp := AC_SRC_OVER;
BlendFunc.BlendFlags := 0;
BlendFunc.SourceConstantAlpha := ShadowOpacity;
BlendFunc.AlphaFormat := 0; //AC_SRC_ALPHA; // if I put this back, the shadow will be completly invisible when merged with a white source image
WinApi.Windows.AlphaBlend(BMP.Canvas.Handle, BmpRect.Left, BmpRect.Top, BmpRect.Right, TextHeight, Shadow.Canvas.Handle, 0, 0, Shadow.Width, Shadow.Height, BlendFunc);
{ Copy the blended area back to the Shadow bmp }
Shadow.Canvas.CopyRect(ShadowRect, BMP.Canvas, BmpRect);
{ Diagonal shadow }
x:= (BMP.Width - TextWidth) DIV 2; // Find center
Shadow.Canvas.Brush.Style:= bsClear;
Shadow.Canvas.Font.Color := ShadowColor;
Shadow.Canvas.TextOut(x, 0, Text);
{ Blur the shadow }
janFX.GaussianBlur(Shadow, Blur, 1);
{ Paste it back }
BMP.Canvas.CopyRect(BmpRect, Shadow.Canvas, ShadowRect);
FINALLY
FreeAndNil(Shadow);
END;
{ Draw actual text at 100% opacity }
if AlignTop
then y := 0
else y := BMP.Height- TextHeight;
BMP.Canvas.Brush.Style:= bsClear;
BMP.Canvas.Font.Color := OriginalColor;
BMP.Canvas.TextOut(x, y, Text);
end;
procedure DrawTextShadowBox(aCanvas: TCanvas; CONST Text: string; X, Y: Integer; ShadowColor: TColor= clTextShadow; ShadowOpacity: Byte= 20; Blur: Byte= 2);
VAR
Shadow: Vcl.Graphics.TBitmap;
BlendFunc: BLENDFUNCTION;
H, W: Integer;
OriginalColor: TColor;
R, R2: TRect;
CONST Edge= 5;
begin
OriginalColor:= aCanvas.Font.Color;
{ Write the shadow on a separate bitmap (overlay) }
Shadow := TBitmap.Create;
TRY
{ Assign font }
Shadow.Canvas.Font.Assign(aCanvas.Font);
Shadow.PixelFormat:= pf24bit;
{ Compute overlay size }
W:= Shadow.Canvas.TextWidth (Text);
H:= Shadow.Canvas.TextHeight(Text);
Shadow.SetSize(W, H);
{ Fill shadow rectangle }
R:= Rect(0, 0, Shadow.Width, Shadow.Height);
Shadow.Canvas.Brush.Color := clBlack; { In AlphaBlend, Black is always 100% transparent. So, paint Shadow completely Black. }
Shadow.Canvas.FillRect(R);
{ Blend rectangle with orig image } { Use the AlphaBlend function to overlay the bitmap holding the text on top of the bitmap holding the original image. }
BlendFunc.BlendOp := AC_SRC_OVER;
BlendFunc.BlendFlags := 0;
BlendFunc.SourceConstantAlpha := ShadowOpacity;
BlendFunc.AlphaFormat := 0; //AC_SRC_ALPHA; // if I put this back, the shadow will be completly invisible when merged with a white source image
WinApi.Windows.AlphaBlend(aCanvas.Handle, x, y, Shadow.Width, Shadow.Height, Shadow.Canvas.Handle, 0, 0, Shadow.Width, Shadow.Height, BlendFunc);
{ Copy the blended area back to the Shadow bmp }
R2:= rect(x, y, x+Shadow.Width, y+Shadow.Height);
Shadow.Canvas.CopyRect(R, aCanvas, R2);
{ Diagonal shadow }
Shadow.Canvas.Brush.Style:= bsClear;
Shadow.Canvas.Font.Color := ShadowColor;
Shadow.Canvas.TextOut(0, 0, Text);
{ Blur the shadow }
janFX.GaussianBlur(Shadow, blur, 1);
{ Paste it back }
aCanvas.CopyRect(R2, Shadow.Canvas, R);
FINALLY
FreeAndNil(Shadow);
END;
{ Draw actual text at 100% opacity }
aCanvas.Brush.Style:= bsClear;
aCanvas.Font.Color := OriginalColor;
aCanvas.TextOut(x, y, Text);
end;
procedure TfrmTest.UseIt;
VAR BackgroundImage: tbitmap;
begin
BackgroundImage := Graphics.TBitmap.Create;
try
BackgroundImage.LoadFromFile('c:\test.bmp');
DrawShadowText (BackgroundImage.Canvas, 'This is some demo text', 20, 40, 140, clRed, clSilver);
Image1.Picture.Bitmap.Assign(BackgroundImage);
FINALLY
BackgroundImage.Free;
end;
end;
benwills
Software developer and IT trainer. working with Delphi & C#
Updated on June 28, 2022Comments
-
benwills almost 2 years
How can I write a semi transparent text on an Image (Jpg,Bmp), or a transparent text (color as same background Image) but with a shadow, something I want to do to watermark the images.
I want to accomplish that using Delphi win32.
-
benwills over 15 yearsX-Ray, that's will write the text with black color, I want the text to be semi transparent or transparent with shadow
-
Joel over 15 yearsFont color is used, not pen color
-
benwills over 15 yearsJim, I wanted transparent text with shadow, or semi transparent text not transparent background
-
byrakham over 15 years>Font color is used, not pen color thanks for the correction, jim.
-
Ben C almost 11 yearsSetting the brush to bsClear doesn't actually draw it transparent, it uses the brush color as the background color. bsClear works fine if you have a solid color'd background, but fails if you don't. If you want to have an actual transparent background for text, you should use: SetBkMode(Canvas.Handle, TRANSPARENT); And make sure to call SetBkMode after you set Canvas.Brush.Color.
-
Server Overflow about 10 yearsProgdigy.com is down? I cannot access it.
-
Anya Shenanigans about 10 yearsIt appears as though there was some disagreement between the developer of
GDI plus
for Delphi and Embarcadero, I don't know if it's been pulled in response to this. You could try searching for some alternatives such as Erik Van Bilsen's GDI plus. -
Jerry Mallett about 3 yearsHi Z80,I've amended this routine to get the width & height of the image I'm overlaying the text on and that would be 6000 x 4000 so that the Tmp text Image it creates has the same dimensions plus added a font size of 200 but I cannot get any type of indication of a shadow no matter what I change. Can you help please...
-
Jerry Mallett about 3 yearsBefore and After, I modified the code again to get the shadow.
-
Server Overflow about 3 yearsUpdated code. They need JanFX library (free) to make the image fuzzy. If you don't want to install it, just ignore the call to that library. The code can be improved but it is functional as it is.