Jump to content

Create a custom angled speech bubble


Go to solution Solved by ioa747,

Recommended Posts

As per a previous thread, I spoke about saving an image displayed in the GUI (thanks @Andreik for clearing up the save to file feature), this is a continuation of the Speech Bubble project but a different issue regarding rotation of the graphic.

The below simple app is almost done but I can't figure out 3 things:

1) Why doesn't it clear out the original image when drawing the rotated image

2) Why is the rotated image shifted over to the left and cropped

3) How to get it to draw an outline with the extra odd line between (Not as critical for the project)

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinAPIHObj.au3>       ; For _GDIPlus_MatrixCreate

Opt('MustDeclareVars', 1)

_Main()

Func _Main()
    Local $hGUI, $hWnd, $hImage, $hGraphic, $cPic
    Local $outputImage = "SpeechBubble.png"
    FileDelete($outputImage)
    Local $fAngle = InputBox( "Enter Angle", "Please enter the angle of the speech bubble pointer")
    ConsoleWrite("Required Angle: " & $fAngle & @CRLF)

    ; Create GUI
    $hGUI = GUICreate("GDI+", 220, 220)
    $cPic = GUICtrlCreatePic('', 0, 0, 220, 220)
    $hWnd = WinGetHandle("GDI+")
    GUISetState()

    ; Create a blank image
    _GDIPlus_Startup()
    $hImage = _GDIPlus_BitmapCreateFromScan0(220, 220)
    $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage)

    ; Create fill brushes and draw pens
        Local $hBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00) ; Light green color
        Local $hBrush2 = _GDIPlus_BrushCreateSolid(0xFF0000FF) ; Light blue color
        Global $hPen = _GDIPlus_PenCreate(0xFF8800AA, 2)
        Global $hPen2 = _GDIPlus_PenCreate(0xFFFF0000, 2)

    ; Draw a Triangle
        Global $tWidth = 220
        Global $tHeight = 150
        Global $TriangleBase = 60
        Global $TriangleHeight = 60
        Global $TriangleX = 110 - $TriangleBase / 2
        Global $TriangleY = 65 ; $CircleY + $CircleDiameter / 2

        Local $aPoints[4][2] ; Create Points Array (Row 0 Col 0 holds row count)
        $aPoints[0][0] = 3  ;Number of points
        $aPoints[1][0] = $TriangleX
        $aPoints[1][1] = $TriangleY
        $aPoints[2][0] = $TriangleX + $TriangleBase
        $aPoints[2][1] = $TriangleY
        $aPoints[3][0] = $TriangleX + $TriangleBase / 2
        $aPoints[3][1] = $TriangleY - $TriangleHeight

        _GDIPlus_GraphicsFillPolygon($hGraphic, $aPoints, $hBrush)
        _GDIPlus_GraphicsDrawPolygon($hGraphic, $aPoints, $hPen)

    ; Draw a circle
    Local $CircleDiameter = 150
    Local $CircleX = (220 - $CircleDiameter) / 2
    Local $CircleY = (220 - $CircleDiameter) / 2

    _GDIPlus_GraphicsFillEllipse ( $hGraphic, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hBrush)
    _GDIPlus_GraphicsDrawEllipse($hGraphic, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hPen)

    ; Create a matrix and rotate the image by $fAngle degrees
        ;Rotation Matrix
        Local $hMatrix = _GDIPlus_MatrixCreate()
        _GDIPlus_MatrixRotate($hMatrix, $fAngle, "False")
        _GDIPlus_GraphicsSetTransform($hGraphic, $hMatrix)
        _GDIPlus_GraphicsDrawImage($hGraphic, $hImage, 0, 0)

        BitmapToCtrl($hImage, $cPic)

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    ; Save the image to $outputImage
    _GDIPlus_ImageSaveToFile($hImage, $outputImage)

    _GDIPlus_BrushDispose($hBrush2)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()

    If FileExists($outputImage) Then ShellExecute($outputImage)

EndFunc   ;==>_Main

Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc

 

Edited by NassauSky
Clarify non critical component
Link to comment
Share on other sites

Here a streamlined version of your script to show you a few flaws in your design.

1-  If you ask to paint 2 things in the same graphic, well, those 2 things will show

2- By rotating a shape, you also rotating its 0,0 origin, see why I needed to adapt according a 45° clockwise rotation

3- I don't understand your question

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>

Opt('MustDeclareVars', 1)

Main()

Func Main()
  Local $hGUI = GUICreate("GDI+", 220, 220)
  Local $idPic = GUICtrlCreatePic('', 10, 10, 200, 200)
  GUISetState()

  _GDIPlus_Startup()
  Local $hImage = _GDIPlus_BitmapCreateFromScan0(200, 200)
  Local $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage)

  _GDIPlus_GraphicsDrawRect($hGraphic, 25, 25, 120, 120) ; <<<<<<<<<<  comment out this line, and only one shape will be shown
  Local $hMatrix = _GDIPlus_MatrixCreate()
  _GDIPlus_MatrixRotate($hMatrix, 45)
  _GDIPlus_GraphicsSetTransform($hGraphic, $hMatrix)
  _GDIPlus_GraphicsDrawRect($hGraphic, 60, -60, 120, 120) ; <<<<<<<<<<<<<< need to adapt 0,0 origin

  BitmapToCtrl($hImage, $idPic)

  For $i = 1 To 800
    If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop
  Next

  _GDIPlus_GraphicsDispose($hGraphic)
  _GDIPlus_MatrixDispose($hMatrix)
  _GDIPlus_BitmapDispose($hImage)
  _GDIPlus_Shutdown()

EndFunc   ;==>Main

Func BitmapToCtrl($hBitmap, $cCtrl)
  Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
  _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
  _WinAPI_DeleteObject($hHBITMAP)
EndFunc   ;==>BitmapToCtrl

 

Edited by Nine
Link to comment
Share on other sites

Thanks @Nine

#1) OK let me modify my example to be closer to my desired end project since I think I see what you're saying and I do want to display 1 thing in the graphic then erase it and then display a 2nd in the graphic.  Here is a modification of my thought which displays the 0 degree speech bubble then asks the user for a rotation upon button click. 

 

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinAPIHObj.au3>       ; For _GDIPlus_MatrixCreate

Opt('MustDeclareVars', 1)

_Main()

Func _Main()
    Global $hGUI, $hWnd, $hImage, $hGraphic, $cPic, $idButton
    Global $outputImage = "SpeechBubble.png"
    Global $fAngle = 0
    FileDelete($outputImage)

    ; Create GUI
    $hGUI = GUICreate("GDI+", 220, 240)
    $idButton = GUICtrlCreateButton("Angle", 0, 0, 100, 20)
    $cPic = GUICtrlCreatePic('', 0, 20, 220, 220)

    $hWnd = WinGetHandle("GDI+")
    GUISetState()

        ; Create a blank image
        _GDIPlus_Startup()
        $hImage = _GDIPlus_BitmapCreateFromScan0(220, 220)
        $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage)

        ; Create fill brushes and draw pens
        Local $hBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00) ; Light green color
        Local $hBrush2 = _GDIPlus_BrushCreateSolid(0xFF0000FF) ; Light blue color
        Global $hPen = _GDIPlus_PenCreate(0xFF8800AA, 2)
        Global $hPen2 = _GDIPlus_PenCreate(0xFFFF0000, 2)

        ; Draw a Triangle
        Global $tWidth = 220
        Global $tHeight = 150
        Global $TriangleBase = 60
        Global $TriangleHeight = 60
        Global $TriangleX = 110 - $TriangleBase / 2
        Global $TriangleY = 65 ; $CircleY + $CircleDiameter / 2

        Local $aPoints[4][2] ; Create Points Array (Row 0 Col 0 holds row count)
        $aPoints[0][0] = 3  ;Number of points
        $aPoints[1][0] = $TriangleX
        $aPoints[1][1] = $TriangleY
        $aPoints[2][0] = $TriangleX + $TriangleBase
        $aPoints[2][1] = $TriangleY
        $aPoints[3][0] = $TriangleX + $TriangleBase / 2
        $aPoints[3][1] = $TriangleY - $TriangleHeight
        _GDIPlus_GraphicsFillPolygon($hGraphic, $aPoints, $hBrush)
        _GDIPlus_GraphicsDrawPolygon($hGraphic, $aPoints, $hPen)

        ; Draw a circle
        Local $CircleDiameter = 150
        Local $CircleX = (220 - $CircleDiameter) / 2
        Local $CircleY = (220 - $CircleDiameter) / 2
        _GDIPlus_GraphicsFillEllipse ( $hGraphic, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hBrush)
        _GDIPlus_GraphicsDrawEllipse($hGraphic, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hPen)

        BitmapToCtrl($hImage, $cPic)

        Do
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE
                    ExitLoop
                Case $idButton
                    Global $fAngle = InputBox( "Enter Angle", "Please enter the angle of the speech bubble pointer")
                    ConsoleWrite("Required Angle: " & $fAngle & @CRLF)
                    SpeechBubbleRedraw()
            EndSwitch

        Until False ;GUIGetMsg() = $GUI_EVENT_CLOSE

        ; Save the image to $outputImage
        _GDIPlus_ImageSaveToFile($hImage, $outputImage)
        _GDIPlus_BrushDispose($hBrush2)
        _GDIPlus_BrushDispose($hBrush)
        _GDIPlus_GraphicsDispose($hGraphic)
        _GDIPlus_BitmapDispose($hImage)
        _GDIPlus_Shutdown()

        If FileExists($outputImage) Then ShellExecute($outputImage)

EndFunc   ;==>_Main

Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc

Func SpeechBubbleRedraw()
    ; Create a matrix and rotate the image by $fAngle degrees
        Local $hMatrix = _GDIPlus_MatrixCreate()
        _GDIPlus_MatrixRotate($hMatrix, $fAngle, "False")
        _GDIPlus_GraphicsSetTransform($hGraphic, $hMatrix)
        _GDIPlus_GraphicsDrawImage($hGraphic, $hImage, 0, 0)

        BitmapToCtrl($hImage, $cPic)
EndFunc

#2) Thanks! That made sense i ran some tests and it looks good.

#3) My example creates a speech bubble from an ellipse and a triangle but when you draw a line around each one individually it doesn't look like a speech bubble. I would need to be able to draw a line around the union of the 2 shapes if that makes sense.

Edited by NassauSky
Link to comment
Share on other sites

Drawing rotated things it's very tricky. It's not just that you have to adjust (translate) the matrix before and after rotation to ensure a rotation around the center of your image but also you have to keep in mind that a rotated image might extend beyond the initial image dimensions. Let's say that the purple box is your bitmap and you have a rectangle in the middle of your image. When it's rotated at 45 deg the rectangle won't fit in your image unless is the rectangle is scaled down or image dimension increased. So it's pretty tricky to work with rotated images in GDI+ but it might be an easier solution if you detail your final goal.

 

Untitled-1.jpg

Edited by Andreik

When the words fail... music speaks.

Link to comment
Share on other sites

Thanks @Andreik,

Yeah that's why I only need to create square images. In this case the Speech Bubble content will be a circle with enough space on it's 4 sides that when it rotates with a point, it shouldn't be clipped off.

There is an example by @Lakes in a long thread at Posted August 30, 2010 about 1/3 down the page 
The QUICK 2D EXAMPLE

 This example is similar in a way to how it refreshes but can't figure exactly how it's code is refreshing and clearing the images in relation to what I want to do.

Edited by NassauSky
Link to comment
Share on other sites

Not pretty but this is what I'm working with now, still needs rotation and optionally creating a border around the outside edge which doesn't cut through the triangle.

Trying some rotation techniques. Some remarked out. CalculateCoordinates func might be the start of getting it rotated correctly.

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinAPIHObj.au3>       ; For _GDIPlus_MatrixCreate

Opt('MustDeclareVars', 1)

_Main()

Func _Main()
    Global $hGUI, $hWnd, $hImage, $hGraphic, $cPic, $idButton
    Global $outputImage = "SpeechBubble.png"
    Global $fAngle      = 0
    Global Const $nPI = 3.1415926535897932384626433832795
    Global $iRadius   = 110
    Global $iCenter   = 110
    Global $iSize     = 220     
    Global $nImgWidth   = 220
        
        
    FileDelete($outputImage)

    ; Create GUI
    $hGUI = GUICreate("GDI+", $nImgWidth, 240)
    $idButton = GUICtrlCreateButton("Angle", 0, 0, 100, 20)
    $cPic = GUICtrlCreatePic('', 0, 20, $nImgWidth, $nImgWidth)

    $hWnd = WinGetHandle("GDI+")
    GUISetState()

        ; Create a blank image
        _GDIPlus_Startup()
        $hImage = _GDIPlus_BitmapCreateFromScan0($nImgWidth, $nImgWidth)
        $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage)

        ; Create fill brushes and draw pens
        Local $hBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00) ; Light green color
        Local $hBrush2 = _GDIPlus_BrushCreateSolid(0xFF0000FF) ; Light blue color
        Global $hPen = _GDIPlus_PenCreate(0xFF8800AA, 2)
        Global $hPen2 = _GDIPlus_PenCreate(0xFFFF0000, 2)

        DrawTriangle($hGraphic, $hBrush, $hPen)
        DrawCircle($hGraphic, $hBrush, $hPen)
        BitmapToCtrl($hImage, $cPic)

        Do
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE
                    ExitLoop
                Case $idButton
                    Global $fAngle = InputBox( "Enter Angle", "Please enter the angle of the speech bubble pointer", 45)
                    ConsoleWrite("Required Angle: " & $fAngle & @CRLF)
                    SpeechBubbleRedraw($hGraphic, $hImage, $hBrush, $hPen)
            EndSwitch

        Until False ;GUIGetMsg() = $GUI_EVENT_CLOSE

        ; Save the image to $outputImage
        _GDIPlus_ImageSaveToFile($hImage, $outputImage)
        _GDIPlus_BrushDispose($hBrush2)
        _GDIPlus_BrushDispose($hBrush)
        _GDIPlus_GraphicsDispose($hGraphic)
        _GDIPlus_BitmapDispose($hImage)
        _GDIPlus_Shutdown()

        If FileExists($outputImage) Then ShellExecute($outputImage)
                ;If FileExists($outputImage2) Then ShellExecute($outputImage2)
EndFunc   ;==>_Main

Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc

Func DrawTriangle($hGraphicT, $hBrushT, $hPenT)
            ; Draw a Triangle
            Global $tWidth = $nImgWidth
            Global $tHeight = 150
            Global $TriangleBase = 60
            Global $TriangleHeight = 60
            Global $TriangleX = 110 - $TriangleBase / 2
            Global $TriangleY = 65 ; $CircleY + $CircleDiameter / 2

            Local $aPoints[4][2] ; Create Points Array (Row 0 Col 0 holds row count)
            $aPoints[0][0] = 3  ;Number of points
            $aPoints[1][0] = $TriangleX
            $aPoints[1][1] = $TriangleY
            $aPoints[2][0] = $TriangleX + $TriangleBase
            $aPoints[2][1] = $TriangleY
            $aPoints[3][0] = $TriangleX + $TriangleBase / 2
            $aPoints[3][1] = $TriangleY - $TriangleHeight
            _GDIPlus_GraphicsFillPolygon($hGraphicT, $aPoints, $hBrushT)
            _GDIPlus_GraphicsDrawPolygon($hGraphicT, $aPoints, $hPenT)  
    
EndFunc

Func DrawCircle($hGraphicT, $hBrushT, $hPenT)
            ; Draw a circle
            Local $CircleDiameter = 150
            Local $CircleX = ($nImgWidth - $CircleDiameter) / 2
            Local $CircleY = ($nImgWidth - $CircleDiameter) / 2
            _GDIPlus_GraphicsFillEllipse ( $hGraphicT, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hBrushT )
            _GDIPlus_GraphicsDrawEllipse ( $hGraphicT, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hPenT )
EndFunc

Func SpeechBubbleRedraw($hGraphicT, $hImageT, $hBrushT, $hPenT)
    ; Create a matrix and rotate the image by $fAngle degrees
        _GDIPlus_GraphicsClear($hGraphicT, 0x00FFFFFF) ; Clear with white (0xFFFFFFFF) or any other desired color

;~      Local $R = ($nPI / 2) - ($fAngle  * ($nPI /180))
;~      Local $iX1 = Int($iCenter + Cos($R) * $iRadius)
;~      Local $iY1 = Int($iCenter - Sin($R) * $iRadius)

;~      ;Local $aCoords = CalculateCoordinates($nImgWidth, $fAngle)
;~      ;Local $iX1 = $aCoords[0]
;~      ;Local $iY1 = $aCoords[1]
;~      ConsoleWrite("Given angle: " & $fAngle & " Result x/y: " & $iX1 & " / " & $iY1 & @CRLF)
;~      Local $hMatrix = _GDIPlus_MatrixCreate()
;~      _GDIPlus_MatrixRotate($hMatrix, $fAngle, "False")
;~      _GDIPlus_GraphicsSetTransform($hGraphic, $hMatrix)
;~      _GDIPlus_GraphicsDrawImage($hGraphic, $hImage, $iY1, $iY1)


        Local $hMatrix = _GDIPlus_MatrixCreate()
        _GDIPlus_MatrixRotate($hMatrix, $fAngle, "False")
        _GDIPlus_GraphicsSetTransform($hGraphicT, $hMatrix)
        DrawTriangle($hGraphicT, $hBrushT, $hPenT)
        DrawCircle($hGraphicT, $hBrushT, $hPenT)

        GUICtrlDelete ( $cPic )
        $cPic = GUICtrlCreatePic('', 0, 20, $nImgWidth, $nImgWidth)
        GUISetState()
        BitmapToCtrl($hImageT, $cPic)


        _GDIPlus_ImageSaveToFile($hImageT, $outputImage)
EndFunc

Func CalculateCoordinates($nImgWidth, $fAngle)
    Local $xx, $yy
    Local $nImgWidthHalf = $nImgWidth / 2

    Switch $fAngle
        Case 0
            $xx = 0
            $yy = 0
        Case 1 To 44
            $xx = $fAngle
            $yy = 0
        Case 45
            $xx = 45
            $yy = -($nImgWidth / 2)
        Case 46 To 89
            $xx = $nImgWidthHalf
            $yy = -($nImgWidth / 2)
        Case 90
            $xx = 0
            $yy = -($nImgWidth)
        Case 91 To 134
            $xx = 0
            $yy = -($nImgWidth)
        Case 135
            $xx = -($nImgWidth)
            $yy = -($nImgWidth)
        Case 136 To 179
            $xx = -($nImgWidth)
            $yy = -($nImgWidth)
        Case 180
            $xx = -($nImgWidth)
            $yy = -($nImgWidth)
        Case 181 To 224
            $xx = -($nImgWidth)
            $yy = -($nImgWidthHalf)
        Case 225
            $xx = -($nImgWidth)
            $yy = -($nImgWidthHalf)
        Case 226 To 269
            $xx = 0
            $yy = 0
        Case 270
            $xx = 0
            $yy = 0
        Case 271 To 314
            $xx = $fAngle - 360
            $yy = 0
        Case 315
            $xx = $fAngle - 360
            $yy = 0
        Case 316 To 359
            $xx = $fAngle - 360
            $yy = -($nImgWidth / 2)
    EndSwitch

    Local $aResult[2]
    $aResult[0] = $xx
    $aResult[1] = $yy

    ConsoleWrite("Given angle: " & $fAngle & " Result x/y: " & $xx & "/" & $yy & @CRLF)

    Return $aResult
EndFunc

 

Edited by NassauSky
Link to comment
Share on other sites

Here is your code modified from first post

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinAPIHObj.au3>       ; For _GDIPlus_MatrixCreate

Opt('MustDeclareVars', 1)

_Main()

Func _Main()
    Local $hGUI, $hWnd, $hImage, $hGraphic, $cPic
    Local $outputImage = "SpeechBubble.png"
    FileDelete($outputImage)
    Local $fAngle = InputBox( "Enter Angle", "Please enter the angle of the speech bubble pointer",10)
    ConsoleWrite("Required Angle: " & $fAngle & @CRLF)

    ; Create GUI
    $hGUI = GUICreate("GDI+", 220, 220)
    $cPic = GUICtrlCreatePic('', 0, 0, 220, 220)
    $hWnd = WinGetHandle("GDI+")
    GUISetState()

    ; Create a blank image
    _GDIPlus_Startup()
    $hImage = _GDIPlus_BitmapCreateFromScan0(220, 220)
    $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage)

    ; Create fill brushes and draw pens
        Local $hBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00) ; Light green color
        Local $hBrush2 = _GDIPlus_BrushCreateSolid(0xFF0000FF) ; Light blue color
        Global $hPen = _GDIPlus_PenCreate(0xFF8800AA, 2)
        Global $hPen2 = _GDIPlus_PenCreate(0xFFFF0000, 2)

    ; Draw a Triangle
        Global $tWidth = 220
        Global $tHeight = 150
        Global $TriangleBase = 60
        Global $TriangleHeight = 60
        Global $TriangleX = 110 - $TriangleBase / 2
        Global $TriangleY = 65 ; $CircleY + $CircleDiameter / 2

        Local $aPoints[4][2] ; Create Points Array (Row 0 Col 0 holds row count)
        $aPoints[0][0] = 3  ;Number of points
        $aPoints[1][0] = $TriangleX
        $aPoints[1][1] = $TriangleY
        $aPoints[2][0] = $TriangleX + $TriangleBase
        $aPoints[2][1] = $TriangleY
        $aPoints[3][0] = $TriangleX + $TriangleBase / 2
        $aPoints[3][1] = $TriangleY - $TriangleHeight

        _GDIPlus_GraphicsFillPolygon($hGraphic, $aPoints, $hBrush)
        _GDIPlus_GraphicsDrawPolygon($hGraphic, $aPoints, $hPen)

    ; Draw a circle
    Local $CircleDiameter = 150
    Local $CircleX = (220 - $CircleDiameter) / 2
    Local $CircleY = (220 - $CircleDiameter) / 2

    _GDIPlus_GraphicsFillEllipse ( $hGraphic, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hBrush)
    _GDIPlus_GraphicsDrawEllipse($hGraphic, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hPen)

    ;This in new - pen for drawing axes lines
    Local $hPenArrow = _GDIPlus_PenCreate(0xFF000000, 1)
    _GDIPlus_PenSetEndCap($hPenArrow, $GDIP_LINECAPARROWANCHOR)
    Local $hPenArrow_ForRotated = _GDIPlus_PenCreate(0xFFF00F00, 1);yellow line - rotated
    _GDIPlus_PenSetEndCap($hPenArrow_ForRotated, $GDIP_LINECAPARROWANCHOR)

    Local $fAxesCenterX=$CircleX+$CircleDiameter/2, $fAxesCenterY=$CircleY+$CircleDiameter/2;center coordinates
    ConsoleWrite("X=" & $fAxesCenterX & "; Y=" & $fAxesCenterY & @CRLF)
    _GDIPlus_GraphicsDrawLine($hGraphic,$fAxesCenterX,$fAxesCenterY,$fAxesCenterX+100,$fAxesCenterY,$hPenArrow);notice value of center coordinates

    ; Create a matrix and rotate the image by $fAngle degrees
        ;Rotation Matrix
        Local $hMatrix = _GDIPlus_MatrixCreate()
        _GDIPlus_MatrixTranslate($hMatrix,+$fAxesCenterX,+$fAxesCenterY);https://www.vbforums.com/showthread.php?841185-GDIPLUS-how-rotate-an-image-by-a-center&s=f20f7a20e35573308599859c5bb9c70d&p=5123549&viewfull=1#post5123549
        _GDIPlus_MatrixRotate($hMatrix, $fAngle, False)
        _GDIPlus_MatrixTranslate($hMatrix,-$fAxesCenterX,-$fAxesCenterY)
        _GDIPlus_GraphicsSetTransform($hGraphic, $hMatrix);https://www.vbforums.com/showthread.php?841185-GDIPLUS-how-rotate-an-image-by-a-center&s=f20f7a20e35573308599859c5bb9c70d&p=5123549&viewfull=1#post5123549


        _GDIPlus_GraphicsDrawImage($hGraphic, $hImage, 0, 0);"Origin display rotated" - comment this line to see axes

        BitmapToCtrl($hImage, $cPic)

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    ; Save the image to $outputImage
    ;_GDIPlus_ImageSaveToFile($hImage, $outputImage)
    _GDIPlus_PenDispose($hPenArrow)
    _GDIPlus_BrushDispose($hBrush2)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()

    ;If FileExists($outputImage) Then ShellExecute($outputImage)

EndFunc   ;==>_Main

Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc

Rotation about center taken from here.

Edited by ahmet
Link to comment
Share on other sites

I found two old scripts - maybe it helps:

;idea taken from http://codepen.io/neilorangepeel/pen/mJoxaJ
;coded by UEZ build 2015-08-20
#pragma compile(Icon, "AutoIt_Main_v10_48x48_only_RGB-A.ico")
#AutoIt3Wrapper_Run_Au3Stripper=y
#Au3Stripper_Parameters=/so /pe /rm
#AutoIt3Wrapper_Run_After=del /f /q "%scriptdir%\%scriptfile%_stripped.au3"

#include <GDIPlus.au3>
#include <GuiConstantsEx.au3>
#include <WindowsConstants.au3>

AutoItSetOption("GUIOnEventMode", 1)
_GDIPlus_Startup()
Global $i
Global Const $iW = 500, $iH = 700, $sTitle = "GDI+ Ascending Rectangles / fps: "
Global Const $hGUI = GUICreate($sTitle & 0, $iW, $iH)
GUISetState()
Global Const $hDC = _WinAPI_GetDC($hGUI)
Global Const $hHBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW, $iH)
Global Const $hDC_backbuffer = _WinAPI_CreateCompatibleDC($hDC)
Global Const $DC_obj = _WinAPI_SelectObject($hDC_backbuffer, $hHBitmap)
Global Const $hCanvas = _GDIPlus_GraphicsCreateFromHDC($hDC_backbuffer)
_GDIPlus_GraphicsSetPixelOffsetMode($hCanvas, 4)

Global Const $iAmount = 24
Global $aData[$iAmount][13] = [[]], $hEffect, $iFPS = 0

For $i = 0 To UBound($aData) - 1
    $aData[$i][0] = Random(20, $iW / 4, 1)  ;size
    $aData[$i][1] = _GDIPlus_BitmapCreateFromScan0($aData[$i][0], $aData[$i][0]) ;hBmp
    $aData[$i][2] = _GDIPlus_ImageGetGraphicsContext($aData[$i][1]) ;hGfx
    _GDIPlus_GraphicsSetPixelOffsetMode($aData[$i][2], 4)
    _GDIPlus_GraphicsSetSmoothingMode($aData[$i][2], 4)
    $aData[$i][3] = Random(0, $iW - $aData[$i][0], 1) ;x pos
    $aData[$i][4] = Random($iH, $iH * 3) ;y pos
    $aData[$i][5] = Random(1, 5) ;vy
    $aData[$i][6] = Random(0x08, 0xA0, 1) * 0x1000000 + Random(0x00000, 0xFFFFFF, 1) ;random color alpha + rgb
    $aData[$i][7] = _GDIPlus_BrushCreateSolid($aData[$i][6]) ;hBrush
    $aData[$i][8] = _GDIPlus_PenCreate(0x20505050, Random(1, Ceiling($aData[$i][0] / 14))) ;hPen
    $aData[$i][9] = Random(-6, 6) ;roation
    $aData[$i][10] = _GDIPlus_MatrixCreate() ;hMatrix
    $aData[$i][11] = Random(-50, 50) ;sin value
    $aData[$i][12] = Random(-2, 2) ;increase value for sin
Next

AdlibRegister("CalcFPS", 1000)

GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")

Anim()
While Sleep(30)
    Anim()
WEnd

Func _Exit()
    AdlibUnRegister("CalcFPS")
    For $i = 0 To UBound($aData) - 1
        _GDIPlus_GraphicsDispose($aData[$i][2])
        _GDIPlus_BitmapDispose($aData[$i][1])
        _GDIPlus_BrushDispose($aData[$i][7])
        _GDIPlus_PenDispose($aData[$i][8])
        _GDIPlus_MatrixDispose($aData[$i][10])
    Next
    _GDIPlus_GraphicsDispose($hCanvas)
    _WinAPI_SelectObject($hDC_backbuffer, $DC_obj)
    _WinAPI_DeleteDC($hDC_backbuffer)
    _WinAPI_DeleteObject($hHBitmap)
    _WinAPI_ReleaseDC($hGUI, $hDC)
    _GDIPlus_Shutdown()
    GUIDelete()
    Exit
EndFunc

Func Anim()
    Local $i, $fX, $fY, $fW, $fH
    _GDIPlus_GraphicsClear($hCanvas, 0xFFFAFAFF)
    For $i = 0 To UBound($aData) - 1
        _GDIPlus_GraphicsClear($aData[$i][2], 0x00000000)
        _GDIPlus_MatrixTranslate($aData[$i][10], $aData[$i][0] / 2, $aData[$i][0] / 2)
        _GDIPlus_MatrixRotate($aData[$i][10], $aData[$i][9])
        _GDIPlus_MatrixTranslate($aData[$i][10], -$aData[$i][0] / 2, -$aData[$i][0] / 2)
        _GDIPlus_GraphicsSetTransform($aData[$i][2], $aData[$i][10])
        $fX = $aData[$i][0] / 5
        $fY = $fX
        $fW = $aData[$i][0] - $aData[$i][0] / 2.5
        $fH = $fW
        _GDIPlus_GraphicsFillRect($aData[$i][2], $fX, $fY, $fW, $fH, $aData[$i][7])
        _GDIPlus_GraphicsDrawRect($aData[$i][2], $fX, $fY, $fW, $fH, $aData[$i][8])
        _GDIPlus_GraphicsDrawImageRect($hCanvas, $aData[$i][1], $aData[$i][3] + Sin($aData[$i][11] / 100) * 100, $aData[$i][4], $aData[$i][0], $aData[$i][0])
        $aData[$i][11] += $aData[$i][12]
        $aData[$i][4] -= $aData[$i][5]
        If $aData[$i][4] < -$aData[$i][0] * 2 Then
            _GDIPlus_MatrixDispose($aData[$i][10])
            _GDIPlus_GraphicsDispose($aData[$i][2])
            _GDIPlus_BitmapDispose($aData[$i][1])
            $aData[$i][0] = Random(20, $iW / 4, 1)  ;size
            $aData[$i][1] = _GDIPlus_BitmapCreateFromScan0($aData[$i][0], $aData[$i][0]) ;hBmp
            $aData[$i][2] = _GDIPlus_ImageGetGraphicsContext($aData[$i][1]) ;hGfx
            _GDIPlus_GraphicsSetPixelOffsetMode($aData[$i][2], 4)
            _GDIPlus_GraphicsSetSmoothingMode($aData[$i][2], 4)
            $aData[$i][3] = Random(0, $iW - $aData[$i][0], 1) ;x pos
            $aData[$i][4] = $iH
            $aData[$i][5] = Random(1, 5) ;vy
            $aData[$i][6] = Random(0x08, 0xA0, 1) * 0x1000000 + Random(0x00000, 0xFFFFFF, 1) ;random color
            _GDIPlus_BrushSetSolidColor($aData[$i][7], $aData[$i][6])
            $aData[$i][9] = Random(-6, 6) ;roation
            $aData[$i][10] = _GDIPlus_MatrixCreate() ;hMatrix
        EndIf
    Next
    _WinAPI_BitBlt($hDC, 0, 0, $iW, $iH, $hDC_backbuffer, 0, 0, $SRCCOPY)
    $iFPS += 1
EndFunc

Func CalcFPS()
    WinSetTitle($hGUI, "", $sTitle & $iFPS)
    $iFPS = 0
EndFunc

 

#include <GDIPlusConstants.au3>
#include <WindowsConstants.au3>
#include <GDIPlus.au3>
#include <WinAPISysWin.au3>

;example
#include <GDIPlus.au3>
_GDIPlus_Startup()
Local Const $STM_SETIMAGE = 0x0172, $iBGTColor = 0xFEDCBA, $iW = 70, $iH = 45
Local $hGUI_ToolTip = GUICreate("", $iW, $iH, -1, -1, $WS_POPUP + $WS_DISABLED, $WS_EX_LAYERED + $WS_EX_TOOLWINDOW)
GUISetBkColor($iBGTColor, $hGUI_ToolTip) ;set GUI background color
Local $iPic = GUICtrlCreatePic("", 0, 0, $iW, $iH)
GUISetState(@SW_SHOW, $hGUI_ToolTip)
_WinAPI_SetLayeredWindowAttributes($hGUI_ToolTip, $iBGTColor, 0xFF)

Local $hHBmp_TP = _GDIPlus_CreateToolTipBitmap("Hallo", $iW, $iH)
Local $hB = GUICtrlSendMsg($iPic, $STM_SETIMAGE, $IMAGE_BITMAP, $hHBmp_TP)
If $hB Then _WinAPI_DeleteObject($hB)

Do
Until GUIGetMsg() = -3

_WinAPI_DeleteObject($hHBmp_TP)
_GDIPlus_Shutdown()
GUIDelete()
Exit




; #FUNCTION# ====================================================================================================================
; Name ..........: _GDIPlus_CreateToolTipBitmap
; Description ...: Creates a tooltip like window using GDI+
; Syntax ........: _GDIPlus_CreateToolTipBitmap($sText[, $iW = 70[, $iH = 50[, $sFont = "Arial"[, $fFontSize = 11[,
;                  $iBGColor = 0xFFFFFFFF[, $iFontColor = 0xFF000000[, $iRadius = 8[, $hImage = 0[, $iImgPosX = 8[,
;                  $iImgPosY = 4[, $iImgW = 16[, $iImgH = 16[, $bGDIBmp = 1]]]]]]]]]]]]])
; Parameters ....: $sText               - A string value.
;                  $iW                  - [optional] An integer value. Default is 70.
;                  $iH                  - [optional] An integer value. Default is 50.
;                  $sFont               - [optional] A string value. Default is "Arial".
;                  $fFontSize           - [optional] A boolean value. Default is 11.
;                  $iBGColor            - [optional] An integer value. Default is 0xFFFFFFFF.
;                  $iFontColor          - [optional] An integer value. Default is 0xFF000000.
;                  $iRadius             - [optional] An integer value. Default is 8.
;                  $hImage              - [optional] A handle value. Default is 0.
;                  $iImgPosX            - [optional] An integer value. Default is 8.
;                  $iImgPosY            - [optional] An integer value. Default is 4.
;                  $iImgW               - [optional] An integer value. Default is 16.
;                  $iImgH               - [optional] An integer value. Default is 16.
;                  $bGDIBmp             - [optional] A binary value. Default is 1.
; Return values .: bitmap handle (GDI+ format bitmap)
; Author ........: UEZ
; Modified ......:
; Remarks .......: AutoIt version 3.3.9.18 or higher is needed
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _GDIPlus_CreateToolTipBitmap($sText, $iW = 70, $iH = 50, $sFont = "Arial", $fFontSize = 11, $iBGColor = 0xFFFFFFFF, $iFontColor = 0xFF000000, $iRadius = 8, $hImage = 0, $iImgPosX = 8, $iImgPosY = 4, $iImgW = 16, $iImgH = 16, $bGDIBmp = 1)
    Local $iX = 0, $iY = 0, $iPenSize = 1, $iSize = 12, $iSize2 = $iSize / 2,   $iDX = $iW / 2
    Local $iWidth = $iW - $iPenSize, $iHeight = $iH - $iPenSize - $iSize
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    Local $hCtxt = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _GDIPlus_GraphicsSetSmoothingMode($hCtxt, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
    _GDIPlus_GraphicsSetTextRenderingHint($hCtxt, $GDIP_TEXTRENDERINGHINTANTIALIASGRIDFIT)
    Local $hPath = _GDIPlus_PathCreate()
    _GDIPlus_PathAddArc($hPath, $iX + $iWidth - ($iRadius * 2), $iY, $iRadius * 2, $iRadius * 2, 270, 90)
    _GDIPlus_PathAddArc($hPath, $iX + $iWidth - ($iRadius * 2), $iY + $iHeight - ($iRadius * 2), $iRadius * 2, $iRadius * 2, 0, 90)
    _GDIPlus_PathAddLine($hPath, $iX + $iWidth - ($iRadius * 2), $iY + $iHeight, $iDX + $iSize2, $iY + $iHeight)
    _GDIPlus_PathAddLine($hPath, $iDX + $iSize2, $iY + $iHeight, $iX + $iDX, $iY + $iHeight + $iSize)
    _GDIPlus_PathAddLine($hPath, $iX + $iDX, $iY + $iHeight + $iSize, $iX + $iDX - $iSize2, $iY + $iHeight)
    _GDIPlus_PathAddArc($hPath,  $iX, $iY + $iHeight - ($iRadius * 2), $iRadius * 2, $iRadius * 2, 90, 90)
    _GDIPlus_PathAddArc($hPath,  $iX, $iY, $iRadius * 2, $iRadius * 2, 180, 90)
    _GDIPlus_PathCloseFigure($hPath)

    Local $hBrush = _GDIPlus_BrushCreateSolid($iBGColor)
    _GDIPlus_GraphicsFillPath($hCtxt, $hPath, $hBrush)
    Local $hPen = _GDIPlus_PenCreate(0xFF000000, $iPenSize)
    _GDIPlus_GraphicsDrawPath($hCtxt, $hPath, $hPen)

    Local $hFamily = _GDIPlus_FontFamilyCreate($sFont)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $fFontSize)
    Local $hFormat = _GDIPlus_StringFormatCreate()
    Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iWidth, $iHeight)
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    Local $aMeasure = _GDIPlus_GraphicsMeasureString($hCtxt, $sText, $hFont, $tLayout, $hFormat)
    DllStructSetData($tLayout, "Y", ($iHeight - DllStructGetData($aMeasure[0], "Height")) / 2)
    _GDIPlus_BrushSetSolidColor($hBrush, $iFontColor)
    _GDIPlus_GraphicsDrawStringEx($hCtxt, $sText, $hFont, $tLayout, $hFormat, $hBrush)

    If $hImage Then
        _GDIPlus_GraphicsSetInterpolationMode($hCtxt, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
        _GDIPlus_GraphicsDrawImageRectRect($hCtxt, $hImage, 0, 0, _GDIPlus_ImageGetWidth($hImage), _GDIPlus_ImageGetHeight($hImage), $iImgPosX, $iImgPosY, $iImgW, $iImgH)
    EndIf

    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_PathDispose($hPath)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_GraphicsDispose($hCtxt)
    If $bGDIBmp Then
        Local $hGDIBmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
        _GDIPlus_BitmapDispose($hBitmap)
        Return $hGDIBmp
    EndIf
    Return $hBitmap
EndFunc   ;==>_GDIPlus_CreateToolTipBitmap

 

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

  • Solution

here is my approach, with out rotate

; https://www.autoitscript.com/forum/topic/211009-create-a-custom-angled-speech-bubble/?do=findComment&comment=1525867

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinAPIHObj.au3>       ; For _GDIPlus_MatrixCreate

Opt('MustDeclareVars', 1)

_Main()

Func _Main()
    Global $hGUI, $hWnd, $hImage, $hGraphic, $cPic, $idButton
    Global $outputImage = "SpeechBubble.png"
    Global $fAngle = 0
    Global $nImgWidth = 220

    FileDelete($outputImage)

    ; Create GUI
    $hGUI = GUICreate("GDI+", $nImgWidth, 240)
    $idButton = GUICtrlCreateButton("Angle", 0, 0, 100, 20)
    $cPic = GUICtrlCreatePic('', 0, 20, $nImgWidth, $nImgWidth)

    $hWnd = WinGetHandle("GDI+")
    GUISetState()

    ; Create a blank image
    _GDIPlus_Startup()
    $hImage = _GDIPlus_BitmapCreateFromScan0($nImgWidth, $nImgWidth)
    $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage)

    ; Create fill brushes and draw pens
    Local $hBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00)     ; Light green color
    Local $hBrush2 = _GDIPlus_BrushCreateSolid(0xFF0000FF)     ; Light blue color
    Global $hPen = _GDIPlus_PenCreate(0xFF8800AA, 2)
    Global $hPen2 = _GDIPlus_PenCreate(0xFFFF0000, 2)

    DrawBubble($hGraphic, $hBrush, $hPen, 0)
    BitmapToCtrl($hImage, $cPic)

    Do
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $idButton
                Global $fAngle = InputBox("Enter Angle", "Please enter the angle of the speech bubble pointer", 45)
                ConsoleWrite("Required Angle: " & $fAngle & @CRLF)
                SpeechBubbleRedraw($hGraphic, $hImage, $hBrush, $hPen, $fAngle)
        EndSwitch

    Until False     ;GUIGetMsg() = $GUI_EVENT_CLOSE

    ; Save the image to $outputImage
    _GDIPlus_ImageSaveToFile($hImage, $outputImage)
    _GDIPlus_BrushDispose($hBrush2)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()

    If FileExists($outputImage) Then ShellExecute($outputImage)
    ;If FileExists($outputImage2) Then ShellExecute($outputImage2)
EndFunc   ;==>_Main
;----------------------------------------------------------------------------------------
Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc   ;==>BitmapToCtrl
;----------------------------------------------------------------------------------------
Func DrawBubble($hGraphicT, $hBrushT, $hPenT, $iAngle = 135)
    ; Draw a Triangle
    Local Const $nPI = 3.1415926535897932384626433832795
    Local $Deg, $R, $iX1, $iY1, $iRadius
    Local $iCenter = $nImgWidth / 2

    Local $aPoints[4][2]         ; Create Points Array (Row 0 Col 0 holds row count)
    $aPoints[0][0] = 3          ;Number of points

    ;b
    $iRadius = $nImgWidth / 2
    $Deg = $iAngle
    $R = ($nPI / 2) - ($Deg * ($nPI / 180))
    $iX1 = $iCenter + Cos($R) * $iRadius
    $iY1 = $iCenter - Sin($R) * $iRadius
    $aPoints[2][0] = $iX1
    $aPoints[2][1] = $iY1
    ;ConsoleWrite("b=" & $aPoints[2][0] & ", " & $aPoints[2][1] & @CRLF)

    ;c
    $iRadius = 25
    $Deg = $Deg + 90
    $R = ($nPI / 2) - ($Deg * ($nPI / 180))
    $iX1 = $iCenter + Cos($R) * $iRadius
    $iY1 = $iCenter - Sin($R) * $iRadius
    $aPoints[3][0] = $iX1
    $aPoints[3][1] = $iY1
    ;ConsoleWrite("c=" & $aPoints[3][0] & ", " & $aPoints[3][1] & @CRLF)

    ;a
    $iRadius = 25
    $Deg = $Deg + 180
    $R = ($nPI / 2) - ($Deg * ($nPI / 180))
    $iX1 = $iCenter + Cos($R) * $iRadius
    $iY1 = $iCenter - Sin($R) * $iRadius
    $aPoints[1][0] = $iX1
    $aPoints[1][1] = $iY1
    ;ConsoleWrite("a=" & $aPoints[1][0] & ", " & $aPoints[1][1] & @CRLF)

    _GDIPlus_GraphicsFillPolygon($hGraphicT, $aPoints, $hBrushT)
    _GDIPlus_GraphicsDrawPolygon($hGraphicT, $aPoints, $hPenT)

    ; Draw a circle
    Local $CircleDiameter = 150
    Local $CircleX = ($nImgWidth - $CircleDiameter) / 2
    Local $CircleY = ($nImgWidth - $CircleDiameter) / 2
    _GDIPlus_GraphicsFillEllipse($hGraphicT, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hBrushT)
    _GDIPlus_GraphicsDrawEllipse($hGraphicT, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hPenT)

    ;hide tail
    $iRadius = ($CircleDiameter / 2) + 1
    $Deg = $iAngle
    $R = ($nPI / 2) - ($Deg * ($nPI / 180))
    $iX1 = $iCenter + Cos($R) * $iRadius
    $iY1 = $iCenter - Sin($R) * $iRadius
    $hPenT = _GDIPlus_PenCreate(0xFF00FF00, 14)
    _GDIPlus_GraphicsDrawLine($hGraphicT, $iCenter, $iCenter, $iX1, $iY1, $hPenT)

EndFunc   ;==>DrawBubble
;----------------------------------------------------------------------------------------
Func SpeechBubbleRedraw($hGraphicT, $hImageT, $hBrushT, $hPenT, $iAngle = 135)
    _GDIPlus_GraphicsClear($hGraphicT, 0x00FFFFFF)     ; Clear with white (0xFFFFFFFF) or any other desired color
    DrawBubble($hGraphicT, $hBrushT, $hPenT, $iAngle)
    GUICtrlDelete($cPic)
    $cPic = GUICtrlCreatePic('', 0, 20, $nImgWidth, $nImgWidth)
    GUISetState()
    BitmapToCtrl($hImageT, $cPic)
    _GDIPlus_ImageSaveToFile($hImageT, $outputImage)
EndFunc   ;==>SpeechBubbleRedraw

 

I know that I know nothing

Link to comment
Share on other sites

This is a basic start but this script can be improved.

#include <GDIPlus.au3>

$hMain = GUICreate('Test', 400, 400)
$cPic = GUICtrlCreatePic('', 0, 0, 400, 400)
GUISetState(@SW_SHOW, $hMain)

_GDIPlus_Startup()
$hBubble = SpeechBubble(400, 400, 'Yayyy!' & @CRLF & 'This is my bubble.')

BitmapRotate($hBubble, 45)
BitmapToCtrl($hBubble, $cPic)

Do
Until GUIGetMsg() = -3

_GDIPlus_BitmapDispose($hBubble)
_GDIPlus_Shutdown()

Func SpeechBubble($iW, $iH, $sString, $iBubbleColor = 0xFF1D3557, $iTextColor = 0xFFFFFFFF, $sFontFamily = 'Arial', $iFontSize = 15, $iPadding = 25)
    Local $aTriangle[4][2] = [[3, 0], [10, Int($iH/2)], [40, Int($iH/2) - 20], [40, Int($iH/2) + 20]]
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontFamily)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $iFontSize)
    Local $hFormat = _GDIPlus_StringFormatCreate()
    Local $hBrush = _GDIPlus_BrushCreateSolid($iBubbleColor)
    Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iW, $iH)
    Local $hPath = _GDIPlus_PathCreate(1)
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)
    _GDIPlus_PathAddEllipse($hPath, $iPadding, $iPadding, $iW - 2 * $iPadding, $iH - 2 * $iPadding)
    _GDIPlus_PathAddPolygon($hPath, $aTriangle)
    _GDIPlus_GraphicsFillPath($hGraphics, $hPath, $hBrush)
    _GDIPlus_BrushSetSolidColor($hBrush, $iTextColor)
    _GDIPlus_GraphicsDrawStringEx($hGraphics, $sString, $hFont, $tLayout, $hFormat, $hBrush)
    _GDIPlus_PathDispose($hPath)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_GraphicsDispose($hGraphics)
    Return $hBitmap
EndFunc

Func BitmapRotate(ByRef $hBitmap, $iAngle)
    Local $iW = _GDIPlus_ImageGetWidth($hBitmap)
    Local $iH = _GDIPlus_ImageGetHeight($hBitmap)
    Local $hNew = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hNew)
    Local $hMatrix = _GDIPlus_MatrixCreate()
    _GDIPlus_MatrixTranslate($hMatrix, $iW / 2, $iH / 2)
    _GDIPlus_MatrixRotate($hMatrix, $iAngle)
    _GDIPlus_GraphicsSetTransform($hGraphics, $hMatrix)
    _GDIPlus_MatrixTranslate($hMatrix, -$iW / 2, -$iH / 2)
    _GDIPlus_GraphicsSetTransform($hGraphics, $hMatrix)
    _GDIPlus_GraphicsDrawImage($hGraphics, $hBitmap, 0, 0)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)
    $hBitmap = $hNew
EndFunc

Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc

Or something like this if you don't want to keep tracking your bitmaps for further release of resources:

#include <GDIPlus.au3>

$hMain = GUICreate('Test', 400, 400)
$cPic = GUICtrlCreatePic('', 0, 0, 400, 400)
GUISetState(@SW_SHOW, $hMain)

SpeechBubble($cPic, 400, 400, 'Yayyy!' & @CRLF & 'This is my bubble.', 45)

Do
Until GUIGetMsg() = -3

Func SpeechBubble($cCtrl, $iW, $iH, $sString, $iAngle = 0, $iBubbleColor = 0xFF1D3557, $iTextColor = 0xFFFFFFFF, $sFontFamily = 'Arial', $iFontSize = 15, $iPadding = 25)
    Local $aTriangle[4][2] = [[3, 0], [10, Int($iH/2)], [40, Int($iH/2) - 20], [40, Int($iH/2) + 20]]
    _GDIPlus_Startup()
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontFamily)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $iFontSize)
    Local $hFormat = _GDIPlus_StringFormatCreate()
    Local $hBrush = _GDIPlus_BrushCreateSolid($iBubbleColor)
    Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iW, $iH)
    Local $hPath = _GDIPlus_PathCreate(1)
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)
    _GDIPlus_PathAddEllipse($hPath, $iPadding, $iPadding, $iW - 2 * $iPadding, $iH - 2 * $iPadding)
    _GDIPlus_PathAddPolygon($hPath, $aTriangle)
    _GDIPlus_GraphicsFillPath($hGraphics, $hPath, $hBrush)
    _GDIPlus_BrushSetSolidColor($hBrush, $iTextColor)
    _GDIPlus_GraphicsDrawStringEx($hGraphics, $sString, $hFont, $tLayout, $hFormat, $hBrush)
    BitmapRotate($hBitmap, $iAngle)
    BitmapToCtrl($hBitmap, $cCtrl)
    _GDIPlus_PathDispose($hPath)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_Shutdown()
EndFunc

Func BitmapRotate(ByRef $hBitmap, $iAngle)
    Local $iW = _GDIPlus_ImageGetWidth($hBitmap)
    Local $iH = _GDIPlus_ImageGetHeight($hBitmap)
    Local $hNew = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hNew)
    Local $hMatrix = _GDIPlus_MatrixCreate()
    _GDIPlus_MatrixTranslate($hMatrix, $iW / 2, $iH / 2)
    _GDIPlus_MatrixRotate($hMatrix, $iAngle)
    _GDIPlus_GraphicsSetTransform($hGraphics, $hMatrix)
    _GDIPlus_MatrixTranslate($hMatrix, -$iW / 2, -$iH / 2)
    _GDIPlus_GraphicsSetTransform($hGraphics, $hMatrix)
    _GDIPlus_GraphicsDrawImage($hGraphics, $hBitmap, 0, 0)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)
    $hBitmap = $hNew
EndFunc

Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc

 

Edited by Andreik

When the words fail... music speaks.

Link to comment
Share on other sites

Thanks @Nine@Andreik@ahmet@UEZ@ioa747

I needed some of that smelling salt you all provided.

@ahmet that finally made some sense to me, speaking about the rotation alone, the translation was the biggest part I was missing as per @Nine and @Andreik might have been leading to.

@ioa747 wow, some classic geometry math I have to brush up on.  Is that "hide tail" section drawing over the piece of the border line that I wanted gone? 

I'm going to run with that without knowing much about the speed of the drawing technique. I'll still keep my eye on this post if there are any tweaks or alternatives (one of which I see might just be posted by @Andreik.

 

Thanks all!

Link to comment
Share on other sites

Like I said I am happy with @ioa747 's solution and have that as a crutch now.  Just now I'm putting everyone's minds together I got this code I pieced together and at first I posted that I couldn't understand how it wasn't clearing the picture control. I got it now. This is the alternative I might use.

 

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinAPIHObj.au3>       ; For _GDIPlus_MatrixCreate

Opt('MustDeclareVars', 1)

_Main()

Func _Main()
    Local $aCanvas, $hImage, $hGraphic
    Global $hGUI, $hWnd, $idButton, $cPic
    Global $outputImage = "SpeechBubble.png"
    Global Const $nPI = 3.1415926535897932384626433832795
    Global $fAngle = 0
    Global $nImgWidth = 220
    Global $CircleDiameter = 150
    Global $iCenter = $nImgWidth / 2
    FileDelete($outputImage)

    ; Create GUI
    $hGUI = GUICreate("GDI+", $nImgWidth, 240)
    $idButton = GUICtrlCreateButton("Angle", 0, 0, 100, 20)
    ;$cPic = GUICtrlCreatePic('', 0, 20, $nImgWidth, $nImgWidth)
    $cPic = CreatePictureControl()
    $hWnd = WinGetHandle("GDI+")
    GUISetState()

        ; Create a blank image
        _GDIPlus_Startup()
        $aCanvas = CreateGraphicContext()
        $hImage = $aCanvas[0]
        $hGraphic = $aCanvas[1]
        ; Create fill brushes and draw pens
        Local $hBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00) ; Light green color
        Local $hBrush2 = _GDIPlus_BrushCreateSolid(0xFF0000FF) ; Light blue color
        Global $hPen = _GDIPlus_PenCreate(0xFF8800AA, 2)
        Global $hPen2 = _GDIPlus_PenCreate(0xFFFF0000, 2)


        DrawTriangle($hGraphic, $hBrush, $hPen)
        DrawCircle($hGraphic, $hBrush, $hPen)
        ClearExtraArcLine($hGraphic)
        BitmapToCtrl($hImage, $cPic)

        Do
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE
                    ExitLoop
                Case $idButton
                    Global $fAngle = InputBox( "Enter Angle", "Please enter the angle of the speech bubble pointer", 45)
                    ConsoleWrite("Required Angle: " & $fAngle & @CRLF)
                    SpeechBubbleRedraw($hGraphic, $hImage, $hBrush, $hPen)
            EndSwitch

        Until False ;GUIGetMsg() = $GUI_EVENT_CLOSE

        ; Save the image to $outputImage
        _GDIPlus_ImageSaveToFile($hImage, $outputImage)
        _GDIPlus_BrushDispose($hBrush2)
        _GDIPlus_BrushDispose($hBrush)
        _GDIPlus_GraphicsDispose($hGraphic)
        _GDIPlus_BitmapDispose($hImage)
        _GDIPlus_Shutdown()

        If FileExists($outputImage) Then ShellExecute($outputImage)
EndFunc   ;==>_Main

Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc

Func CreatePictureControl()
    GUICtrlDelete($cPic)
    Local $cPicT = GUICtrlCreatePic('', 0, 20, $nImgWidth, $nImgWidth)
    GUISetState()
    Return $cPicT
EndFunc

Func CreateGraphicContext()
    Local $hImageT = _GDIPlus_BitmapCreateFromScan0($nImgWidth, $nImgWidth)
    Local $hGraphicT = _GDIPlus_ImageGetGraphicsContext($hImageT)
    Local $aCanvas[2]
    $aCanvas[0]=$hImageT
    $aCanvas[1]=$hGraphicT
    Return $aCanvas
EndFunc

Func DrawTriangle($hGraphicT, $hBrushT, $hPenT)
            ; Draw a Triangle
            Global $tWidth = $nImgWidth
            Global $tHeight = 150
            Global $TriangleBase = 60
            Global $TriangleHeight = 60
            Global $TriangleX = 110 - $TriangleBase / 2
            Global $TriangleY = 65 ; $CircleY + $CircleDiameter / 2

            Local $aPoints[4][2] ; Create Points Array (Row 0 Col 0 holds row count)
            $aPoints[0][0] = 3  ;Number of points
            $aPoints[1][0] = $TriangleX
            $aPoints[1][1] = $TriangleY
            $aPoints[2][0] = $TriangleX + $TriangleBase
            $aPoints[2][1] = $TriangleY
            $aPoints[3][0] = $TriangleX + $TriangleBase / 2
            $aPoints[3][1] = $TriangleY - $TriangleHeight
            _GDIPlus_GraphicsFillPolygon($hGraphicT, $aPoints, $hBrushT)
            _GDIPlus_GraphicsDrawPolygon($hGraphicT, $aPoints, $hPenT)

EndFunc

Func DrawCircle($hGraphicT, $hBrushT, $hPenT)
        ; Draw a circle

        Local $CircleX = ($nImgWidth - $CircleDiameter) / 2
        Local $CircleY = ($nImgWidth - $CircleDiameter) / 2
        _GDIPlus_GraphicsFillEllipse ( $hGraphicT, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hBrushT )
        _GDIPlus_GraphicsDrawEllipse ( $hGraphicT, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hPenT )
EndFunc

Func SpeechBubbleRedraw($hGraphicT, $hImageT, $hBrushT, $hPenT)
    ; Create a matrix and rotate the image by $fAngle degrees
        _GDIPlus_GraphicsClear($hGraphicT, 0x00FFFFFF) ; Clear with white (0xFFFFFFFF) or any other desired color

        Local $hMatrix = _GDIPlus_MatrixCreate()
        _GDIPlus_MatrixTranslate($hMatrix,+110,+110)
        _GDIPlus_MatrixRotate($hMatrix, $fAngle, "False")
        _GDIPlus_MatrixTranslate($hMatrix,-110,-110)
        _GDIPlus_GraphicsSetTransform($hGraphicT, $hMatrix)


        DrawTriangle($hGraphicT, $hBrushT, $hPenT)
        DrawCircle($hGraphicT, $hBrushT, $hPenT)
        ClearExtraArcLine($hGraphicT)
        CreatePictureControl() ;Got it
        BitmapToCtrl($hImageT, $cPic)
        _GDIPlus_ImageSaveToFile($hImageT, $outputImage)
EndFunc

Func ClearExtraArcLine($hGraphicT)
    ;Hide tail
    Local  $fAngleT=$fAngle-$fAngle
    Local $iRadius = ($CircleDiameter / 2) + 1
    Local $Deg = $fAngleT
    Local $R = ($nPI / 2) - ($Deg * ($nPI / 180))
    Local $iX1 = $iCenter + Cos($R) * $iRadius
    Local $iY1 = $iCenter - Sin($R) * $iRadius
    Local $hPenT = _GDIPlus_PenCreate(0xFF00FF00, 26)
    _GDIPlus_GraphicsDrawLine($hGraphicT, $iCenter, $iCenter, $iX1, $iY1, $hPenT)
EndFunc

Thanks all!!!!!!!!!

Edited by NassauSky
Link to comment
Share on other sites

It's good that you got your answer. I am just concerned when I see hard coded values like +/- 110. Maybe you can calculate these values with respect to your bitmap/bubble size but if these won't change in different context then never mind, use them as they are.

When the words fail... music speaks.

Link to comment
Share on other sites

Thanks @Andreik ,

 

Updated code with adding text using ideas from your font technique:
 

I don't think I'm going much further with this unless someone has clarity on some parts that might not be the best way of handling the layers or displaying.

#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <WinAPIHObj.au3>       ; For _GDIPlus_MatrixCreate

Opt('MustDeclareVars', 1)

_Main()

Func _Main()
    Local $aCanvas, $hImage, $hGraphic
    Global $hGUI, $hWnd, $idButton, $cPic
    Global $outputImage = "SpeechBubble.png"
    Global Const $nPI = 3.1415926535897932384626433832795
    Global $fAngle = 0
    Global $nImgWidth = 220
    Global $CircleDiameter = 150
    Global $iCenter = $nImgWidth / 2
    FileDelete($outputImage)

    ; Create GUI
    $hGUI = GUICreate("GDI+", $nImgWidth, $nImgWidth+20) ; 20 pixels for button height otherwise it's a square pic ctrl
    $idButton = GUICtrlCreateButton("Angle", 0, 0, 100, 20)
    ;$cPic = GUICtrlCreatePic('', 0, 20, $nImgWidth, $nImgWidth)
    $cPic = CreatePictureControl()
    $hWnd = WinGetHandle("GDI+")
    GUISetState()

        ; Create a blank image
        _GDIPlus_Startup()
        $aCanvas = CreateGraphicContext()
        $hImage = $aCanvas[0]
        $hGraphic = $aCanvas[1]
        ; Create fill brushes and draw pens
        Local $hBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00) ; Light green color
        Local $hBrush2 = _GDIPlus_BrushCreateSolid(0xFF0000FF) ; Light blue color
        Global $hPen = _GDIPlus_PenCreate(0xFF8800AA, 2)
        Global $hPen2 = _GDIPlus_PenCreate(0xFFFF0000, 2)

        DrawTriangle($hGraphic, $hBrush, $hPen)
        DrawCircle($hGraphic, $hBrush, $hPen)
        ClearExtraArcLine($hGraphic)
        BitmapToCtrl($hImage, $cPic)

        Do
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE
                    ExitLoop
                Case $idButton
                    Global $fAngle = InputBox( "Enter Angle", "Please enter the angle of the speech bubble pointer", 45)
                    ConsoleWrite("Required Angle: " & $fAngle & @CRLF)
                    SpeechBubbleRedraw($hGraphic, $hImage, $hBrush, $hPen)
            EndSwitch

        Until False ;GUIGetMsg() = $GUI_EVENT_CLOSE

        ; Save the image to $outputImage
        ;_GDIPlus_ImageSaveToFile($hImage, $outputImage)
        _GDIPlus_BrushDispose($hBrush2)
        _GDIPlus_BrushDispose($hBrush)
        _GDIPlus_GraphicsDispose($hGraphic)
        _GDIPlus_BitmapDispose($hImage)
        _GDIPlus_Shutdown()

        If FileExists($outputImage) Then ShellExecute($outputImage)
EndFunc   ;==>_Main

Func BitmapToCtrl($hBitmap, $cCtrl)
    Local $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)
    _WinAPI_DeleteObject(GUICtrlSendMsg($cCtrl, 0x0172, 0, $hHBITMAP))
    _WinAPI_DeleteObject($hHBITMAP)
EndFunc

Func CreatePictureControl()
    GUICtrlDelete($cPic)
    Local $cPicT = GUICtrlCreatePic('', 0, 20, $nImgWidth, $nImgWidth)
    GUISetState()
    Return $cPicT
EndFunc

Func CreateGraphicContext()
    Local $hImageT = _GDIPlus_BitmapCreateFromScan0($nImgWidth, $nImgWidth)
    Local $hGraphicT = _GDIPlus_ImageGetGraphicsContext($hImageT)
    Local $aCanvas[2]
    $aCanvas[0]=$hImageT
    $aCanvas[1]=$hGraphicT
    Return $aCanvas
EndFunc

Func DrawTriangle($hGraphicT, $hBrushT, $hPenT)
            Global $tWidth = $nImgWidth
            Global $tHeight = 150
            Global $TriangleBase = 60
            Global $TriangleHeight = 60
            Global $TriangleX = $iCenter - $TriangleBase / 2
            Global $TriangleY = 65 ; $CircleY + $CircleDiameter / 2

            Local $aPoints[4][2] ; Create Points Array (Row 0 Col 0 holds row count)
            $aPoints[0][0] = 3  ;Number of points
            $aPoints[1][0] = $TriangleX
            $aPoints[1][1] = $TriangleY
            $aPoints[2][0] = $TriangleX + $TriangleBase
            $aPoints[2][1] = $TriangleY
            $aPoints[3][0] = $TriangleX + $TriangleBase / 2
            $aPoints[3][1] = $TriangleY - $TriangleHeight
            _GDIPlus_GraphicsFillPolygon($hGraphicT, $aPoints, $hBrushT)
            _GDIPlus_GraphicsDrawPolygon($hGraphicT, $aPoints, $hPenT)
EndFunc

Func DrawCircle($hGraphicT, $hBrushT, $hPenT)

        Local $CircleX = ($nImgWidth - $CircleDiameter) / 2
        Local $CircleY = ($nImgWidth - $CircleDiameter) / 2
        _GDIPlus_GraphicsFillEllipse ( $hGraphicT, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hBrushT )
        _GDIPlus_GraphicsDrawEllipse ( $hGraphicT, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hPenT )
EndFunc

Func SpeechBubbleRedraw($hGraphicT, $hImageT, $hBrushT, $hPenT)
    ; Create a matrix and rotate the image by $fAngle degrees
    _GDIPlus_GraphicsClear($hGraphicT, 0x00FFFFFF) ; Clear with white (0xFFFFFFFF) or any other desired color

    Local $hMatrix = _GDIPlus_MatrixCreate()
    _GDIPlus_MatrixTranslate($hMatrix, +$iCenter, +$iCenter)
    _GDIPlus_MatrixRotate($hMatrix, $fAngle, "False")
    _GDIPlus_MatrixTranslate($hMatrix, -$iCenter, -$iCenter)
    _GDIPlus_GraphicsSetTransform($hGraphicT, $hMatrix)
    DrawTriangle($hGraphicT, $hBrushT, $hPenT)
    DrawCircle($hGraphicT, $hBrushT, $hPenT)
    ClearExtraArcLine($hGraphicT)
    CreatePictureControl()
    BitmapToCtrl($hImageT, $cPic)
    ; _GDIPlus_ImageSaveToFile($hImageT, $outputImage)

    Local $aCanvas2 = CreateGraphicContext()
    Local $hImage2 = $aCanvas2[0]
    Local $hGraphic2 = $aCanvas2[1]

    ; Draw text on top of the speech bubble
    Local $iTextColor = 0xFFFFFFFF, $sFontFamily = 'Arial', $iFontSize = 15, $iBubbleColor = 0xFF1D3557
    Local $sString = "Fill In This Field" ; This is a test of a speech bubble to see how much text will fit inside."
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontFamily)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $iFontSize)
    Local $hFormat = _GDIPlus_StringFormatCreate()
    Local $hTextBrush = _GDIPlus_BrushCreateSolid($iBubbleColor)
    Local $tLayout = _GDIPlus_RectFCreate(47, 60, $iCenter+15, $iCenter) ; 47 & 60 best estimation (maybe a formula exists)
    Local $hPath = _GDIPlus_PathCreate(1)
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphic2, 2)
    _GDIPlus_GraphicsDrawStringEx($hGraphic2, $sString, $hFont, $tLayout, $hFormat, $hTextBrush)

    _GDIPlus_BrushDispose($hTextBrush)
    _GDIPlus_FontDispose($hFont)
    BitmapToCtrl($hImage2, $cPic)


    ; Combine both bitmaps into a single one
    Local $aCanvas3 = CreateGraphicContext()
    Local $hImage3 = $aCanvas3[0]
    Local $hGraphic3 = $aCanvas3[1]

    Local $iWidth =  _GDIPlus_ImageGetWidth($hImageT)
    Local $iHeight =  _GDIPlus_ImageGetHeight($hImageT)
    Local $hMergedImage = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $hGraphic3)

    Local $hMergedGraphics = _GDIPlus_ImageGetGraphicsContext($hMergedImage)
    _GDIPlus_GraphicsDrawImageRect($hMergedGraphics, $hImageT, 0, 0, $iWidth, $iHeight)
    _GDIPlus_GraphicsDrawImageRect($hMergedGraphics, $hImage2, 0, 0, $iWidth, $iHeight)

    ; Save the combined image
    _GDIPlus_ImageSaveToFile($hMergedImage, $outputImage)

    ; Clean up resources
    _GDIPlus_GraphicsDispose($hMergedGraphics)
    _GDIPlus_BitmapDispose($hMergedImage)
EndFunc

Func ClearExtraArcLine($hGraphicT)
    ;Hide tail
    Local  $fAngleT=$fAngle-$fAngle
    Local $iRadius = ($CircleDiameter / 2) + 1
    Local $Deg = $fAngleT
    Local $R = ($nPI / 2) - ($Deg * ($nPI / 180))
    Local $iX1 = $iCenter + Cos($R) * $iRadius
    Local $iY1 = $iCenter - Sin($R) * $iRadius
    Local $hPenT = _GDIPlus_PenCreate(0xFF00FF00, 26) ; Not sure why 26 and it's significance
    _GDIPlus_GraphicsDrawLine($hGraphicT, $iCenter, $iCenter, $iX1, $iY1, $hPenT)
EndFunc

 

Edited by NassauSky
Link to comment
Share on other sites

Here my 2 cents.  I liked @ioa747 approach, much simpler not to rotate.

#include <GDIPlus.au3>
#include <GUIConstants.au3>
#include <Math.au3>
#include <WinAPISysWin.au3>

; bubble speech - radian

Opt("MustDeclareVars", True)

Global Const $SIZE = 250, $CENTER = $SIZE / 2, $DIAMETER = 200, $RADIUS = $DIAMETER / 2, $BORDER = 25

DrawBubble(45, 300, 300, 8, 25, "Hello World !" & @CR & "This is " & @ComputerName)

Func DrawBubble($iDegree, $iPosX, $iPosY, $iWidth, $iHeight, $sText)
  _GDIPlus_Startup()
  Local $hGUI = GUICreate("Bubble", $SIZE, $SIZE, $iPosX, $iPosY, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
  GUISetBkColor(0xBADBEE)
  _WinAPI_SetLayeredWindowAttributes($hGUI, 0xBADBEE)
  GUISetState(@SW_SHOW)

  Local $hPen = _GDIPlus_PenCreate(0xFF000000, 3)
  Local $hPenT = _GDIPlus_PenCreate(0xFFEE3333, 4)
  Local $hBrush = _GDIPlus_BrushCreateSolid(0xFFEE3333)

  Local $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
  _GDIPlus_GraphicsSetSmoothingMode($hGraphic, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
  _GDIPlus_GraphicsFillEllipse($hGraphic, $BORDER, $BORDER, $DIAMETER, $DIAMETER, $hBrush)
  _GDIPlus_GraphicsDrawEllipse($hGraphic, $BORDER, $BORDER, $DIAMETER, $DIAMETER, $hPen)

  Local $aPoints[4][2] = [[3]], $iRadian
  $iRadian = _Radian(Mod(450 - $iDegree, 360))
  $aPoints[1][0] = $CENTER + (($RADIUS + $iHeight) * Cos($iRadian))
  $aPoints[1][1] = $CENTER - (($RADIUS + $iHeight) * Sin($iRadian))

  $iRadian = _Radian(Mod(450 - $iDegree - $iWidth, 360))
  $aPoints[2][0] = $CENTER + ($RADIUS * Cos($iRadian))
  $aPoints[2][1] = $CENTER - ($RADIUS * Sin($iRadian))

  $iRadian = _Radian(Mod(450 - $iDegree + $iWidth, 360))
  $aPoints[3][0] = $CENTER + ($RADIUS * Cos($iRadian))
  $aPoints[3][1] = $CENTER - ($RADIUS * Sin($iRadian))

  _GDIPlus_GraphicsFillPolygon($hGraphic, $aPoints, $hBrush)
  _GDIPlus_GraphicsDrawPolygon($hGraphic, $aPoints, $hPen)
  _GDIPlus_GraphicsDrawLine($hGraphic, $aPoints[2][0], $aPoints[2][1], $aPoints[3][0], $aPoints[3][1], $hPenT)

  AddText($hGraphic, $sText)

  Do
  Until GUIGetMsg() = $GUI_EVENT_CLOSE

  _GDIPlus_BrushDispose($hBrush)
  _GDIPlus_PenDispose($hPen)
  _GDIPlus_PenDispose($hPenT)
  _GDIPlus_GraphicsDispose($hGraphic)
  _GDIPlus_Shutdown()
EndFunc   ;==>DrawBubble

Func AddText($hGraphic, $sText, $sFontName = "Comic Sans MS", $fSize = 14, $iColor = 0xFFFFFFFF)
  Local $hBrush = _GDIPlus_BrushCreateSolid($iColor), $hFormat = _GDIPlus_StringFormatCreate()
  _GDIPlus_StringFormatSetAlign($hFormat, 1)   ;center text horizontally + vertically
  _GDIPlus_StringFormatSetLineAlign($hFormat, 1)
  Local $hFamily = _GDIPlus_FontFamilyCreate($sFontName), $hFont = _GDIPlus_FontCreate($hFamily, $fSize)

  ; calculate string rectangle
  Local $iRadian = _Radian(135)     ; -45°
  Local $iLeft = $CENTER + ($RADIUS * Cos($iRadian))
  Local $iTop = $CENTER - ($RADIUS * Sin($iRadian))
  $iRadian = _Radian(315) ; +135°
  Local $iWidth = $CENTER + ($RADIUS * Cos($iRadian)) - $iLeft
  Local $iHeight = $CENTER - ($RADIUS * Sin($iRadian)) - $iTop
  Local $tLayout = _GDIPlus_RectFCreate($iLeft, $iTop, $iWidth, $iHeight)

  _GDIPlus_GraphicsDrawStringEx($hGraphic, $sText, $hFont, $tLayout, $hFormat, $hBrush)
  _GDIPlus_FontDispose($hFont)
  _GDIPlus_FontFamilyDispose($hFamily)
  _GDIPlus_StringFormatDispose($hFormat)
  _GDIPlus_BrushDispose($hBrush)
EndFunc   ;==>AddText

 

Link to comment
Share on other sites

That is great @Nine.  I think I can also use something like this without saving the output to file. I just modified it a little to shrink the bubble for my purposes of pointing to small items on the screen. Also added some code so the font direction doesn't follow the rotation of the bubble and marked up some code. I also changed the smoothing mode since on my screen it looked better with smoothing mode not applied but left the line in case anyone else wants to adjust it accordingly.

 

#include <GDIPlus.au3>
#include <GUIConstants.au3>
#include <Math.au3>
#include <WinAPISysWin.au3>

Opt("MustDeclareVars", True)

Global Const $SIZE = 220, $CENTER = $SIZE / 2, $DIAMETER = 150, $RADIUS = $DIAMETER / 2, $BORDER = 25
Local $fAngle = InputBox("Enter Angle","Please enter tail angle", 45)
DrawBubble($fAngle, 300, 300, 8, 25, "Click the mystery database here.")

Func DrawBubble($iDegree, $iPosX, $iPosY, $iWidth, $iHeight, $sText)
    _GDIPlus_Startup()

    ;-- Create GUI Transparent
    Local $hGUI = GUICreate("Bubble", $SIZE, $SIZE, $iPosX, $iPosY, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
    GUISetBkColor(0xBADBEE)
    _WinAPI_SetLayeredWindowAttributes($hGUI, 0xBADBEE)
    GUISetState(@SW_SHOW)

    ;-- Setup Pens/Brushes (
    Local $hPen = _GDIPlus_PenCreate(0xFF000000, 3)     ; Triangle and Circle Border
    Local $hPenX = _GDIPlus_PenCreate(0xFFEE3333, 26) ; Hide Extra Tail Line
    Local $hBrush = _GDIPlus_BrushCreateSolid(0xFFEE3333)

    ;-- Setup Canvas to Draw on GUI
    Local $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    ;_GDIPlus_GraphicsSetSmoothingMode($hGraphic, 0) ; $GDIP_SMOOTHINGMODE_HIGHQUALITY)

    ;-- Rotate Canvas From Input
    Local Const $nPI = 3.1415926535897932384626433832795
    Local $fAngle = $iDegree
    Local $nImgWidth = 220
    Local $iCenter = $nImgWidth / 2
    Local $hMatrix = _GDIPlus_MatrixCreate()
    _GDIPlus_MatrixTranslate($hMatrix, +$iCenter, +$iCenter)
    _GDIPlus_MatrixRotate($hMatrix, $fAngle, "False")
    _GDIPlus_MatrixTranslate($hMatrix, -$iCenter, -$iCenter)
    _GDIPlus_GraphicsSetTransform($hGraphic, $hMatrix)

    ;-- Create Triangle
    Global $tWidth = $nImgWidth
    Global $tHeight = 150
    Global $TriangleBase = 60
    Global $TriangleHeight = 60
    Global $TriangleX = $iCenter - $TriangleBase / 2
    Global $TriangleY = 65 ; $CircleY + $CircleDiameter / 2

    Local $aPoints[4][2] ; Create Points Array (Row 0 Col 0 holds row count)
    $aPoints[0][0] = 3  ;Number of points
    $aPoints[1][0] = $TriangleX
    $aPoints[1][1] = $TriangleY
    $aPoints[2][0] = $TriangleX + $TriangleBase
    $aPoints[2][1] = $TriangleY
    $aPoints[3][0] = $TriangleX + $TriangleBase / 2
    $aPoints[3][1] = $TriangleY - $TriangleHeight
    _GDIPlus_GraphicsFillPolygon($hGraphic, $aPoints, $hBrush)
    _GDIPlus_GraphicsDrawPolygon($hGraphic, $aPoints, $hPen)

    ;-- Create Circle
    Local $CircleDiameter = 150
    Local $CircleX = ($nImgWidth - $CircleDiameter) / 2
    Local $CircleY = ($nImgWidth - $CircleDiameter) / 2
    _GDIPlus_GraphicsFillEllipse ( $hGraphic, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hBrush )
    _GDIPlus_GraphicsDrawEllipse ( $hGraphic, $CircleX, $CircleY, $CircleDiameter, $CircleDiameter, $hPen )

    ;-- Hide Extra Tail Line
    Local  $fAngleT=$fAngle-$fAngle
    Local $iRadius = ($CircleDiameter / 2) + 1
    Local $Deg = $fAngleT
    Local $R = ($nPI / 2) - ($Deg * ($nPI / 180))
    Local $iX1 = $iCenter + Cos($R) * $iRadius + 1
    Local $iY1 = $iCenter - Sin($R) * $iRadius - 1
    _GDIPlus_GraphicsDrawLine($hGraphic, $iCenter, $iCenter, $iX1, $iY1, $hPenX)

    AddText($hGraphic, $sText , 0) ; 0=Do not have text follow the tails rotation angle

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    ;--Cleanup
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_PenDispose($hPenX)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_Shutdown()
EndFunc   ;==>DrawBubble

Func AddText($hGraphic, $sText, $iDegree = 0, $sFontName = "Comic Sans MS", $fSize = 14, $iColor = 0xFFFFFFFF)
    
    ;-- Rotate Canvas From Input
    Local Const $nPI = 3.1415926535897932384626433832795
    Local $fAngle = $iDegree
    Local $nImgWidth = 220
    Local $CircleDiameter = 150
    Local $iCenter = $nImgWidth / 2
    Local $hMatrix = _GDIPlus_MatrixCreate()
    _GDIPlus_MatrixTranslate($hMatrix, +$iCenter, +$iCenter)
    _GDIPlus_MatrixRotate($hMatrix, $fAngle, "False")
    _GDIPlus_MatrixTranslate($hMatrix, -$iCenter, -$iCenter)
    _GDIPlus_GraphicsSetTransform($hGraphic, $hMatrix)  

    ;-- Setup Font Characteristics
    Local $hBrush = _GDIPlus_BrushCreateSolid($iColor)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontName)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $fSize)
    Local $hFormat = _GDIPlus_StringFormatCreate()
    _GDIPlus_StringFormatSetAlign($hFormat, 1)   ; Center text horizontally + vertically
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)

    ;-- Create String Rectangle Boundary
    Local $iRadian = _Radian(135)     ; -45°
    Local $iLeft = $CENTER + ($RADIUS * Cos($iRadian))
    Local $iTop = $CENTER - ($RADIUS * Sin($iRadian))
    $iRadian = _Radian(315) ; +135°
    Local $iWidth = $CENTER + ($RADIUS * Cos($iRadian)) - $iLeft
    Local $iHeight = $CENTER - ($RADIUS * Sin($iRadian)) - $iTop
    Local $tLayout = _GDIPlus_RectFCreate($iLeft, $iTop, $iWidth, $iHeight)
    ;-- Draw String Inside Rectangle Boundary
    _GDIPlus_GraphicsDrawStringEx($hGraphic, $sText, $hFont, $tLayout, $hFormat, $hBrush)

    ;-- Cleanup
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_BrushDispose($hBrush)
EndFunc   ;==>AddText

 

Link to comment
Share on other sites

Here my simple version of a speech bubble:

#include <GDIPlus.au3>

_GDIPlus_Startup()
Global $iW = 600
Global $iH = 600

Global $hGUI = GUICreate("Test", $iW, $iH)
GUISetBkColor(0x808080)
GUISetState()

_GDIPlus_Startup()
Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
_GDIPlus_GraphicsSetSmoothingMode($hGraphic, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
_GDIPlus_GraphicsSetTextRenderingHint($hGraphic, $GDIP_TEXTRENDERINGHINTANTIALIAS)
_GDIPlus_DrawSpeechBubble($hGraphic, "Blah blah blah" & @CRLF & "blah blah!", 40, -20, 4, 3, 100, "Comic Sans MS", 7, 0xFF000000, 14, 0xFFFFFFFF, 0xFFFF00FF, 0xFF000000, 2, 100, 100)

Do
Until GUIGetMsg() = -3

_GDIPlus_GraphicsDispose($hGraphic)
_GDIPlus_Shutdown()

Exit

;code by UEZ build 2023-10-27
Func _GDIPlus_DrawSpeechBubble($hGraphic, $sString, $fFontSize = 12, $fAngle = -20, $fScaleX = 1.0, $fScaleY = 1.0, $fSize = 100, $sFontname = "Arial", $fLegSize = 7, $iLineColor = 0xFF000000, $fPenSize = 2, _
                               $iFillColor = 0xFFFFFFFF, $iTxtColor = 0xFF000000, $iTxtBorderColor = $iTxtColor, $fPenSizeBorder = 1, $fXOffset = $fPenSize / 2, $fYOffset = $fPenSize / 2)
    Local Const $hPath = _GDIPlus_PathCreate(), $hMatrix = _GDIPlus_MatrixCreate(), $hPen = _GDIPlus_PenCreate($iLineColor, $fPenSize), $hBrush = _GDIPlus_BrushCreateSolid($iFillColor), _
                $hBrush_txt = _GDIPlus_BrushCreateSolid($iTxtColor), $hPen_txt = _GDIPlus_PenCreate($iTxtBorderColor, $fPenSizeBorder),  _
                $hFamily = _GDIPlus_FontFamilyCreate($sFontname), $hFormat = _GDIPlus_StringFormatCreate(), $hFont = _GDIPlus_FontCreate($hFamily, $fFontSize, 0)

    _GDIPlus_PathAddEllipse($hPath, 1, 1, $fSize, $fSize)
    _GDIPlus_PathAddEllipse($hPath, $fSize / 2 - $fLegSize, $fSize - $fSize / 4, $fLegSize, $fSize / 2)
    _GDIPlus_PathWindingModeOutline($hPath, 0, 0.1)
    _GDIPlus_MatrixTranslate($hMatrix, $fSize / 2, $fSize / 2)
    _GDIPlus_MatrixRotate($hMatrix, $fAngle)
    _GDIPlus_MatrixTranslate($hMatrix, -$fSize / 2, -$fSize / 2)
    _GDIPlus_PathTransform($hPath, $hMatrix)
    _GDIPlus_MatrixSetElements($hMatrix, 1, 0, 0, 1, 0, 0) ;reset matrix

    _GDIPlus_MatrixScale($hMatrix, $fScaleX, $fScaleY)
    _GDIPlus_PathTransform($hPath, $hMatrix)
    _GDIPlus_MatrixSetElements($hMatrix, 1, 0, 0, 1, 0, 0) ;reset matrix

    _GDIPlus_MatrixTranslate($hMatrix, $fXOffset, $fYOffset)
    _GDIPlus_PathTransform($hPath, $hMatrix)

    _GDIPlus_GraphicsDrawPath($hGraphic, $hPath, $hPen)
    _GDIPlus_GraphicsFillPath($hGraphic, $hPath, $hBrush)

    _GDIPlus_PathReset($hPath)
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)
    Local $tLayout = _GDIPlus_RectFCreate($fXOffset, $fYOffset, $fSize * $fScaleX, $fSize * $fScaleY)
    _GDIPlus_PathAddString($hPath, $sString, $tLayout, $hFamily, 0, $fFontSize, $hFormat)
    _GDIPlus_GraphicsFillPath($hGraphic, $hPath, $hBrush_txt)
    _GDIPlus_GraphicsDrawPath($hGraphic, $hPath, $hPen_txt)
    ;_GDIPlus_GraphicsDrawStringEx($hGraphic, $sString, $hFont, $tLayout, $hFormat, $hBrush_txt)

    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_PathDispose($hPath)
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_PenDispose($hPen_txt)
    _GDIPlus_MatrixDispose($hMatrix)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_BrushDispose($hBrush_txt)
EndFunc

 

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...