Page 1 of 1
DrawText() with border?
Posted: Sun Sep 19, 2010 4:45 pm
by c4s
How can draw a text with a border around it (with alpha support)?
Well, I had the idea of loading a relatively bigger font and drawing this one first and then the smaller one in the center of it. But then the padding between each character won't be correct and alpha won't work either.
Can anyone help me out? I already searched in the forum and even the whole internet but didn't found a solution...
Re: DrawText() with border?
Posted: Sun Sep 19, 2010 5:06 pm
by kenmo
One classic way is to draw the same exact text, in a different color, around it.
This is what I use:
Code: Select all
Procedure Write(x.l, y.l, Message.s, Color.i, Center.i = #False, Outline.l = -1)
Static dx.l
If (Message)
If (Center)
dx = x - TextWidth(Message)/2
Else
dx = x
EndIf
If (Outline >= 0)
DrawText(dx - 1, y, Message, Outline)
DrawText(dx + 1, y, Message, Outline)
DrawText(dx, y - 1, Message, Outline)
DrawText(dx, y + 1, Message, Outline)
EndIf
DrawText(dx, y, Message, Color)
EndIf
EndProcedure
It looks fine. However it can only do a single fixed-width border, and it calls DrawText() 5 times... which has actually caused frame slowdown for me sometimes.
Re: DrawText() with border?
Posted: Sun Sep 19, 2010 5:09 pm
by srod
I'm very rushed and am not sure if the following is what you are after? If it is then you can probably do the same sort of thing using the drawing lib and the alpha channels etc.
Code: Select all
text$ = "Hello Stinky!"
LoadFont(1, "Arial", 60)
pen = CreatePen_(#PS_SOLID, 2, #Blue)
If OpenWindow(0, 100, 100, 440, 300, "PureBasic - Image")
If CreateImage(0, 440, 300)
hdc = StartDrawing(ImageOutput(0))
If hdc
Box(0, 0, 440, 300, #White)
oldPen = SelectObject_(hdc, pen)
SetBkMode_(hdc, #TRANSPARENT)
oldFont = SelectObject_(hdc, FontID(1))
SetTextAlign_(hdc, #TA_LEFT|#TA_TOP)
BeginPath_(hdc)
TextOut_(hdc, 0, 0, text$, Len(text$))
EndPath_(hdc)
SelectObject_(hdc, oldFont)
StrokeAndFillPath_(hdc)
SelectObject_(hdc, oldPen)
StopDrawing() ; This is absolutely needed when the drawing operations are finished !!! Never forget it !
EndIf
EndIf
ImageGadget(1, 0, 0, 0, 0, ImageID(0))
Repeat
EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow ; If the user has pressed on the close button
EndIf
DeleteObject_(pen)
Re: DrawText() with border?
Posted: Sun Sep 19, 2010 5:54 pm
by c4s
@kenmo
I also thought of this but it's a very unpleasant way of doing it. Also it doesn't allow making thicker borders and alpha doesn't work because DrawText() lays over DrawText() etc.
@srod
Kind of. But I don't see where I could change the thickness of the border and the color inside the characters.
Re: DrawText() with border?
Posted: Sun Sep 19, 2010 7:17 pm
by srod
Code: Select all
text$ = "Hello Stinky!"
LoadFont(1, "Arial", 70)
Procedure BorderedText(hdc, x, y, text$, borderWidth, borderColor, fillColor)
Protected pen, oldPen, brush, oldBrush
pen = CreatePen_(#PS_SOLID, borderWidth, borderColor)
oldPen = SelectObject_(hdc, pen)
brush = CreateSolidBrush_(fillColor)
oldBRush = SelectObject_(hdc, brush)
SetBkMode_(hdc, #TRANSPARENT)
SetTextAlign_(hdc, #TA_LEFT|#TA_TOP)
BeginPath_(hdc)
TextOut_(hdc, x, y, text$, Len(text$))
EndPath_(hdc)
StrokeAndFillPath_(hdc)
SelectObject_(hdc, oldBrush)
SelectObject_(hdc, oldPen)
DeleteObject_(brush)
DeleteObject_(pen)
EndProcedure
If OpenWindow(0, 100, 100, 500, 300, "PureBasic - Image")
If CreateImage(0, 500, 300)
hdc = StartDrawing(ImageOutput(0))
If hdc
Box(0, 0, 500, 300, #White)
oldFont = SelectObject_(hdc, FontID(1))
BorderedText(hdc, 0, 0, text$, 1, #Blue, #White)
BorderedText(hdc, 0, 80, text$, 4, #Blue, #Red)
BorderedText(hdc, 0, 160, text$, 2, #Red, #Gray)
SelectObject_(hdc, oldFont)
StopDrawing()
EndIf
EndIf
ImageGadget(1, 0, 0, 0, 0, ImageID(0))
Repeat
EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow
EndIf
Re: DrawText() with border?
Posted: Sun Sep 19, 2010 7:47 pm
by c4s
Too bad there isn't a cross-platform solution. Anyway thank you srod!
Now I'll just have to try to get alpha involved.

Re: DrawText() with border?
Posted: Sun Sep 19, 2010 8:59 pm
by srod
c4s wrote:Now I'll just have to try to get alpha involved.

Probably best doing this on a pixel by pixel basis (use the drawing buffer). I hacked up some alpha support using a combination of the above code and PB's DrawText() on the alpha-channel, but the effects were relatively poor due to anti-aliasing effects etc.
Re: DrawText() with border?
Posted: Sun Sep 19, 2010 10:27 pm
by c4s
srod wrote:Probably best doing this on a pixel by pixel basis (use the drawing buffer). I hacked up some alpha support using a combination of the above code and PB's DrawText() on the alpha-channel, but the effects were relatively poor due to anti-aliasing effects etc.
I'm testing again and this stuff seems complicated to me.
Could you please share your code?
Re: DrawText() with border?
Posted: Mon Sep 20, 2010 9:29 am
by srod
I haven't got any code for the pixel by pixel stuff though it should be easy enough.
All I did was test some code using a combination of the above and PB's 2d drawing lib to give some alpha support and whilst it worked, it wasn't good enough because of the text anti-alias.
Re: DrawText() with border?
Posted: Mon Sep 20, 2010 11:56 am
by c4s
So you would draw the API stuff on a separate 24bit image with a color like $FF00FF as the background, then create a 32bit alpha image containing the API image and replace each $FF00FF pixel with full alpha, then draw the real things (unfortunately the API image creation must be before the actual StartDrawing() block...not cool) and the outlined "text" with DrawAlphaImage()?
This is really complicated to me and yes, I already tried it but didn't work. Looks like I didn't try hard enough though. What makes me think is your comment about anti-alias. Does that mean it could look strange on some systems?
So again I searched through the net using terms like "text border", "draw text outline" but it seems that all I get is something like kenmo's idea and another approach using .NET... This is really disappointing.

Re: DrawText() with border?
Posted: Mon Sep 20, 2010 12:23 pm
by c4s
This is my current solution and I don't like that I have to create the image before the actual Drawing block also a thickness small than 10 doesn't look good and finally I can't handle it as a normal DrawText() function.
Code: Select all
EnableExplicit
Enumeration
#Font
#Window
#Image
#ImageGadget
EndEnumeration
Procedure ImageDrawTextBordered(Text.s, TextFontNr, TextColor=$000000, BorderColor=$FFFFFF, BorderThickness=1)
Protected ImageNr, Image1Nr, Image2Nr, hDC
Protected Width, Height, iX, iY
Protected Pen, PenOld, FontOld
; Get width & height of the text+border
ImageNr = CreateImage(#PB_Any, 1, 1)
If ImageNr
If StartDrawing(ImageOutput(ImageNr))
DrawingFont(FontID(TextFontNr))
Width = TextWidth(Text) + (BorderThickness * 2)
Height = TextHeight(Text) + (BorderThickness * 2)
StopDrawing()
EndIf
FreeImage(ImageNr)
EndIf
; Draw bordered text on Image1
Image1Nr = CreateImage(#PB_Any, Width, Height, 24)
If Image1Nr
hDC = StartDrawing(ImageOutput(Image1Nr))
If hDC
Box(0, 0, Width, Height, $FF00FF)
FontOld = SelectObject_(hDC, FontID(TextFontNr))
Pen = CreatePen_(#PS_SOLID, BorderThickness, BorderColor)
PenOld = SelectObject_(hDC, Pen)
SetBkMode_(hDC, #TRANSPARENT)
BeginPath_(hDC)
TextOut_(hDC, BorderThickness, BorderThickness, Text, Len(Text))
EndPath_(hDC)
StrokeAndFillPath_(hDC)
SelectObject_(hDC, PenOld)
DeleteObject_(Pen)
SelectObject_(hDC, FontOld)
DrawingMode(#PB_2DDrawing_Transparent)
DrawingFont(FontID(TextFontNr))
DrawText(BorderThickness, BorderThickness, Text, TextColor)
StopDrawing()
EndIf
; Draw Image1 on Image2, make $FF00FF parts transparent
Image2Nr = CreateImage(#PB_Any, Width, Height, 32)
If Image2Nr
If StartDrawing(ImageOutput(Image2Nr))
DrawImage(ImageID(Image1Nr), 0, 0)
DrawingMode(#PB_2DDrawing_AlphaChannel)
For iY = 0 To Height - 1
For iX = 0 To Width - 1
If Point(iX, iY) & $FFFFFF = $FF00FF
Plot(iX, iY, $00000000)
EndIf
Next
Next
StopDrawing()
EndIf
EndIf
FreeImage(Image1Nr)
EndIf
ProcedureReturn Image2Nr
EndProcedure
Define Text.s, Width, Height
Define ImageNr
Text = "Test"
Width = 330
Height = 150
LoadFont(#Font, "", 90, #PB_Font_HighQuality)
ImageNr = ImageDrawTextBordered(Text, #Font, $F0F0F0, $FFFF00, 10) ; StartDrawing in a Drawing block isn't allowed
If CreateImage(#Image, Width, Height)
If StartDrawing(ImageOutput(#Image))
DrawingMode(#PB_2DDrawing_Gradient)
BackColor($00FFFF) : FrontColor($FF0000)
LinearGradient(0, Height, Width, Height)
Box(0, 0, Width, Height)
DrawAlphaImage(ImageID(ImageNr), 10, 10) ; Draw the text image
FreeImage(ImageNr)
StopDrawing()
EndIf
EndIf
If OpenWindow(#Window, #PB_Any, #PB_Any, Width, Height, "DrawTextBordered() Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ImageGadget(#ImageGadget, 0, 0, Width, Height, ImageID(#Image))
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf
Re: DrawText() with border?
Posted: Mon Sep 20, 2010 12:43 pm
by srod
Well the text itself doesn't have an alpha-channel and that particular example wouldn't require any additional images at all in that you could easily render the bordered text directly onto a window (because you are not using alpha values with the text itself).
Here's my quick hack which renders the text with alpha. I use your gradient filled image from above.
Code: Select all
text$ = "Heyho!"
LoadFont(1, "Arial", 90)
;Create a background brush.
If CreateImage(1, 450, 400)
If StartDrawing(ImageOutput(1))
DrawingMode(#PB_2DDrawing_Gradient)
BackColor($00FFFF) : FrontColor($FF0000)
LinearGradient(0, 400, 450, 400)
Box(0, 0, 450, 400)
StopDrawing()
EndIf
EndIf
backBrush = CreatePatternBrush_(ImageID(1))
Procedure GetAlphaBorderedText(image, x, y, text$, borderWidth, borderColor, fillColor, alpha)
Protected pen, oldPen, brush, oldBrush, oldFont, *mem.RGBQUAD, pxHeight, pxWidth, i , j
pen = CreatePen_(#PS_SOLID, borderWidth, borderColor)
If IsImage(image)
hdc = StartDrawing(ImageOutput(image))
If hdc
oldFont = SelectObject_(hdc, FontID(1))
oldPen = SelectObject_(hdc, pen)
brush = CreateSolidBrush_(fillColor)
oldBRush = SelectObject_(hdc, brush)
SetBkMode_(hdc, #TRANSPARENT)
SetTextAlign_(hdc, #TA_LEFT|#TA_TOP)
BeginPath_(hdc)
TextOut_(hdc, x, y, text$, Len(text$))
EndPath_(hdc)
StrokeAndFillPath_(hdc)
SelectObject_(hdc, oldBrush)
SelectObject_(hdc, oldPen)
DeleteObject_(brush)
DeleteObject_(pen)
;Apply per-pixel alpha values.
;We change all oqaque pixels to transparent and transparent ones are given the specified alpha value.
*mem = DrawingBuffer()
If DrawingBufferPixelFormat() & (#PB_PixelFormat_32Bits_RGB | #PB_PixelFormat_32Bits_BGR)
pxHeight = ImageHeight(image)
pxWidth = ImageWidth(image)
For i = 1 To pxHeight
For j = 1 To pxWidth
If *mem\rgbReserved = 0
*mem\rgbReserved = alpha
Else
*mem\rgbReserved = 0
EndIf
*mem + SizeOf(RGBQUAD)
Next
Next
EndIf
StopDrawing()
EndIf
EndIf
EndProcedure
hWnd = OpenWindow(0, 100, 100, 450, 400, "PureBasic - Image")
If hWnd
SetClassLongPtr_(hWnd, #GCL_HBRBACKGROUND, backBrush)
InvalidateRect_(hWnd, 0, 1)
If CreateImage(0, 450, 140, 32)
GetAlphaBorderedText(0, 20, 0, text$, 6, #Red, #White, 80)
EndIf
ImageGadget(1, 0, 0, 0, 0, ImageID(0))
Repeat
EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow
EndIf
You could easily modify this to simulate a DrawAlphaText() kind of function.
Re: DrawText() with border?
Posted: Mon Sep 20, 2010 8:53 pm
by Demivec
Here is code by Trond that outlines text.
Code: Select all
h = 500
w = 500
CreateImage(0, h, w, 24)
LoadFont(0, "Impact", 40)
text.s = "In my pants!"
tcol = #Red
ocol = #White ; can't be the same as tcol!
owidth = 2
StartDrawing(ImageOutput(0))
DrawingFont(FontID(0))
FrontColor(tcol)
DrawRotatedText(100, 350, text, 45)
For y = owidth To h-owidth-1
For x = owidth To w-owidth-1
c1 = Point(x, y)
If c1 <> tcol
For y2 = -owidth To owidth
For x2 = -owidth To owidth
If Point(x+x2, y+y2) = tcol
Plot(x, y, ocol)
Break 2
EndIf
Next
Next
EndIf
Next
Next
StopDrawing()
OpenWindow(0, 0, 0, 500, 500, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
ImageGadget(0, 0, 0, 0, 0, ImageID(0))
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Break
EndSelect
ForEver
Here's the source
link where this and related code were presented in relationship to producing a watermark.
Re: DrawText() with border?
Posted: Mon Sep 20, 2010 9:42 pm
by c4s
Thank you for that link. I probably was to focused on "border" instead of "outline" when searching the forum.