Results 1 to 3 of 3

Thread: [RESOLVED] Scale and Position Glyph

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    772

    Resolved [RESOLVED] Scale and Position Glyph

    Hello,

    I want to scale and precisely position glyphs in my project.
    I can get it to work, but I have to hard-code the position values.

    I cannot figure out the math to automatically do this to any scale that may be set.

    In this demo project I want to draw the "Upwards Arrow" glyph 1 pixel below the cross-hairs,
    and be able to scale the glyph to whatever size I want.

    Name:  Form.png
Views: 101
Size:  4.3 KB

    Below is complete code for project.
    I have colored red the hard-coded values that I would like to convert to a formula.

    Form code:
    Code:
    Option Explicit
    
    
    Dim gdiplusToken As Long
    
    Dim stat As Long
    
    Dim graphics As Long
    
    Dim brushBlack As Long
    Dim penRed As Long
    Dim penGreen As Long
    
    Dim fontFamily As Long
    
    Public Enum TextJustify
        JustifyCenter
        JustifyLeft
        JustifyRight
    End Enum
    
    
    Private Sub Form_Load()
    
        Form1.Caption = "GDI+"
        Form1.Width = Screen.TwipsPerPixelX * 600
        Form1.Height = Screen.TwipsPerPixelY * 465
        Form1.BackColor = &H8000000F
        Form1.ScaleMode = vbPixels
        
        Picture1.Appearance = 0
        Picture1.Left = 16
        Picture1.Top = 16
        Picture1.Height = 366
        Picture1.Width = 552
        'Picture1.Font = "courier new"
        Picture1.AutoRedraw = True
        Picture1.ScaleMode = vbPixels
        
        Command1.Width = Picture1.Width
        Command1.Height = 25
        Command1.Left = Picture1.Left
        Command1.Top = Picture1.Top + Picture1.Height + 10
        Command1.Caption = "Sacle and Position Glyph"
    
        ' Initialize Windows GDI+
        Dim GdiplusStartupInput As GdiplusStartupInput
        GdiplusStartupInput.GdiplusVersion = 1
        GdiplusStartupInput.DebugEventCallback = 0
        GdiplusStartupInput.SuppressBackgroundThread = False
        GdiplusStartupInput.SuppressExternalCodecs = False
        Dim status As GpStatus
        status = GdiplusStartup(gdiplusToken, GdiplusStartupInput, 0)
        If status <> Ok Then
            MsgBox "Error loading GDI+!", vbCritical
            Call GdiplusShutdown(gdiplusToken)
        End If
        
        Call GraphicsInitialize
    
    End Sub
    
    
    Private Sub Form_Unload(Cancel As Integer)
    
        Call GraphicsUninitialize
        
        ' Clean up resources used by Windows GDI+
        Call GdiplusShutdown(gdiplusToken)
        
    End Sub
    
    
    Private Sub Command1_Click()
    
        'Picture1.Cls
        stat = GdipGraphicsClear(graphics, &HFFFFFFFF)                  'clear to white
        
        Call DrawSymbol(graphics)
    
        Picture1.Refresh
        
    End Sub
    
    
    Public Sub DrawSymbol(graphics As Long)
    
        stat = GdipCreateFontFamilyFromName(StrPtr("Times New Roman"), 0, fontFamily)
                                                                                            
        Dim desiredSize As Single
        desiredSize = 50
        
        Dim sString As String
        sString = ChrW(&H2191)      'Upwards Arrow
    
        Call DrawSymbolString(graphics, _
                              sString, _
                              0, 0, _
                              JustifyCenter, _
                              fontFamily, FontStyle.FontStyleRegular, desiredSize)
                                          
    End Sub
    
    
    Public Sub DrawSymbolString(G As Long, _
                                sString As String, _
                                x As Single, y As Single, _
                                Justify As TextJustify, _
                                fontFamily As Long, _
                                FontStyle As FontStyle, _
                                fontSize As Single)
                                                                                                                   
        '-------------------------------------------------------------------------------
        ' Draw crosshairs
        
        Dim pt As POINTF
        pt.x = Picture1.Width / 2:
        pt.y = 150
    
        stat = GdipDrawLine(G, penRed, pt.x - 20, pt.y, pt.x + 20, pt.y)
        stat = GdipDrawLine(G, penRed, pt.x, pt.y - 20, pt.x, pt.y + 20)
        '-------------------------------------------------------------------------------
    
        '-------------------------------------------------------------------------------
        ' Create a string path
    
        ' Create StringFormat and set
        Dim stringFormatGlyph As Long
        stat = GdipStringFormatGetGenericTypographic(stringFormatGlyph)
        stat = GdipSetStringFormatAlign(stringFormatGlyph, StringAlignmentCenter)
    
        ' Create string path
        Dim pathString As Long
        stat = GdipCreatePath(FillModeWinding, pathString)
    
        ' Set the layout rect for string path
        Dim rct As RECTF
        rct.Left = pt.x         'x
        rct.Top = pt.y - 13     'y
        rct.Right = 0           'width  (0 means no boundary)
        rct.Bottom = 0          'height (0 means no boundary)
    
        ' Add string to path
        stat = GdipAddPathString(pathString, _
                    StrPtr(sString), -1, fontFamily, FontStyleRegular, fontSize, _
                        rct, stringFormatGlyph)
        '-------------------------------------------------------------------------------
    
        '-------------------------------------------------------------------------------
        ' Translate and Scale
        
        Dim matrix As Long
        stat = GdipCreateMatrix(matrix)
        
        Dim scaleY As Single
        'scaleY = 1
        'scaleY = 1.5
        'scaleY = 2
        'scaleY = 3
        scaleY = 4
        
        Dim offsetY As Single
        'offsetY = -23           'when ScaleY = 1
        'offsetY = 34.5          'when ScaleY = 1.5
        'offsetY = 63            'when ScaleY = 2
        'offsetY = 91.5          'when ScaleY = 3
        offsetY = 106           'when ScaleY = 4
        
        stat = GdipTranslateMatrix(matrix, x - 0, y - offsetY, MatrixOrderAppend)
        stat = GdipScaleMatrix(matrix, 1, scaleY, MatrixOrderAppend)
        stat = GdipTransformPath(pathString, matrix)
        '-------------------------------------------------------------------------------
        
        '-------------------------------------------------------------------------------
        ' Draw string path
        
        stat = GdipFillPath(graphics, brushBlack, pathString)      'fill path
        'stat = GdipDrawPath(graphics, penRed, pathString)         'outline path
        '-------------------------------------------------------------------------------
    
        '-------------------------------------------------------------------------------
        ' Clean-up
        
        stat = GdipDeleteStringFormat(stringFormatGlyph)
        stat = GdipDeletePath(pathString)
        stat = GdipDeleteMatrix(matrix)
        '-------------------------------------------------------------------------------
    
    End Sub
    
    
    Public Sub GraphicsInitialize()
        
        ' Create GDI+ objects
    
        stat = GdipCreateFromHDC(Picture1.hdc, graphics)
    
        stat = GdipSetSmoothingMode(graphics, SmoothingModeAntiAlias)           'objects
        stat = GdipSetTextRenderingHint(graphics, TextRenderingHintAntiAlias)   'text
        
        stat = GdipCreateSolidFill(&HFF000000, brushBlack)
        stat = GdipCreatePen1(&HFFFF0000, 1, UnitPixel, penRed)
        stat = GdipCreatePen1(&HFF00FF00, 1, UnitPixel, penGreen)
    
    End Sub
    
    
    Public Sub GraphicsUninitialize()
    
        ' Clean-up GDI+ objects
    
        stat = GdipDeleteFontFamily(fontFamily)
        stat = GdipDeletePen(penRed)
        stat = GdipDeletePen(penGreen)
        stat = GdipDeleteBrush(brushBlack)
        stat = GdipDeleteGraphics(graphics)
    
    End Sub
    Module code:
    Code:
    Option Explicit
    
    
    Public Type POINTF
       x As Single
       y As Single
    End Type
    
    Public Type RECTF
       Left As Single
       Top As Single
       Right As Single
       Bottom As Single
    End Type
    
    Public Type GdiplusStartupInput
       GdiplusVersion As Long
       DebugEventCallback As Long
       SuppressBackgroundThread As Long
       SuppressExternalCodecs As Long
    End Type
    
    
    Public Enum GpStatus
       Ok = 0
       GenericError = 1
       InvalidParameter = 2
       OutOfMemory = 3
       ObjectBusy = 4
       InsufficientBuffer = 5
       NotImplemented = 6
       Win32Error = 7
       WrongState = 8
       Aborted = 9
       FileNotFound = 10
       ValueOverflow = 11
       AccessDenied = 12
       UnknownImageFormat = 13
       FontFamilyNotFound = 14
       FontStyleNotFound = 15
       NotTrueTypeFont = 16
       UnsupportedGdiplusVersion = 17
       GdiplusNotInitialized = 18
       PropertyNotFound = 19
       PropertyNotSupported = 20
    End Enum
    
    Public Enum GpUnit
       UnitWorld
       UnitDisplay
       UnitPixel
       UnitPoint
       UnitInch
       UnitDocument
       UnitMillimeter
    End Enum
    
    Public Enum QualityMode
       QualityModeInvalid = -1
       QualityModeDefault = 0
       QualityModeLow = 1
       QualityModeHigh = 2
    End Enum
    
    Public Enum FontStyle
       FontStyleRegular = 0
       FontStyleBold = 1
       FontStyleItalic = 2
       FontStyleBoldItalic = 3
       FontStyleUnderline = 4
       FontStyleStrikeout = 8
    End Enum
    
    Public Enum StringAlignment
       StringAlignmentNear = 0
       StringAlignmentCenter = 1
       StringAlignmentFar = 2
    End Enum
    
    Public Enum SmoothingMode
       SmoothingModeInvalid = QualityModeInvalid
       SmoothingModeDefault = QualityModeDefault
       SmoothingModeHighSpeed = QualityModeLow
       SmoothingModeHighQuality = QualityModeHigh
       SmoothingModeNone
       SmoothingModeAntiAlias
    End Enum
    
    Public Enum TextRenderingHint
       TextRenderingHintSystemDefault = 0
       TextRenderingHintSingleBitPerPixelGridFit
       TextRenderingHintSingleBitPerPixel
       TextRenderingHintAntiAliasGridFit
       TextRenderingHintAntiAlias
       TextRenderingHintClearTypeGridFit
    End Enum
    
    Public Enum FillMode
       FillModeAlternate
       FillModeWinding
    End Enum
    
    Public Enum MatrixOrder
       MatrixOrderPrepend = 0
       MatrixOrderAppend = 1
    End Enum
    
    
    Public Declare Function GdiplusStartup Lib "gdiplus" (token As Long, inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As GpStatus
    Public Declare Sub GdiplusShutdown Lib "gdiplus" (ByVal token As Long)
    
    Public Declare Function GdipCreateFromHDC Lib "gdiplus" (ByVal hdc As Long, graphics As Long) As GpStatus
    Public Declare Function GdipDeleteGraphics Lib "gdiplus" (ByVal graphics As Long) As GpStatus
    Public Declare Function GdipSetSmoothingMode Lib "gdiplus" (ByVal graphics As Long, ByVal SmoothingMd As SmoothingMode) As GpStatus
    Public Declare Function GdipSetTextRenderingHint Lib "gdiplus" (ByVal graphics As Long, ByVal mode As TextRenderingHint) As GpStatus
    Public Declare Function GdipGraphicsClear Lib "gdiplus" (ByVal graphics As Long, ByVal lColor As Long) As GpStatus
    Public Declare Function GdipDrawLine Lib "gdiplus" (ByVal graphics As Long, ByVal pen As Long, ByVal x1 As Single, ByVal y1 As Single, ByVal x2 As Single, ByVal y2 As Single) As GpStatus
    
    Public Declare Function GdipCreateSolidFill Lib "gdiplus" (ByVal argb As Long, brush As Long) As GpStatus
    Public Declare Function GdipDeleteBrush Lib "gdiplus" (ByVal brush As Long) As GpStatus
    
    Public Declare Function GdipCreatePen1 Lib "gdiplus" (ByVal color As Long, ByVal Width As Single, ByVal unit As GpUnit, pen As Long) As GpStatus
    Public Declare Function GdipDeletePen Lib "gdiplus" (ByVal pen As Long) As GpStatus
    
    Public Declare Function GdipCreateFontFamilyFromName Lib "gdiplus" (ByVal name As Long, ByVal fontCollection As Long, fontFamily As Long) As GpStatus
    Public Declare Function GdipDeleteFontFamily Lib "gdiplus" (ByVal fontFamily As Long) As GpStatus
    
    Public Declare Function GdipStringFormatGetGenericTypographic Lib "gdiplus" (StringFormat As Long) As GpStatus
    Public Declare Function GdipSetStringFormatAlign Lib "gdiplus" (ByVal StringFormat As Long, ByVal align As StringAlignment) As GpStatus
    Public Declare Function GdipDeleteStringFormat Lib "gdiplus" (ByVal StringFormat As Long) As GpStatus
    
    Public Declare Function GdipCreateMatrix Lib "gdiplus" (matrix As Long) As GpStatus
    Public Declare Function GdipDeleteMatrix Lib "gdiplus" (ByVal matrix As Long) As GpStatus
    Public Declare Function GdipTranslateMatrix Lib "gdiplus" (ByVal matrix As Long, ByVal offsetX As Single, ByVal offsetY As Single, ByVal order As MatrixOrder) As GpStatus
    Public Declare Function GdipScaleMatrix Lib "gdiplus" (ByVal matrix As Long, ByVal scaleX As Single, ByVal scaleY As Single, ByVal order As MatrixOrder) As GpStatus
    
    Public Declare Function GdipCreatePath Lib "gdiplus" (ByVal brushmode As FillMode, path As Long) As GpStatus
    Public Declare Function GdipDeletePath Lib "gdiplus" (ByVal path As Long) As GpStatus
    Public Declare Function GdipFillPath Lib "gdiplus" (ByVal graphics As Long, ByVal brush As Long, ByVal path As Long) As GpStatus
    Public Declare Function GdipDrawPath Lib "gdiplus" (ByVal graphics As Long, ByVal pen As Long, ByVal path As Long) As GpStatus
    Public Declare Function GdipAddPathString Lib "gdiplus" (ByVal path As Long, ByVal str As Long, ByVal length As Long, ByVal family As Long, ByVal style As Long, ByVal emSize As Single, layoutRect As RECTF, ByVal StringFormat As Long) As GpStatus
    Public Declare Function GdipTransformPath Lib "gdiplus" (ByVal path As Long, ByVal matrix As Long) As GpStatus

  2. #2
    Lively Member
    Join Date
    Jul 2017
    Posts
    107

    Re: Scale and Position Glyph

    With some help from Claude, the below code works. I haven't read through it, so hopefully it's not doing any wacky LLM ****.

    add this definition:
    Code:
    Public Declare Function GdipGetPathWorldBounds Lib "gdiplus" ( _
        ByVal path As Long, _
        ByRef bounds As RECTF, _
        ByVal matrix As Long, _
        ByVal pen As Long _
    ) As GpStatus

    Change DrawSymbolString:

    Code:
    Public Sub DrawSymbolString(G As Long, _
                                sString As String, _
                                x As Single, y As Single, _
                                Justify As TextJustify, _
                                fontFamily As Long, _
                                FontStyle As FontStyle, _
                                fontSize As Single)
                                                                                                                   
        '-------------------------------------------------------------------------------
        ' Draw crosshairs
        
        Dim pt As POINTF
        pt.x = Picture1.Width / 2
        pt.y = 150
    
        stat = GdipDrawLine(G, penRed, pt.x - 20, pt.y, pt.x + 20, pt.y)
        stat = GdipDrawLine(G, penRed, pt.x, pt.y - 20, pt.x, pt.y + 20)
        '-------------------------------------------------------------------------------
    
        '-------------------------------------------------------------------------------
        ' Create a string path
    
        ' Create StringFormat and set
        Dim stringFormatGlyph As Long
        stat = GdipStringFormatGetGenericTypographic(stringFormatGlyph)
        stat = GdipSetStringFormatAlign(stringFormatGlyph, StringAlignmentCenter)
        
        ' Create string path
        Dim pathString As Long
        stat = GdipCreatePath(FillModeWinding, pathString)
    
        ' Set desired scale factor
        Dim scaleY As Single
        scaleY = 8  ' Change this value as needed
        
        ' Calculate position to account for scaling
        ' When we scale, we need to offset by (1 - scale)/2 to maintain top edge position
        Dim adjustedY As Single
        adjustedY = pt.y + 1 - ((scaleY - 1) / 2)
    
        ' Set the layout rect for string path with the adjusted Y position
        Dim rct As RECTF
        rct.Left = pt.x         'x
        rct.Top = adjustedY     'y - adjusted to account for scaling
        rct.Right = 0           'width  (0 means no boundary)
        rct.Bottom = 0          'height (0 means no boundary)
    
        ' Add string to path
        stat = GdipAddPathString(pathString, _
                    StrPtr(sString), -1, fontFamily, FontStyleRegular, fontSize, _
                        rct, stringFormatGlyph)
        '-------------------------------------------------------------------------------
    
        '-------------------------------------------------------------------------------
        ' Apply scaling
        
        ' Create transformation matrix
        Dim matrix As Long
        stat = GdipCreateMatrix(matrix)
        
        ' Get path bounds before scaling
        Dim pathBounds As RECTF
        stat = GdipGetPathWorldBounds(pathString, pathBounds, 0, 0)
        
        ' We want the top edge of the arrow to stay at pt.y + 1
        ' Calculate the anchor point for scaling
        Dim anchorY As Single
        anchorY = pathBounds.Top
        
        ' Scale around the top edge
        stat = GdipTranslateMatrix(matrix, 0, -anchorY, MatrixOrderAppend)
        stat = GdipScaleMatrix(matrix, 1, scaleY, MatrixOrderAppend)
        stat = GdipTranslateMatrix(matrix, 0, anchorY, MatrixOrderAppend)
        
        ' Apply transform
        stat = GdipTransformPath(pathString, matrix)
        '-------------------------------------------------------------------------------
        
        '-------------------------------------------------------------------------------
        ' Draw string path
        
        stat = GdipFillPath(graphics, brushBlack, pathString)      'fill path
        
        ' Optional: Draw the bounding box for debugging
        'Dim finalBounds As RECTF
        'stat = GdipGetPathWorldBounds(pathString, finalBounds, 0, 0)
        'stat = GdipDrawRectangle(G, penGreen, finalBounds.Left, finalBounds.Top, _
        '                        finalBounds.Right - finalBounds.Left, finalBounds.Bottom - finalBounds.Top)
        '-------------------------------------------------------------------------------
    
        '-------------------------------------------------------------------------------
        ' Clean-up
        
        stat = GdipDeleteStringFormat(stringFormatGlyph)
        stat = GdipDeletePath(pathString)
        stat = GdipDeleteMatrix(matrix)
        '-------------------------------------------------------------------------------
    
    End Sub

  3. #3

    Thread Starter
    Fanatic Member
    Join Date
    Nov 2018
    Posts
    772

    Re: Scale and Position Glyph

    Works perfectly!!

    Thanks bahbahbah
    (Thanks Claude)

    Just curios...
    Claude's code verbatim, and no tweaking on your part?
    (Amazing in my opinion if so)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width