DrawText() with border?

Just starting out? Need help? Post your questions and find answers here.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

DrawText() with border?

Post 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...
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
User avatar
kenmo
Addict
Addict
Posts: 2033
Joined: Tue Dec 23, 2003 3:54 am

Re: DrawText() with border?

Post 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.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: DrawText() with border?

Post 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)
I may look like a mule, but I'm not a complete ass.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: DrawText() with border?

Post 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.
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: DrawText() with border?

Post 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
I may look like a mule, but I'm not a complete ass.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: DrawText() with border?

Post 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. ;)
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: DrawText() with border?

Post 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.
I may look like a mule, but I'm not a complete ass.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: DrawText() with border?

Post 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?
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: DrawText() with border?

Post 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.
I may look like a mule, but I'm not a complete ass.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: DrawText() with border?

Post 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. :(
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: DrawText() with border?

Post 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
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: DrawText() with border?

Post 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.
I may look like a mule, but I'm not a complete ass.
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: DrawText() with border?

Post 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.
c4s
Addict
Addict
Posts: 1981
Joined: Thu Nov 01, 2007 5:37 pm
Location: Germany

Re: DrawText() with border?

Post by c4s »

Thank you for that link. I probably was to focused on "border" instead of "outline" when searching the forum.
If any of you native English speakers have any suggestions for the above text, please let me know (via PM). Thanks!
Post Reply