QB64 Phoenix Edition
CUR / ANI file loader - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Prolific Programmers (https://qb64phoenix.com/forum/forumdisplay.php?fid=26)
+---- Forum: Petr (https://qb64phoenix.com/forum/forumdisplay.php?fid=52)
+---- Thread: CUR / ANI file loader (/showthread.php?tid=1524)



CUR / ANI file loader - Petr - 03-05-2023

Hey guys.

I found in the depths of the hard disk my older thing, which has only one task - to allow you to use mouse cursors in a way other than the built-in function such as _MOUSESHOW "LINK" and the like. The ANI files have the same format (or very similar), so I included them in the library as well, under the same command.

command:
handle = LOADCURSOR (path\file.cur) or
handle = LOADCURSOR (path\file.ani)
Function to the variable handle returns a positive number of the cursor or animation in the case of ani file.

Then there is the command PUTCURSOR (handle, positionX, positionY) - this command, like the _PUTIMAGE command, places the cursor image on the screen. It is intended for the graphics screen loop when the screen background is refreshed on each cycle. I could, if there was interest, make the modification to a hardware image, then you won't have to monitor the screen refresh, but the _Display command will be necessary.

_Putimage will never work with the handle returned by the LOADCURSOR function!

PUTCURSOR is also a common command and places animations loaded from ANI files in the same way.

Then we have the FREECURSOR command, it releases the loaded images of cursors or animations from memory. Usage: FREECURSOR (handle)

I've gone so far as to allow the decomposition of animation from ANI files into individual frames. For this you need the following 2 functions:

LENCURSOR - works only with ANI files - returns the number of ANI animation frames. Usage:
Frames = LENCURSOR (handle)

And the second function - it will allow you to get a handle from the animation compatible with the _PUTIMAGE command of each frame in the animation. Example for getting the third image in the animation:
Pic3 = DECOMPOSECURSOR (handle, 3)


This is an older program (I had to patch it a lot to get it to work in PE) and I have a feeling that someone on Linux once reported something to me about display issues. If this happens, please go to the cursor.bm file, subroutine PUTCUR, disable all the lines related to the mouse (110, 111, 112), I have a note that this should be the source of the problem at the time.

Finally, I would like to mention the so-called field within a field, I see on the forum that you deal with something like that. I use something like this in this program. So let's break down the cursor.bi file:


TYPE Cursors_internal
    StartOffset AS LONG 'use ANI
    EndOffset AS LONG

    X_reduction AS INTEGER ' CUR X coordinate reduction read from file
    Y_reduction AS INTEGER ' CUR Y coordinate reduction read from file
    Image AS LONG '          own CUR image is saved here
    Flag AS LONG '1 for ANI, 2 for CUR
END TYPE

DIM SHARED ACTIVE_MOUSE_CURSOR ' variable, which memorize new mouse cursor usage. Used in PutCur SUB
REDIM SHARED Internal_Recorded(0) AS Cursors_internal
REDIM SHARED Internal_Recorded_ANIs(0) AS LONG 'frames array


StartOffset.
  What is it? That's exactly it. A field within a field. This value indicates the starting index number in the Internal_Recorded_ANIs field. This is because an animation in an ANI file can have many frames. So how to write them in one field? You just add an auxiliary field and that is the Internal_Recorded_ANIs field.
EndOffset
This is the index value of the Internal_Recorded_ANIs field, it indicates the last index value that belongs to that one particular ANI file.

See how easy it is now to add more and more ANI images?

X_reduction AS INTEGER ' CUR X coordinate reduction read from file
Y_reduction AS INTEGER ' CUR Y coordinate reduction read from file

these values are contained in the CUR and ANI files and specify the number of pixels to shift the display so that the mouse points where the image

  Image AS LONG 'own CUR image is saved here

exactly as the comment says. When you load a CUR file, you get them as one image. This can be added directly to the main field, it's one record for one item, no problem with that.

  Flag AS LONG '1 for ANI, 2 for CUR
this is just a note that is written here by LOADCURSOR, according to which the program knows whether it should take the image from the main or from the auxiliary field

Finally own field
REDIM SHARED Internal_Recorded(0) AS Cursors_internal
  As you can see above: You call the file via its handle, which is the index number of the Internal_Recorder field. From this you will immediately know if it is a CUR or ANI file. For ANI, the first two records will tell you about the images that belong to that particular ANI. Next, learn about display corrections. So you have everything you need Smile


BI, BM, ANI and CUR files in attachment (zip format)

Code: (Select All)
'$include:'cursors.bi'


'2023 easyest at first: Program need folders CUR and ANI contains *.cur filer in CUR directory and *.ani files in ANI directory (or rewrite paths)
'Easy - how use it:

'example how load cursor with yellow arrow (in file CUR\3dgarro.cur)

'You need 32 graphic screen for use:
Screen _NewImage(1024, 768, 32)



'Load cursor to memory:
YellowCursor = LOADCURSOR(".\CUR\3dgarro.cur")
'test if load is correct, value must be higher than zero:
If YellowCursor > 0 Then Print "Cursor load correct" Else Print "Cursor load failed": End
'show this cursor as mouse cursor:


Print _Dest
Do Until RB = -1
    While _MouseInput: Wend
    RB = _MouseButton(2)
    Mx = _MouseX
    MY = _MouseY

    Cls 'reset screen
    Print "Press right mouse button for quit", _Dest

    PUTCURSOR YellowCursor, Mx, MY

    ' _Display
    _Limit 20
Loop
_AutoDisplay

Print "Now freeing your cursor from memory..."
FREECURSOR YellowCursor
Sleep 3

Print "Loading file mentronom.ani"
M = LOADCURSOR("ANI\metronom.ani")
If M > 0 Then Print "Metronom.ani loaded correctly" Else Print "Loading failed"
Sleep 3

RB = 0
Do Until RB = -1
    While _MouseInput: Wend
    RB = _MouseButton(2)
    Mx = _MouseX
    MY = _MouseY

    Cls 'reset screen
    Print "Press right mouse button for quit"
    PUTCURSOR M, Mx, MY
    _Display
    _Limit 20
Loop
_AutoDisplay

ImgF = LENCURSOR(M)
Print "That is not all. You can split the ANI file into individual images:"
Print "First step for this is - how much frames contains ANI file? Use function LENCURSOR!"
Print "Matronom.ani contains "; ImgF; "frames."
Sleep 3
Print "Ok"
Print "Now use function DecomposeCursor (handle, image number):"
Print "Extract all frames:"
Sleep 2
Cls

For all = 1 To ImgF
    Image1 = DECOMPOSECURSOR(M, all)
    _PutImage (300 + stpX, 450), Image1
    stpX = stpX + _Width(Image1) * 2
    _FreeImage Image1
Next
Print "Press any key for continue..."
Sleep
Cls
'continue previous old presentation
















ReDim cursorsList(0) As String '                                array contains drive path and file names

If WIN Then path$ = _CWD$ + "\cursors" Else path$ = "/cursors"
MakeList path$, "*.*", cursorsList() '                          path, mask, array as STRING
Dim Animated_Cursors(UBound(cursorsList)) As Long

Y = 50
my& = _NewImage(1024, 768, 32)
Screen my&
Cls , _RGB32(25, 55, 100)

'                                                               load ANI and CUR files to memory (new - both use the same statements)
For a = 0 To UBound(cursorsList) - 1
    Locate 1, 1: Print "Loading "; SHOW_PERCENTS(a, UBound(cursorsList) - 1); "%"
    Animated_Cursors(a) = LOADCURSOR(cursorsList(a))
Next

path$ = _CWD$ + "\cur\aero_pen_xl.cur"
k = LOADCURSOR(path$) '           load mouse cursor, which is show on the screen with others cursors as mouse


b = a
x = 50: Y = 0 '                                            its because it is better to set it with _SETALPHA or _CLEARCOLOR by user, as need.
sb = 20
If UBound(cursorsList) > 48 Then OnScreenShow = 48 Else OnScreenShow = UBound(cursorsList) '                 cursor to view to one screen: 40
Cls , _RGB32(25, 55, 100)

Do '                                                       create on screen list for all cursors. Some CUR types have NOT UNVISIBLE BACKGROUND.
    Do
        If OnScreenShow > UBound(cursorsList) Then OnScreenShow = 48
        start = OnScreenShow - 48
        If UBound(cursorsList) < 48 Then OnScreenShow = UBound(cursorsList): start = LBound(cursorsList)

        For a = start To OnScreenShow - 1
            Y = Y + 55
            If Y > _Height - 55 Then Y = 55: x = x + 250
            If x > _Width Then x = 50: Y = 0
            _PrintMode _KeepBackground
            Cname$ = Right$(cursorsList(a), LastPos(cursorsList(a), Chr$(92))) + " [" + LTrim$(Str$(a)) + "]" ' _revinstr not exists in time i developed this (or i use older IDE :} )
            If Len(Cname$) > 19 Then Cname$ = "..." + Right$(Cname$, 16)
            _PrintString (x + 70, Y + 5), Cname$
            Line (x, Y)-(x + 48, Y + 48), _RGB32(25, 55, 100), BF
            PUTCURSOR Animated_Cursors(a), x, Y
        Next
        x = 50: Y = 0

        PCopy _Display, 1
        While _MouseInput: Wend
        PUTCURSOR k, _MouseX, _MouseY
        _PrintString (650, 730), "Press space for next or ESC for end"
        _Display
        '        _LIMIT 15
        PCopy 1, _Display
        k& = _KeyDown(32)
        kk& = _KeyDown(27)
    Loop Until k& Or kk&
    If kk& Then Exit Do
    OnScreenShow = OnScreenShow + 40
    _Delay .2
    x = 50: Y = 0
    Cls , _RGB32(25, 55, 100)
    PCopy _Display, 1
Loop

Screen my&


For free = 1 To UBound(cursorsList) - 2 'index cursorslist - 1 is cursor named "k", You can ask to it with "PRINT k" if k is LOADCURSOR output value.
    FREECURSOR free '                    delete all cursors from memory, not "k" cursor
Next
FREECURSOR k

_Source my&
_Dest my&
Cls

vv& = _NewImage(50, 50, 256)
_ClearColor 0, vv&
Color _RGB32(0, 0, 0), _RGB32(255, 255, 255)
If WIN Then
    HourGlas = LOADCURSOR(_CWD$ + "\cursors\hourglas.ani")
    M = LOADCURSOR(_CWD$ + "\cursors\hourgla2.ani") '                loading other cursor with name "m"
Else
    HourGlas = LOADCURSOR(cwd$ + "/cursors/hourglas.ani")
    M = LOADCURSOR(cwd$ + "/cursors/hourgla2.ani")
End If

If HourGlas < 1 Then Beep 'if is image not saved in memory, then beep (inspired with newimage)
Do
    _Dest vv&
    Cls , 255
    PUTCURSOR HourGlas, 0, 0
    _Dest my&
    _PutImage (_Width / 2 - 150, _Height / 2 - 150)-(_Width / 2 + 150, _Height / 2 + 150), vv&, my&
    While _MouseInput: Wend
    PUTCURSOR M, _MouseX, _MouseY
    Color _RGB32(255, 128, 99)
    _PrintString (650, 730), "                  Press ESC for end", my&
    _Limit 20
    _Display
    Cls , _RGB32(0, 0, 0)
Loop While _KeyDown(27) <> -1




'$include:'cursors.bm'


Function SHOW_PERCENTS (num, based)
    SHOW_PERCENTS = Int(num / (based / 100))
End Function



RE: CUR / ANI file loader - mnrvovrfc - 03-05-2023

Let's see if it could help get out of painful boredom with the obsession with (lib)Adwaita on GNOME, or those Babylon cuneiform arrowhead-looking cursors of limited adjustment on KDE and XFCE. Even if only inside a QB64 program window. :/

sdlBasic had a trick like this I think but that mouse cursor didn't work reliably. Too bad the Italian author didn't have many contributors to go further with games and other things to take advantage of the capability.