Jump to content

Valentines Hearts


InunoTaishou
 Share

Recommended Posts

Credit's go to @UEZ for his post fromĀ 5 years ago for the GDI+ Heart!

Really, I was just bored... I didn't have a valentine (:'( )so I whipped this up in a couple of hours. Pretty sure I cleaned up all my resources properly but it's late and I'm tired.

$HAPPY_VALENTINES is the string that is shown in the middle of the screen

$MAX_HEART_COUNT controls how many hearts are displayed

When you first run it it will display a tooltip with the % of the string drawn (since it needs to drawn line by line). The larger the heart, the longer it'll take to draw it. Once it's been drawn once it's saved (@TempDir & "\heart.png") and will be reloaded on each startup. It will check to make sure that the heart saved is the size in the current script. If the sizes don't match it deletes the old saved heart and redraws the correct size. (I.e., if the heart saved is 350px wide and the $MAX_HEART_SIZE is 250, it deletes the old heart.png and remakes it the correct size)

; Coded by InunoTaishou
; Credits to UEZ for the code to draw a heart

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <ScreenCapture.au3>
#pragma compile(Icon, "heart.ico")

Opt("MustDeclareVars", 1)
Opt("GUICloseOnEsc", 1)
Opt("TrayIconHide", 1)

_GDIPlus_Startup()

Global Const $HAPPY_VALENTINES = "Happy Valentine's Day AutoIT"
; maximum hearts to be drawn
Global Const $MAX_HEART_COUNT = 50
; bounding rectangle that encloses the above string, width and height
Global Const $VALENTINE_STRING_WIDTH = 400
Global Const $VALENTINE_STRING_HEIGHT = 150
Global Const $MIN_HEART_SIZE = 50
Global Const $MAX_HEART_SIZE = 300
Global Const $RECT_HAPPY_VALENTINES[4] = [(@DesktopWidth / 2) - ($VALENTINE_STRING_WIDTH / 2), (@DesktopHeight / 2) - ($VALENTINE_STRING_HEIGHT / 2), $VALENTINE_STRING_WIDTH, $VALENTINE_STRING_HEIGHT] ; x, y, width, height
Global Const $MIN_TIMEOUT = 3000
Global Const $MAX_TIMEOUT = 7000
Global Const $MIN_VERTICAL_SPEED = 2
Global Const $MAX_VERTICAL_SPEED = 5
Global Const $MIN_HORIZONTAL_OFFSET = 100
Global Const $MAX_HORIZONTAL_OFFSET = 175
Global Const $PNG_HEART_PATH = @TempDir & "\heart.png"
Global Const $HWND_PEN = _GDIPlus_PenCreate(0, 1)
Global Const $HWND_FONT_FAMILY = _GDIPlus_FontFamilyCreate("Segoe UI")
Global Const $HWND_FONT_VALENTINE = _GDIPlus_FontCreate($HWND_FONT_FAMILY, 32)
Global Const $RECT_VALENTINE = _GDIPlus_RectFCreate($RECT_HAPPY_VALENTINES[0], $RECT_HAPPY_VALENTINES[1], $RECT_HAPPY_VALENTINES[2], $RECT_HAPPY_VALENTINES[3])
Global Const $RECT_FPS = _GDIPlus_RectFCreate(0, 0, 50, 16)
Global Const $HWND_FONT_FPS = _GDIPlus_FontCreate($HWND_FONT_FAMILY, 8.5)
Global Const $HWND_STRING_FORMAT_VALENTINE = _GDIPlus_StringFormatCreate()
Global Const $HWND_STRING_FORMAT_FPS = _GDIPlus_StringFormatCreate()
Global Const $HWND_VALENTINE_TEXT_BRUSH = _GDIPlus_BrushCreateSolid(0xFFFF0000)
Global Const $HWND_BK_BRUSH = _GDIPlus_BrushCreateSolid(0xFF1A1A1A)
Global Const $HWND_FPS_BRUSH = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
Global Const $pi = ACos(-1)
Global Const $dy = 30
Global $dist, $p, $l, $r, $col
Global $heart_buffer[$MAX_HEART_COUNT][12]
Global $frmMain = GUICreate("<3", @DesktopWidth, @DesktopHeight, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOPMOST))
Global $fps_counter = 0
Global $fps = 0
Global $hWnd_graphic = _GDIPlus_GraphicsCreateFromHWND($frmMain)
Global $hWnd_bitmap = _GDIPlus_BitmapCreateFromGraphics(@DesktopWidth, @DesktopHeight, $hWnd_graphic)
Global $hWnd_backbuffer = _GDIPlus_ImageGetGraphicsContext($hWnd_bitmap)

GDIPValentinesDay()

Func GDIPValentinesDay()
    Local Const $hWnd_heart_bitmap = CreateHeartBitmap($MAX_HEART_SIZE, ($MAX_HEART_SIZE - 1), ($MAX_HEART_SIZE / 2))

    ; center text
    _GDIPlus_StringFormatSetAlign($HWND_STRING_FORMAT_VALENTINE, 1)
    _GDIPlus_StringFormatSetLineAlign($HWND_STRING_FORMAT_VALENTINE, 1)
    ; set smoothing
    _GDIPlus_GraphicsSetSmoothingMode($hWnd_backbuffer, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
    _GDIPlus_GraphicsSetSmoothingMode($hWnd_graphic, $GDIP_SMOOTHINGMODE_HIGHQUALITY)

    GUISetBkColor(0x000000, $frmMain)
    _WinAPI_SetLayeredWindowAttributes($frmMain, 0x000000, 255)

    GUISetState(@SW_SHOW, $frmMain)

    SetHeartsMap()

    AdlibRegister("CalculateFps", 1000)

    Do
        _GDIPlus_GraphicsClear($hWnd_backbuffer, 0xFF000000)

        For $i = 0 To $MAX_HEART_COUNT - 1
            ; if there is a timer
            If ($heart_buffer[$i][4] <> -1) Then
                ; if the timer has reached the timeout
                If (TimerDiff($heart_buffer[$i][5]) > $heart_buffer[$i][4]) Then
                    ; shrink the heart
                    $heart_buffer[$i][2] -= 1
                    $heart_buffer[$i][3] -= 1
                EndIf
            EndIf

            ; if the heart has a horizontal move
            If ($heart_buffer[$i][7]) Then
                ; if the number of pixels moved in the x position is greater than the max pixels to move horizontally
                If (Abs($heart_buffer[$i][10]) > Abs($heart_buffer[$i][9])) Then
                    If ($heart_buffer[$i][11] > 35) Then
                        ; change direction
                        $heart_buffer[$i][8] *= -1
                        ; reset the jerk counter
                        $heart_buffer[$i][11] = 0
                    Else
                        $heart_buffer[$i][11] += 1
                    EndIf
                EndIf

                ; adjust the x offset
                $heart_buffer[$i][0] += $heart_buffer[$i][8]
                ; adjust how many pixels the heart has moved in that direction
                $heart_buffer[$i][10] += $heart_buffer[$i][8]
            EndIf

            ; if the y position of the heart is <= 0 or the heart width <= 2, reset that heart
            If ($heart_buffer[$i][1] <= ($heart_buffer[$i][2] * -1) Or $heart_buffer[$i][2] <= 2) Then SetHeartStats($i)

            _GDIPlus_GraphicsDrawImageRect($hWnd_backbuffer, $hWnd_heart_bitmap, $heart_buffer[$i][0], $heart_buffer[$i][1], $heart_buffer[$i][2], $heart_buffer[$i][3])

            ; adjust y coordinate of heart
            $heart_buffer[$i][1] -= $heart_buffer[$i][6]
        Next

        _GDIPlus_GraphicsFillRect($hWnd_backbuffer, $RECT_HAPPY_VALENTINES[0], $RECT_HAPPY_VALENTINES[1], $RECT_HAPPY_VALENTINES[2], $RECT_HAPPY_VALENTINES[3], $HWND_BK_BRUSH)
        _GDIPlus_GraphicsDrawStringEx($hWnd_backbuffer, $HAPPY_VALENTINES, $HWND_FONT_VALENTINE, $RECT_VALENTINE, $HWND_STRING_FORMAT_VALENTINE, $HWND_VALENTINE_TEXT_BRUSH)

        _GDIPlus_GraphicsFillRect($hWnd_backbuffer, 0, 0, 50, 16, $HWND_BK_BRUSH)
        _GDIPlus_GraphicsDrawStringEx($hWnd_backbuffer, "FPS: " & $fps_counter, $HWND_FONT_FPS, $RECT_FPS, $HWND_STRING_FORMAT_FPS, $HWND_FPS_BRUSH)

        _GDIPlus_GraphicsDrawImage($hWnd_graphic, $hWnd_bitmap, 0, 0)

        $fps += 1

        Sleep(10)
    Until (GUIGetMsg() = $GUI_EVENT_CLOSE)

    AdlibUnRegister("CalculateFps")

    _GDIPlus_PenDispose($HWND_PEN)
    _GDIPlus_GraphicsDispose($hWnd_graphic)
    _GDIPlus_BitmapDispose($hWnd_bitmap)
    _GDIPlus_BitmapDispose($hWnd_heart_bitmap)
    _GDIPlus_FontFamilyDispose($HWND_FONT_FAMILY)
    _GDIPlus_FontDispose($HWND_FONT_VALENTINE)
    _GDIPlus_StringFormatDispose($HWND_STRING_FORMAT_VALENTINE)
    _GDIPlus_BrushDispose($HWND_VALENTINE_TEXT_BRUSH)
    _GDIPlus_BrushDispose($HWND_BK_BRUSH)
    _GDIPlus_FontDispose($HWND_FONT_FPS)
    _GDIPlus_StringFormatDispose($HWND_STRING_FORMAT_FPS)
    _GDIPlus_BrushDispose($HWND_FPS_BRUSH)
    _GDIPlus_Shutdown()

    GUIDelete($frmMain)
    Return 0
EndFunc   ;==>GDIPValentinesDay

Func CalculateFps()
    $fps_counter = $fps
    $fps = 0
EndFunc   ;==>CalculateFps

Func SetHeartsMap()
    For $i = 0 To $MAX_HEART_COUNT - 1
        SetHeartStats($i)
    Next
EndFunc   ;==>SetHeartsMap

Func SetHeartStats(Const ByRef $iIndex)
    ; determine horizontal direction to move
    Local Const $multiply_by = (Random(0, 1, 1) ? 1 : -1)
    Local $valid_position = False
    Local $y_offset = 0

    ; width of heart
    $heart_buffer[$iIndex][2] = Ceiling(Random($MIN_HEART_SIZE, $MAX_HEART_SIZE, 1) / 10) * 10
    ; height of heart
    $heart_buffer[$iIndex][3] = $heart_buffer[$iIndex][2] - 1

    Do
        ; x position of heart
        $heart_buffer[$iIndex][0] = Random(0, @DesktopWidth, 1)
        ; y position of heart
        $heart_buffer[$iIndex][1] = Random(@DesktopHeight - $MIN_HEART_SIZE - $y_offset, @DesktopHeight, 1)

        For $i = 0 To $MAX_HEART_COUNT - 1
            If ($i = $iIndex) Then ContinueLoop

            ; make sure the heart doesn't start in the same spot as another heart
            If (_WinAPI_PtInRectEx($heart_buffer[$iIndex][0], $heart_buffer[$iIndex][1], $heart_buffer[$i][0], $heart_buffer[$i][1], $heart_buffer[$i][0] + $heart_buffer[$i][2], $heart_buffer[$i][1] + $heart_buffer[$i][3])) Then
                $valid_position = False
                $y_offset += 5
                ExitLoop
            Else
                $valid_position = True
            EndIf
        Next
    Until ($valid_position)

    If (Random(0, 3, 1) = False) Then
        ; add timeout for some hearts
        $heart_buffer[$iIndex][4] = Random($MIN_TIMEOUT, $MAX_TIMEOUT, 1)
        ; init timer for that heart
        $heart_buffer[$iIndex][5] = TimerInit()
    Else
        $heart_buffer[$iIndex][4] = -1
        $heart_buffer[$iIndex][5] = 0
    EndIf

    ; set vertical move speed
    $heart_buffer[$iIndex][6] = Random($MIN_VERTICAL_SPEED, $MAX_VERTICAL_SPEED, 1)

    If (Random(0, 4, 1)) Then
        ; horizontal moving is true
        $heart_buffer[$iIndex][7] = True
        ; set horizontal move speed
        $heart_buffer[$iIndex][8] = Random($MIN_VERTICAL_SPEED / 2, $MAX_VERTICAL_SPEED / 2, 1) * $multiply_by
        ; set the max pixels to move horizontally
        $heart_buffer[$iIndex][9] = Random($MIN_HORIZONTAL_OFFSET, $MAX_HORIZONTAL_OFFSET, 1) * $multiply_by
        ; set the pixels the heart has moved in this direction
        $heart_buffer[$iIndex][10] = 0
        ; set the counter to 0, this controls how long to wait before adjusting direction once it's reached the max horizontal offset. (makes the heart not jerk left/right)
        $heart_buffer[$iIndex][11] = 0
    Else
        ; horizontal moving is false
        $heart_buffer[$iIndex][7] = False
        $heart_buffer[$iIndex][8] = 0
        $heart_buffer[$iIndex][9] = 0
        $heart_buffer[$iIndex][10] = 0
        $heart_buffer[$iIndex][11] = 0
    EndIf
EndFunc   ;==>SetHeartStats

Func CreateHeartBitmap(Const ByRef $iW, Const ByRef $iH, Const ByRef $iRadius)
    Local $hWnd_heart_graphic = _GDIPlus_GraphicsCreateFromHWND($frmMain)
    Local $hWnd_heart_bitmap = _GDIPlus_BitmapCreateFromGraphics($iW, $iH, $hWnd_heart_graphic)
    Local $hWnd_heart_buffer = _GDIPlus_ImageGetGraphicsContext($hWnd_heart_bitmap)
    Local $percent = 0

    If (Not FileExists(@TempDir & "\heart.png")) Then
        ToolTip("Drawing heart, please wait ... 0% complete")
        _GDIPlus_GraphicsSetSmoothingMode($hWnd_heart_buffer, 2)
        _GDIPlus_GraphicsClear($hWnd_heart_buffer, 0x00000000)

        For $y = 1 To $iH
            For $x = 1 To $iW
                $dist = Sqrt((($iW / 2) - $x) ^ 2 + (($iH / 2) - $y) ^ 2)
                $p = ATan2(($iW / 2) - $x, ($iH / 2) - $y)
                $l = Abs($p) / $pi
                $r = (13 * $l - 22 * $l ^ 2 + 10 * $l ^ 3) / (6 - 5 * $l) * $iRadius
                If $dist <= $r Then
                    $col = "0xFFFF0000"
                Else
                    $col = "0x00000000"
                EndIf
                _GDIPlus_PenSetColor($HWND_PEN, $col)
                _GDIPlus_GraphicsDrawRect($hWnd_heart_buffer, $x, $y - $dy, 1, 1, $HWND_PEN)
            Next
            $percent = Round((($y * $iW) / ($iH * $iW)) * 100, 0)
            ToolTip("Drawing heart, please wait ..." & @CRLF & @TAB & @TAB & $percent & "% complete" & @CRLF & "[" & StringPercent($percent) & "]")
        Next
        ToolTip("")
        _GDIPlus_ImageSaveToFile($hWnd_heart_bitmap, $PNG_HEART_PATH)
    Else
        $hWnd_heart_bitmap = _GDIPlus_ImageLoadFromFile($PNG_HEART_PATH)

        ; if the saved image is not the proper size, remake it
        If (_GDIPlus_ImageGetWidth($hWnd_heart_bitmap) <> $MAX_HEART_SIZE) Then
            _GDIPlus_BitmapDispose($hWnd_heart_bitmap)
            _GDIPlus_GraphicsDispose($hWnd_heart_graphic)
            Do
                FileDelete($PNG_HEART_PATH)
            Until (Not FileExists($PNG_HEART_PATH))
            Return CreateHeartBitmap($iW, $iH, $iRadius)
        EndIf
    EndIf

    _GDIPlus_GraphicsDispose($hWnd_heart_graphic)

    Return $hWnd_heart_bitmap
EndFunc   ;==>CreateHeartBitmap

Func StringPercent(Const ByRef $iPercent)
    Local $string_return = ""
    For $i = 0 To $iPercent
        $string_return &= "|"
    Next
    For $i = $iPercent To 100
        $string_return &= " "
    Next
    Return $string_return
EndFunc   ;==>StringPercent

Func ATan2($y, $x)
    Return (2 * ATan($y / ($x + Sqrt($x * $x + $y * $y))))
EndFunc   ;==>ATan2

Most of it should be pretty self explanatory. There's a lot of comments too.

And since I like to see pictures of the code people post, here's mine! (The script is animated, my picture is not ;))

X4WNabQ.png

Ā 

Edit: Moved the Drawn string and rectangle outside the for loop for each heart. Increased FPS dramatically! Lol Also included the .ico and .exe

Valentines Hearts.au3

Valentines Hearts.exe

heart.ico

Edited by InunoTaishou
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

×
×
  • Create New...