• Re: Implementing Graphics::MeasureCharacterRanges

    From Joe Betz@21:1/5 to All on Sun May 22 22:06:32 2022
    Additionally,
    - a HandlerArray of ExternalHandle objects (`HandleArray withAll: ((1 to: regionCount) collect: [:i | GdiplusRegion new asParameter])`) -> "Invalid parameter"
    - a PointerArray of GdiplusRegion objects (`PointerArray withAll: ((1 to: regionCount) collect: [:i | GdiplusRegion new])`) -> "Object busy"

    Both of these seem more promising than my previous attempts, but still don't work.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Joe Betz@21:1/5 to All on Sun May 22 21:18:18 2022
    I'm trying to use the GDI+ method Graphics::MeasureCharacterRanges but am getting errors from Windows that are no help in debugging the problem.

    I think my issue is with the last argument to the method, which according to https://docs.microsoft.com/en-us/windows/win32/api/gdiplusgraphics/nf-gdiplusgraphics-graphics-measurecharacterranges needs to be a "pointer to an array of Region objects".

    What does that mean in Dolphin?

    A few possibilities I've tried, plus the errors returned:
    - an ExternalHandle -> "Invalid access to memory location"
    - array of ExternalHandle objects -> "Invalid access to memory location"
    - array of GdiplusRegion objects -> "Cannot coerce an Array to lpvoid"

    I also tried creating a subclass of ExternalStructure called GpRegion, but that didn't seem correct since Region it isn't a nice simple C struct but rather a full blown C++ monstrosity. Or whatever it is they call classes.





    GdiplusGraphics>>measureCharacterRanges: aString font: aFont layoutRect: aRectangle format: aStringFormat
    | text regionCount regions status |
    text := aString asUtf16String.
    regionCount := aStringFormat getMeasurableCharacterRangeCount value.
    regions := (1 to: regionCount) collect: [ :index | GdiplusRegion new ].
    (status := GdiplusLibrary default
    gdipMeasureCharacterRanges: self asParameter
    str: text
    length: text size
    font: aFont asParameter
    layoutRect: (RECTF fromRectangle: aRectangle)
    stringFormat: aStringFormat asParameter
    regionCount: regionCount
    regions: regions asParameter) == Ok
    ifFalse: [GdiplusError signal: 'GdipMeasureCharacterRanges failed' with: status].
    ^regions

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Joe Betz@21:1/5 to All on Mon May 23 10:18:16 2022
    Okay, some progress after a full night's sleep.

    I found one off-by-one error in a separate method, then after fixing that, HandleArray seems to be working correctly if used like so:

    regionCount := aStringFormat getMeasurableCharacterRangeCount value.
    regions := (1 to: regionCount) collect: [ :i | GdiplusRegion fromRectangle: aRectangle ].
    regionHandles := HandleArray withAll: (regions collect: [:each| each asParameter])

    Now the problem is with the regions being returned. The first one seems to be correct, but the rest all have negative extents.

    E.g. for the string 'Hello world!', the regions for each character are:

    H -> 3@0 corner: 11@22
    e -> 14@0 corner: 8@22
    l -> 22@0 corner: 4@22
    l -> 26@0 corner: 4@22
    o -> 30@0 corner: 9@22
    39@0 corner: 4@22
    w -> 43@0 corner: 12@22
    o -> 55@0 corner: 9@22
    r -> 64@0 corner: 6@22
    l -> 70@0 corner: 4@22
    d -> 74@0 corner: 9@22
    ! -> 83@0 corner: 5@22

    Origins look correct, but corners are obviously wrong.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Joe Betz@21:1/5 to All on Wed May 25 18:07:20 2022
    Oddly, treating the corners as extents gives a mostly correct region rectangle (i.e., `Rectangle origin: rect origin extent: rect corner`).But mostly correct isn't good enough since I need it to be pixel perfect. Also, it failed completely on strings
    longer than a dozen characters, so I've given up on MeasureCharacterRanges completely.

    Backing up a bit, the whole reason I'm doing this is to implement a custom text editor. Which requires a caret, which requires knowing the position of each and every character in the text editor in order to know exactly where to position it.

    MeasureCharacterRanges is supposed to let me get the region for each character in a string, and seemed like the best way to delegate kerning and other subtle formatting responsibilities to GDI+. But it obviously isn't, so now I've regressed to
    positioning and drawing each individual character inside the text editor, using Graphics::GetKerningPairs to get kerning information, and using `GdiplusStringFormat genericTypographic` to ensure that GDI+ doesn't add any additionally padding.

    So far it's working well enough. And proved yet again that in the world of Win32 programming, if I want something done it's better to do it myself.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)