No sé cómo introducir una variable en una función de la manera correcta Topic is solved

Esta sección es para preguntas sobre programación/scripting usando AutoHotkey.

Moderator: Flipeador

Archa

No sé cómo introducir una variable en una función de la manera correcta

14 Apr 2023, 07:35

Hola gente,

La idea era crear un programa que cambiando la variable "Lados" dibuje un polígono de cualquier número de lados.

El caso es que al introducir la "variable1" en la función, no funciona. Sin embargo, las variables 2, 3 y 4, sí.

No entiendo por qué el resto de variables funcionan y la "variable1" no, ya que a mi parecer son idénticas.

Soy bastante novato con las funciones, sé que estoy haciendo algo mal pero no sé el que.

Code: Select all

;INICIO DEL PROGRAMA------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

CoordMode, Mouse, Screen
#include classCanvas.ahk

RED_pen := new Canvas.Pen(0xFFFF0000, 2)

gui, -Caption +HwndMyGui1Hwnd +AlwaysOnTop +ToolWindow +E0x20
gui, Add, text, w1922 h1082 x-1 y-1 vmainbox hwndMainBox border
gui, Color, 808080 ;262626
gui, show, NA x0 y0 w1920 h1080, Ventana principal
WinSet, Transparent, 255, ahk_id %MyGui1Hwnd%
WinSet, TransColor, 808080, ahk_id %MyGui1Hwnd%

surface1 := new Canvas.Surface(1920,1080)
surface1.Clear(0xFF808080)
surface1.Smooth := "Best"
Viewport := new Canvas.Viewport(MainBox).Attach(Surface1)
ClonedSurface:=Surface1.clone().draw(Surface1)  
Viewport.Refresh()

X0 := 960
Y0 := 540

Radio := 20
Lados := 4
pi := 3.14159265
Grados := 360 / Lados
Incremento := Grados
IndexLados := Lados

loop, %Lados%
{
GradosToRad := Grados * (pi / 180)
Coseno := Cos(GradosToRad)
Seno := Sin(GradosToRad)

X%IndexLados% := (Coseno * Radio) + X0
XIndexLados := X%IndexLados%

Y%IndexLados% := (Seno * Radio) + Y0
YIndexLados := Y%IndexLados%

;------------------------------------------------

string := "[" XIndexLados "," YIndexLados "]"
if (Lados = IndexLados)
string%IndexLados% := string

if (Lados > IndexLados)
string%IndexLados% := string "," stringIndexLados
stringIndexLados := string%IndexLados%

;------------------------------------------------

Grados := Grados + Incremento
IndexLados := IndexLados - 1
}

msgbox, X1 = %X1%`nY1 = %Y1%`n`nX2 = %X2%`nY2 = %Y2%`n`nX3 = %X3%`nY3 = %Y3%`n`nX4 = %X4%`nY4 = %Y4%`n`n

stringInicial := "["
stringFinal := "]"

variable1 := stringInicial stringIndexLados stringFinal															;No funciona		<------

variable2 := [[X1,Y1],[X2,Y2],[X3,Y3],[X4,Y4]]																	;Si funciona
variable3 := [[980,540],[960,520],[940,540],[960,560]]															;Si funciona
variable4 := [[980.000000,540.000000],[960.000000,520.000000],[940.000000,540.000000],[960.000000,560.000000]]	;Si funciona

msgbox, variable1 = %variable1%

ClonedSurface:=Surface1.clone().draw(Surface1)

Surface1.DrawPolygon(Red_pen,[[X1,Y1],[X2,Y2],[X3,Y3],[X4,Y4]])		;Si funciona
;Surface1.DrawPolygon(Red_pen,variable1)							;No funciona	<------
;Surface1.DrawPolygon(Red_pen,variable2)							;Si funciona
;Surface1.DrawPolygon(Red_pen,variable3)							;Si funciona
;Surface1.DrawPolygon(Red_pen,variable4)							;Si funciona

Viewport.Refresh()

;FIN DEL PROGRAMA------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Code: Select all

;INICIO DE classCanvas.ahk------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

#NoEnv

/*
Copyright 2012 Anthony Zhang <[email protected]>

This file is part of Canvas-AHK. Source code is available at <https://github.com/Uberi/Canvas-AHK>.

Canvas-AHK is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
TODO
----

* console output module with colors by reading the bitmap, console functions at http://msdn.microsoft.com/en-us/library/windows/desktop/ms682073(v=vs.85).aspx
* consider color transformations using http://msdn.microsoft.com/en-us/library/windows/desktop/ms533875(v=vs.85).aspx
* effects support with http://msdn.microsoft.com/en-us/library/windows/desktop/ms533971(v=vs.85).aspx
    * GpStatus WINGDIPAPI GdipDrawImageFX(GpGraphics *graphics, GpImage *image, GpRectF *source, GpMatrix *xForm, CGpEffect *effect, GpImageAttributes *imageAttributes, GpUnit srcUnit)
    * GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect *effect, RECT *roi, BOOL useAuxData, VOID **auxData, INT *auxDataSize)
    * Status __stdcall GdipCreateEffect(const GUID guid, CGpEffect **effect)
* opengl backend
* ASCII backend with drawing functions here: http://free.pages.at/easyfilter/bresenham.html
* combine the draw* and fill* functions: DrawPie(Pen) and FillPie(Brush) -> Pie(Pen) and Pie(Brush)
* fold pens into brushes; allow brushes to define widths, fills, etc.
* add hatch brush, texture brush, and linear/radial gradient brush capabilities to Brush class
* use CachedBitmap for animations or faster drawing: http://msdn.microsoft.com/en-us/library/ms533975(v=vs.85).aspx
*/

/*
#Warn All
#Warn LocalSameAsGlobal, Off

i := new Canvas.Surface
i.Load(A_ScriptDir . "\Earthrise.jpg")
s := new Canvas.Surface(400,400)
s.Draw(i)

f := new Canvas.Font("Georgia",18)
s.Text(new Canvas.Brush(0xFFFFFFFF),f,"Earthrise: Dawn of a new era",30,80)

Gui, +LastFound
v := new Canvas.Viewport(WinExist()).Attach(s)

p := new Canvas.Pen(0x80FF0000,10)
t := new Canvas.Pen(0xFF00FF00,1)
b := new Canvas.Brush(0xAA0000FF)

Gui, Show, w400 h400, Canvas Demo
Return

GuiClose:
ExitApp

Tab::
f.Measure("Earthrise: Dawn of a new era",W,H)
s.DrawRectangle(t,30,80,W,H)
v.Refresh()
Return

Space::
s.Clear(0xFFFFFF00)
 .Push()
 .Translate(50,50)
 .Rotate(60)
 .FillRectangle(b,0,0,50,50)
 .Pop()
 .DrawEllipse(p,70,70,100,100)
 .DrawCurve(t,[[10,10],[50,10],[10,50]],True)
 .FillPie(b,100,100,50,50,0,270)
v.Refresh()
Return
*/

class Canvas
{
    static _ := Canvas.Initialize()

    Initialize()
    {
        this.hModule := DllCall("LoadLibrary","Str","gdiplus","UPtr")

        ;initialize the GDI+ library
        VarSetCapacity(StartupInformation,A_PtrSize + 12)
        NumPut(1,StartupInformation,0,"UInt") ;GDI+ version (version 1)
        NumPut(0,StartupInformation,4) ;debug callback (disabled)
        NumPut(0,StartupInformation,A_PtrSize + 4) ;suppress background thread (disabled)
        NumPut(0,StartupInformation,A_PtrSize + 8) ;suppress external image codecs (disabled)
        Token := 0
        this.CheckStatus(DllCall("gdiplus\GdiplusStartup","UPtr*",Token,"UPtr",&StartupInformation,"UPtr",0)
            ,"GdiplusStartup","Could not initialize GDI+ library")
        this.Token := Token
    }

    __Delete()
    {
        ;shut down the GDI+ library
        DllCall("gdiplus\GdiplusShutdown","UPtr",this.Token)
    }

    Lenient()
    {
        this.Surface.CheckStatus := this.Surface.StubCheckStatus
        this.Surface.CheckPen := this.Surface.StubCheckPen
        this.Surface.CheckBrush := this.Surface.StubCheckBrush
        this.Surface.CheckFormat := this.Surface.StubCheckFormat
        this.Surface.CheckLine := this.Surface.StubCheckLine
        this.Surface.CheckRectangle := this.Surface.StubCheckRectangle
        this.Surface.CheckSector := this.Surface.StubCheckSector
        this.Surface.CheckPoint := this.Surface.StubCheckPoint
        this.Surface.CheckPoints := this.Surface.StubCheckPoints

        this.Viewport.CheckStatus := this.Viewport.StubCheckStatus

        this.Pen.CheckStatus := this.Pen.StubCheckStatus

        this.Brush.CheckStatus := this.Brush.StubCheckStatus

        this.Font.CheckStatus := this.Font.StubCheckStatus
    }

    class Viewport
    {
        __New(hWindow)
        {
            this.hWindow := hWindow

            ;create a memory device context for double buffering use
            this.hMemoryDC := DllCall("CreateCompatibleDC","UPtr",0,"UPtr")
            If !this.hMemoryDC
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not create memory device context (error in CreateCompatibleDC)")

            ;subclass window to override WM_PAINT
            this.pCallback := RegisterCallback(this.PaintCallback,"Fast",6)
            If !DllCall("Comctl32\SetWindowSubclass"
                ,"UPtr",hWindow ;window handle
                ,"UPtr",this.pCallback ;callback pointer
                ,"UPtr",hWindow ;subclass ID
                ,"UPtr",0) ;arbitrary data to pass to this particular subclass callback and ID
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not subclass window (error in SetWindowSubclass)")
        }

        __Delete()
        {
            ;remove subclass of window
            If !DllCall("Comctl32\RemoveWindowSubclass","UPtr",this.hWindow,"UPtr",this.pCallback,"UPtr",this.hWindow)
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not remove subclass from window (error in RemoveWindowSubclass)")

            ;free paint callback
            DllCall("GlobalFree","UPtr",this.pCallback,"UPtr")
        }

        Attach(Surface)
        {
            If !DllCall("Comctl32\SetWindowSubclass"
                ,"UPtr",this.hWindow ;window handle
                ,"UPtr",this.pCallback ;callback pointer
                ,"UPtr",this.hWindow ;subclass ID
                ,"UPtr",&this) ;arbitrary data to pass to this particular subclass callback and ID
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not update window subclass (error in SetWindowSubclass)")
            this.Surface := Surface
            this.Width := Surface.Width
            this.Height := Surface.Height
            Return, this
        }

        Detach()
        {
            this.Surface := False
            Return, this
        }

        Refresh(X = 0,Y = 0,W = 0,H = 0)
        {
            If (X < 0 || X > this.Width)
                throw Exception("INVALID_INPUT",A_ThisFunc,"Invalid X-axis coordinate: " . X)
            If (Y < 0 || Y > this.Height)
                throw Exception("INVALID_INPUT",A_ThisFunc,"Invalid Y-axis coordinate: " . Y)
            If (W < 0 || W > this.Width)
                throw Exception("INVALID_INPUT",A_ThisFunc,"Invalid width: " . W)
            If (H < 0 || H > this.Height)
                throw Exception("INVALID_INPUT",A_ThisFunc,"Invalid height: " . W)

            If W = 0
                W := this.Width
            If H = 0
                H := this.Height

            ;flush the GDI+ drawing batch
            this.CheckStatus(DllCall("gdiplus\GdipFlush","UPtr",this.Surface.pGraphics,"UInt",1) ;FlushIntention.FlushIntentionSync
                ,"GdipFlush","Could not flush GDI+ pending rendering operations")

            ;set up rectangle structure representing area to redraw
            VarSetCapacity(Rect,16)
            NumPut(X,Rect,0,"UInt")
            NumPut(Y,Rect,4,"UInt")
            NumPut(X + W,Rect,8,"UInt")
            NumPut(Y + H,Rect,12,"UInt")

            ;trigger redrawing of the window
            If !DllCall("InvalidateRect","UPtr",this.hWindow,"UPtr",&Rect,"UInt",0)
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not add rectangle to update region (error in InvalidateRect)")
            If !DllCall("UpdateWindow","UPtr",this.hWindow)
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not update window (error in UpdateWindow)")

            Return, this
        }

        PaintCallback(Message,wParam,lParam,hWindow,pInstance)
        {
            If Message != 0xF ;WM_PAINT
                Return, DllCall("Comctl32\DefSubclassProc","UPtr",hWindow,"UInt",Message,"UPtr",wParam,"UPtr",lParam,"UPtr") ;call the next handler in the window's subclass chain

            ;prepare window for painting
            VarSetCapacity(PaintStruct,A_PtrSize + 60)
            hWindowDC := DllCall("BeginPaint","UPtr",hWindow,"UPtr",&PaintStruct,"UPtr")
            If !hWindowDC
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not prepare window for painting (error in BeginPaint)")

            this := Object(pInstance) ;obtain the instance

            If this.Surface ;surface attached
            {
                ;obtain dimensions of update region
                X := NumGet(PaintStruct,A_PtrSize + 4,"UInt")
                Y := NumGet(PaintStruct,A_PtrSize + 8,"UInt")
                W := NumGet(PaintStruct,A_PtrSize + 12,"UInt") - X
                H := NumGet(PaintStruct,A_PtrSize + 16,"UInt") - Y

                ;create the GDI bitmap
                hBitmap := 0
                this.CheckStatus(DllCall("gdiplus\GdipCreateHBITMAPFromBitmap","UPtr",this.Surface.pBitmap,"UPtr*",hBitmap,"UInt",0x000000)
                    ,"GdipCreateHBITMAPFromBitmap","Could not convert GDI+ bitmap to GDI bitmap")

                ;select the bitmap into the memory device context
                hOriginalBitmap := DllCall("SelectObject","UPtr",this.hMemoryDC,"UPtr",hBitmap,"UPtr")
                If !hOriginalBitmap
                    throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not select bitmap into memory device context (error in SelectObject)")

                ;transfer bitmap from memory device context to window device context
                If !DllCall("BitBlt","UPtr",hWindowDC,"Int",X,"Int",Y,"Int",W,"Int",H,"UPtr",this.hMemoryDC,"Int",X,"Int",Y,"UInt",0xCC0020) ;SRCCOPY
                    throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not transfer bitmap data from memory device context to window device context (error in BitBlt)")

                ;deselect the bitmap from the memory device context
                If !DllCall("SelectObject","UPtr",this.hMemoryDC,"UPtr",hOriginalBitmap,"UPtr")
                    throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not deselect bitmap from memory device context (error in SelectObject)")

                ;delete the bitmap
                If !DllCall("DeleteObject","UPtr",hBitmap)
                    throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not delete bitmap (error in DeleteObject)")
            }

            ;finish painting window
            DllCall("EndPaint","UPtr",hWindow,"UPtr",&PaintStruct)
            Return, 0
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }
    }

    class Surface
    {
        __New(Width = 1,Height = 1)
        {
            ObjInsert(this,"",Object())

            If Width < 1
                throw Exception("INVALID_INPUT",-1,"Invalid width: " . Width)
            If Height < 1
                throw Exception("INVALID_INPUT",-1,"Invalid height: " . Height)

            pBitmap := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateBitmapFromScan0","Int",Width,"Int",Height,"Int",0,"Int",0x26200A,"UPtr",0,"UPtr*",pBitmap) ;PixelFormat32bppARGB
                ,"GdipCreateBitmapFromScan0","Could not create bitmap")
            this.pBitmap := pBitmap

            ;create a graphics object
            pGraphics := 0
            this.CheckStatus(DllCall("gdiplus\GdipGetImageGraphicsContext","UPtr",this.pBitmap,"UPtr*",pGraphics)
                ,"GdipGetImageGraphicsContext","Could not obtain graphics object")
            this.pGraphics := pGraphics

            this.States := []

            this.Width := Width
            this.Height := Height

            this.Interpolation := "None"
            this.Smooth := "None"
            this.Compositing := "Blend"
        }

        __Get(Key)
        {
            If (Key != "" && Key != "base")
                Return, this[""][Key]
        }

        __Set(Key,Value)
        {
            static InterpolationStyles := {None:   5 ;InterpolationMode.InterpolationModeNearestNeighbor
                                          ,Linear: 6 ;InterpolationMode.InterpolationModeHighQualityBilinear
                                          ,Cubic:  7} ;InterpolationMode.InterpolationModeHighQualityBicubic
            static SmoothStyles := {None: 3 ;SmoothingMode.SmoothingModeNone
                                   ,Good: 4 ;SmoothingMode.SmoothingModeAntiAlias8x4
                                   ,Best: 5} ;SmoothingMode.SmoothingModeAntiAlias8x8
            static CompositingStyles := {Blend:     0 ;CompositingMode.CompositingModeSourceOver
                                        ,Overwrite: 1} ;CompositingMode.CompositingModeSourceCopy
            If (Key = "Interpolation")
            {
                If !InterpolationStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid interpolation mode: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetInterpolationMode","UPtr",this.pGraphics,"UInt",InterpolationStyles[Value])
                    ,"GdipSetInterpolationMode","Could not set interpolation mode")
            }
            Else If (Key = "Smooth")
            {
                If !SmoothStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid smooth mode: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetSmoothingMode","UPtr",this.pGraphics,"UInt",SmoothStyles[Value])
                    ,"GdipSetSmoothingMode","Could not set smooth mode")
            }
            Else If (Key = "Compositing")
            {
                If !CompositingStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid compositing mode: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetCompositingMode","UPtr",this.pGraphics,"UInt",CompositingStyles[Value])
                    ,"GdipSetCompositingMode","Could not set compositing mode")
            }
            this[""][Key] := Value
            Return, Value
        }

        __Delete()
        {
            ;delete graphics object
            Result := DllCall("gdiplus\GdipDeleteGraphics","UPtr",this.pGraphics)
            If Result != 0 ;Status.Ok
            {
                DllCall("gdiplus\GdipDisposeImage","UPtr",this.pBitmap) ;delete bitmap object
                this.CheckStatus(Result,"GdipDeleteGraphics","Could not delete graphics object")
            }

            ;delete bitmap object
            this.CheckStatus(DllCall("gdiplus\GdipDisposeImage","UPtr",this.pBitmap)
                ,"GdipDisposeImage","Could not delete bitmap pointer")
        }

        Load(Path) ;wip: document this
        {
            Attributes := FileExist(Path)
            If !Attributes ;path does not exist
                throw Exception("INVALID_INPUT",-1,"Invalid path: " . Path)
            If InStr(Attributes,"D") ;path is not a file
                throw Exception("INVALID_INPUT",-1,"Invalid file: " . Path)

            ;create bitmap and obtain dimensions
            pBitmap := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateBitmapFromFile", "WStr",Path,"UPtr*",pBitmap) ;wip: should use higher level of exception as should CreateBitmap()
                ,"GdipCreateBitmapFromFile","Could not create bitmap from file")
            Width := 0
            this.CheckStatus(DllCall("gdiplus\GdipGetImageWidth","UPtr",pBitmap,"UInt*",Width)
                ,"GdipGetImageWidth","Could not obtain image width")
            Height := 0
            this.CheckStatus(DllCall("gdiplus\GdipGetImageHeight","UPtr",pBitmap,"UInt*",Height)
                ,"GdipGetImageHeight","Could not obtain image height")

            this.pBitmap := pBitmap
            this.Width := Width
            this.Height := Height
            Return, this
        }

        Save(Path)
        {
            ;wip: implement and document this
            Return, this
        }

        Clone(Width = "",Height = "")
        {
            Copy := new this.base(this.Width,this.Height)
            ;wip: clone the bitmap contents and settings and resize it
            Return, Copy
        }

        Clear(Color = 0x00000000)
        {
            If Color Is Not Integer
                throw Exception("INVALID_INPUT",-1,"Invalid color: " . Color)
            Return, this.CheckStatus(DllCall("gdiplus\GdipGraphicsClear","UPtr",this.pGraphics,"UInt",Color)
                ,"GdipGraphicsClear","Could not clear graphics")
        }

        Text(Brush,Font,Value,X,Y,W = "",H = "")
        {
            this.CheckBrush(Brush)
            this.CheckFont(Font)

            ;determine dimensions automatically if not specified
            If (W = "")
                W := 0
            If (H = "")
                H := 0

            ;create bounding rectangle
            this.CheckRectangle(X,Y,W,H)
            VarSetCapacity(Rectangle,16)
            NumPut(X,Rectangle,0,"Float")
            NumPut(Y,Rectangle,4,"Float")
            NumPut(W,Rectangle,8,"Float")
            NumPut(H,Rectangle,12,"Float")

            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawString"
                ,"UPtr",this.pGraphics ;graphics handle
                ,"WStr",Value ;string value
                ,"Int",-1 ;null terminated
                ,"UPtr",Font.hFont ;font handle
                ,"UPtr",&Rectangle ;bounding rectangle
                ,"UPtr",Font.hFormat ;string format
                ,"UPtr",Brush.pBrush) ;fill brush
                ,"GdipDrawString","Could not draw text")
        }

        GetPixel(X,Y,ByRef Color)
        {
            this.CheckPoint(X,Y)
            If (X < 0 || X > this.Width)
                throw Exception("INVALID_INPUT",-1,"Invalid X-axis coordinate: " . X)
            If (Y < 0 || Y > this.Height)
                throw Exception("INVALID_INPUT",-1,"Invalid Y-axis coordinate: " . Y)

            Return, this.CheckStatus(DllCall("gdiplus\GdipBitmapGetPixel","UPtr",this.pBitmap,"Int",X,"Int",Y,"UInt*",Color)
                ,"GdipBitmapGetPixel","Could not obtain pixel from bitmap")
        }

        SetPixel(X,Y,Color)
        {
            this.CheckPoint(X,Y)
            If (X < 0 || X > this.Width)
                throw Exception("INVALID_INPUT",-1,"Invalid X-axis coordinate: " . X)
            If (Y < 0 || Y > this.Height)
                throw Exception("INVALID_INPUT",-1,"Invalid Y-axis coordinate: " . Y)
            If Color Is Not Integer
                throw Exception("INVALID_INPUT",-1,"Invalid color: " . Color)

            Return, this.CheckStatus(DllCall("gdiplus\GdipBitmapSetPixel","UPtr",this.pBitmap,"Int",X,"Int",Y,"UInt",Color)
                ,"GdipBitmapSetPixel","Could not set bitmap pixel")
        }

        Draw(Surface,X = 0,Y = 0,W = "",H = "",SourceX = 0,SourceY = 0,SourceW = "",SourceH = "")
        {
            If !Surface.pBitmap
                throw Exception("INVALID_INPUT",-1,"Invalid surface: " . Surface)

            If (W = "")
                W := this.Width
            If (H = "")
                H := this.Height
            If (SourceW = "")
                SourceW := Surface.Width
            If (SourceH = "")
                SourceH := Surface.Height

            this.CheckRectangle(X,Y,W,H)
            this.CheckRectangle(SourceX,SourceY,SourceW,SourceH)

            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawImageRectRect","UPtr",this.pGraphics,"UPtr",Surface.pBitmap
                ,"Float",X,"Float",Y,"Float",W,"Float",H
                ,"Float",SourceX,"Float",SourceY,"Float",SourceW,"Float",SourceH
                ,"Int",2,"UPtr",0,"UPtr",0,"UPtr",0) ;Unit.UnitPixel
                ,"GdipDrawImageRectRect","Could not transfer image data to surface")
        }

        DrawArc(Pen,X,Y,W,H,Start,Sweep)
        {
            this.CheckPen(Pen)
            this.CheckSector(X,Y,W,H,Start,Sweep)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawArc","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X,"Float",Y,"Float",W,"Float",H,"Float",Start - 90,"Float",Sweep)
                ,"GdipDrawArc","Could not draw arc")
        }

        DrawCurve(Pen,Points,Closed = False,Tension = 1)
        {
            this.CheckPen(Pen)
            Length := this.CheckPoints(Points,PointArray)
            If Tension Not Between 0 And 1
                throw Exception("INVALID_INPUT",-1,"Invalid curve tension: " . Tension)

            If Closed
                Return, this.CheckStatus(DllCall("gdiplus\GdipDrawClosedCurve2","UPtr",this.pGraphics,"UPtr",Pen.pPen,"UPtr",&PointArray,"Int",Length,"Float",Tension)
                    ,"GdipDrawClosedCurve2","Could not draw curve")
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawCurve2","UPtr",this.pGraphics,"UPtr",Pen.pPen,"UPtr",&PointArray,"Int",Length,"Float",Tension)
                ,"GdipDrawCurve2","Could not draw curve")
        }

        DrawEllipse(Pen,X,Y,W,H)
        {
            this.CheckPen(Pen)
            this.CheckRectangle(X,Y,W,H)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawEllipse","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X,"Float",Y,"Float",W,"Float",H)
                ,"GdipDrawEllipse","Could not draw ellipse")
        }

        DrawPie(Pen,X,Y,W,H,Start,Sweep)
        {
            this.CheckPen(Pen)
            this.CheckSector(X,Y,W,H,Start,Sweep)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawPie","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X,"Float",Y,"Float",W,"Float",H,"Float",Start - 90,"Float",Sweep)
                ,"GdipDrawPie","Could not draw pie")
        }

        DrawPolygon(Pen,Points)
        {
            this.CheckPen(Pen)
            Length := this.CheckPoints(Points,PointArray)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawPolygon","UPtr",this.pGraphics,"UPtr",Pen.pPen,"UPtr",&PointArray,"Int",Length)
                ,"GdipDrawPolygon","Could not draw polygon")
        }

        DrawRectangle(Pen,X,Y,W,H)
        {
            this.CheckPen(Pen)
            this.CheckRectangle(X,Y,W,H)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawRectangle","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X,"Float",Y,"Float",W,"Float",H)
                ,"GdipDrawRectangle","Could not draw rectangle")
        }

        FillCurve(Brush,Points)
        {
            this.CheckBrush(Brush)
            Length := this.CheckPoints(Points,PointArray)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillClosedCurve","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"UPtr",&PointArray,"Int",Length)
                ,"GdipFillClosedCurve","Could not fill curve")
        }

        FillEllipse(Brush,X,Y,W,H)
        {
            this.CheckBrush(Brush)
            this.CheckRectangle(X,Y,W,H)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillEllipse","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"Float",X,"Float",Y,"Float",W,"Float",H)
                ,"GdipFillEllipse","Could not fill ellipse")
        }

        FillPie(Brush,X,Y,W,H,Start,Sweep)
        {
            this.CheckBrush(Brush)
            this.CheckSector(X,Y,W,H,Start,Sweep)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillPie","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"Float",X,"Float",Y,"Float",W,"Float",H,"Float",Start - 90,"Float",Sweep)
                ,"GdipFillPie","Could not fill pie")
        }

        FillPolygon(Brush,Points)
        {
            this.CheckBrush(Brush)
            Length := this.CheckPoints(Points,PointArray)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillPolygon","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"UPtr",&PointArray,"Int",Length)
                ,"GdipFillPolygon","Could not fill polygon")
        }

        FillRectangle(Brush,X,Y,W,H)
        {
            this.CheckBrush(Brush)
            this.CheckRectangle(X,Y,W,H)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillRectangle","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"Float",X,"Float",Y,"Float",W,"Float",H)
                ,"GdipFillRectangle","Could not fill rectangle")
        }

        Line(Pen,X1,Y1,X2,Y2)
        {
            this.CheckPen(Pen)
            this.CheckLine(X1,Y1,X2,Y2)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawLine","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X1,"FLoat",Y1,"Float",X2,"Float",Y2)
                ,"GdipDrawLine","Could not draw line")
        }

        Lines(Pen,Points)
        {
            this.CheckPen(Pen)
            Length := this.CheckPoints(Points,PointArray)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawLines","UPtr",this.pGraphics,"UPtr",Pen.pPen,"UPtr",&PointArray,"Int",Length)
                ,"GdipDrawLines","Could not draw lines")
        }

        Push()
        {
            State := 0
            this.CheckStatus(DllCall("gdiplus\GdipSaveGraphics","UPtr",this.pGraphics,"UInt*",State) ;GraphicsState
                ,"GdipSaveGraphics","Could not save graphics state")
            this.States.Insert(State)
            Return, this
        }

        Pop()
        {
            If !this.States.HasKey(1) ;stack is empty
                throw Exception("INVALID_INPUT",-1,"Invalid transformation stack entries")
            State := this.States.Remove()
            this.CheckStatus(DllCall("gdiplus\GdipRestoreGraphics","UPtr",this.pGraphics,"UInt",State) ;GraphicsState
                ,"GdipRestoreGraphics","Could not restore graphics state")
            Return, this
        }

        Translate(X,Y)
        {
            this.CheckPoint(X,Y)
            Return, this.CheckStatus(DllCall("gdiplus\GdipTranslateWorldTransform","UPtr",this.pGraphics,"Float",X,"Float",Y,"UInt",0) ;MatrixOrder.MatrixOrderPrepend
                ,"GdipTranslateWorldTransform","Could not apply translation matrix")
        }

        Rotate(Angle)
        {
            If Angle Is Not Number
                throw Exception("INVALID_INPUT",-1,"Invalid angle: " . Angle)
            Return, this.CheckStatus(DllCall("gdiplus\GdipRotateWorldTransform","UPtr",this.pGraphics,"Float",Angle,"UInt",0) ;MatrixOrder.MatrixOrderPrepend
                ,"GdipRotateWorldTransform","Could not apply rotation matrix")
        }

        Scale(X,Y)
        {
            this.CheckPoint(X,Y)
            Return, this.CheckStatus(DllCall("gdiplus\GdipScaleWorldTransform","UPtr",this.pGraphics,"Float",X,"Float",Y,"UInt",0) ;MatrixOrder.MatrixOrderPrepend
                ,"GdipScaleWorldTransform","Could not apply scale matrix")
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        StubCheckPen(Pen)
        {
        }

        StubCheckBrush(Brush)
        {
        }

        StubCheckFormat(Brush)
        {
        }

        StubCheckLine(X1,Y1,X2,Y2)
        {
        }

        StubCheckRectangle(X,Y,W,H)
        {
        }

        StubCheckSector(X,Y,W,H,Start,Sweep)
        {
        }

        StubCheckPoint(X,Y)
        {
        }

        StubCheckPoints(Points,ByRef PointArray)
        {
            Length := Points.MaxIndex()
            VarSetCapacity(PointArray,Length << 3)
            Offset := 0
            Loop, %Length%
            {
                Point := Points[A_Index]
                NumPut(Point[1],PointArray,Offset,"Float"), Offset += 4
                NumPut(Point[2],PointArray,Offset,"Float"), Offset += 4
            }
            Return, Length
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }

        CheckPen(Pen)
        {
            If !Pen.pPen
                throw Exception("INVALID_INPUT",-2,"Invalid pen: " . Pen)
        }

        CheckBrush(Brush)
        {
            If !Brush.pBrush
                throw Exception("INVALID_INPUT",-2,"Invalid brush: " . Brush)
        }

        CheckFont(Font)
        {
            If !(Font.hFontFamily && Font.hFont && Font.hFormat)
                throw Exception("INVALID_INPUT",-2,"Invalid font: " . Font)
        }

        CheckLine(X1,Y1,X2,Y2)
        {
            If X1 Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate 1: " . X1)
            If Y1 Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate 1: " . Y1)
            If X2 Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate 2: " . X2)
            If Y2 Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate 2: " . Y2)
        }

        CheckRectangle(X,Y,W,H)
        {
            If X Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate: " . X)
            If Y Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate: " . Y)
            If W < 0
                throw Exception("INVALID_INPUT",-2,"Invalid width: " . W)
            If H < 0
                throw Exception("INVALID_INPUT",-2,"Invalid height: " . H)
        }

        CheckSector(X,Y,W,H,Start,Sweep)
        {
            If X Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate: " . X)
            If Y Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate: " . Y)
            If W < 0
                throw Exception("INVALID_INPUT",-2,"Invalid width: " . W)
            If H < 0
                throw Exception("INVALID_INPUT",-2,"Invalid height: " . H)
            If Start Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid start angle: " . Start)
            If Sweep Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid sweep angle: " . Sweep)
        }

        CheckPoint(X,Y)
        {
            If X Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate: " . X)
            If Y Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate: " . Y)
        }

        CheckPoints(Points,ByRef PointArray)
        {
            Length := Points.MaxIndex()
            If !Length
                throw Exception("INVALID_INPUT",-2,"Invalid point set: " . Points)
            VarSetCapacity(PointArray,Length << 3)
            Offset := 0
            Loop, %Length%
            {
                Point := Points[A_Index]
                If !IsObject(Point)
                    throw Exception("INVALID_INPUT",-2,"Invalid point: " . Point)
                PointX := Point[1]
                PointY := Point[2]
                If PointX Is Not Number
                    throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate: " . PointX)
                If PointY Is Not Number
                    throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate: " . PointX)

                NumPut(PointX,PointArray,Offset,"Float"), Offset += 4
                NumPut(PointY,PointArray,Offset,"Float"), Offset += 4
            }
            Return, Length
        }
    }

    class Pen
    {
        __New(Color = 0xFFFFFFFF,Width = 1)
        {
            If Color Is Not Integer
                throw Exception("INVALID_INPUT",-1,"Invalid color: " . Color)
            If Width Is Not Number
                throw Exception("INVALID_INPUT",-1,"Invalid width: " . Width)

            ObjInsert(this,"",Object())

            ;create the pen
            pPen := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreatePen1","UInt",Color,"Float",Width,"UInt",2,"UPtr*",pPen) ;Unit.UnitPixel
                ,"GdipCreatePen1","Could not create pen")
            this.pPen := pPen

            ;set properties
            this.Color := Color
            this.Width := Width
            this.Join := "Miter"
            this.Type := "Solid"
            this.StartCap := "Flat"
            this.EndCap := "Flat"
        }

        __Delete()
        {
            ;delete the pen
            this.CheckStatus(DllCall("gdiplus\GdipDeletePen","UPtr",this.pPen)
                ,"GdipDeletePen","Could not delete pen")
        }

        __Get(Key)
        {
            If (Key != "" && Key != "base")
                Return, this[""][Key]
        }

        __Set(Key,Value)
        {
            static JoinStyles := Object("Miter",0 ;LineJoin.LineJoinMiter
                                       ,"Bevel",1 ;LineJoin.LineJoinBevel
                                       ,"Round",2) ;LineJoin.LineJoinRound
            static TypeStyles := Object("Solid",0 ;DashStyleSolid
                                       ,"Dash",1 ;DashStyleDash
                                       ,"Dot",2 ;DashStyleDot
                                       ,"DashDot",3) ;DashStyleDashDot
            static CapStyles := Object("Flat",0 ;LineCap.LineCapFlat
                                      ,"Square",1 ;LineCap.LineCapSquare
                                      ,"Round",2 ;LineCap.LineCapRound
                                      ,"Triangle",3) ;LineCap.LineCapTriangle
            If (Key = "Color") ;set pen color
            {
                If Value Is Not Integer
                    throw Exception("INVALID_INPUT",-1,"Invalid color: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenColor","UPtr",this.pPen,"UInt",Value)
                    ,"GdipSetPenColor","Could not set pen color")
            }
            Else If (Key = "Width") ;set pen width
            {
                If Value Is Not Number
                    throw Exception("INVALID_INPUT",-1,"Invalid width: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenWidth","UPtr",this.pPen,"Float",Value)
                    ,"GdipSetPenWidth","Could not set pen width")
            }
            Else If (Key = "Join") ;set pen line join style
            {
                If !JoinStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid pen join: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenLineJoin","UPtr",this.pPen,"UInt",JoinStyles[Value])
                    ,"GdipSetPenLineJoin","Could not set pen join")
            }
            Else If (Key = "Type") ;set pen type
            {
                If !TypeStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid pen type: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenDashStyle","UPtr",this.pPen,"UInt",TypeStyles[Value])
                    ,"GdipSetPenDashStyle","Could not set pen type")
            }
            Else If (Key = "StartCap") ;set pen start cap
            {
                If !CapStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid pen start cap: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenStartCap","UPtr",this.pPen,"UInt",CapStyles[Value])
                    ,"GdipSetPenStartCap","Could not set pen start cap")
            }
            Else If (Key = "EndCap") ;set pen end cap
            {
                If !CapStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid pen end cap: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenEndCap","UPtr",this.pPen,"UInt",CapStyles[Value])
                    ,"GdipSetPenEndCap","Could not set pen end cap")
            }
            this[""][Key] := Value
            Return, Value
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }
    }

    class Brush
    {
        __New(Color = 0xFFFFFFFF)
        {
            If Color Is Not Integer
                throw Exception("INVALID_INPUT",-1,"Invalid color: " . Color)

            ObjInsert(this,"",Object())

            ;create the brush
            pBrush := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateSolidFill","UInt",Color,"UPtr*",pBrush)
                ,"GdipCreateSolidFill","Could not create brush")
            this.pBrush := pBrush

            this.Color := Color
        }

        __Delete()
        {
            ;delete the brush
            this.CheckStatus(DllCall("gdiplus\GdipDeleteBrush","UPtr",this.pBrush)
                ,"GdipDeleteBrush","Could not delete brush")
        }

        __Get(Key)
        {
            If (Key != "" && Key != "base")
                Return, this[""][Key]
        }

        __Set(Key,Value)
        {
            If (Key = "Color") ;set brush color
            {
                If Value Is Not Integer
                    throw Exception("INVALID_INPUT",-1,"Invalid color: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetSolidFillColor","UPtr",this.pBrush,"UInt",Value)
                    ,"GdipSetSolidFillColor","Could not set brush color")
            }
            this[""][Key] := Value
            Return, Value
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }
    }
    
    class Font
    {
        static _ := Canvas.Font.Initialize()

        Initialize()
        {
            ;create device context for graphics object
            this.hDC := DllCall("CreateCompatibleDC","UPtr",0,"UPtr")
            If !this.hDC
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not create device context (error in CreateCompatibleDC)")

            ;create a graphics object
            pGraphics := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateFromHDC","UPtr",this.hDC,"UPtr*",pGraphics)
                ,"GdipCreateFromHDC","Could not create graphics object")
            this.pGraphics := pGraphics
        }

        __New(Typeface,Size)
        {
            If Size Is Not Number
                throw Exception("INVALID_INPUT",-1,"Invalid size: " . Size)
            If Size <= 0
                throw Exception("INVALID_INPUT",-1,"Invalid size: " . Size)

            ObjInsert(this,"",Object())

            ;create string format
            hFormat := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateStringFormat","Int",0x800,"Int",0,"Ptr*",hFormat) ;StringFormat.StringFormatFlagsMeasureTrailingSpaces, LANG_NEUTRAL
                ,"GdipCreateStringFormat","Could not create string format")

            this.hFormat := hFormat

            this[""].Typeface := Typeface
            this[""].Size := Size

            this[""].Bold := False
            this[""].Italic := False
            this[""].Underline := False
            this[""].Strikeout := False

            this.UpdateFontFamily()
            this.UpdateFont()
        }

        UpdateFontFamily()
        {
            ;delete previous font family if present
            If this.hFontFamily
                this.CheckStatus(DllCall("gdiplus\GdipDeleteFontFamily","UPtr",this.hFontFamily)
                    ,"GdipDeleteFontFamily","Could not delete font family")

            ;create font family
            hFontFamily := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateFontFamilyFromName","WStr",this.Typeface,"UPtr",0,"UPtr*",hFontFamily)
                ,"GdipCreateFontFamilyFromName","Could not create font family")
            this.hFontFamily := hFontFamily
        }

        UpdateFont()
        {
            ;delete previous font if present
            If this.hFont
                this.CheckStatus(DllCall("gdiplus\GdipDeleteFont","UPtr",this.hFont)
                    ,"GdipDeleteFont","Could not delete font")

            ;determine font style
            Style := 0 ;FontStyle.FontStyleRegular
            If this.Bold
                Style |= 1 ;FontStyle.FontStyleBold
            If this.Italic
                Style |= 2 ;FontStyle.FontStyleItalic
            If this.Underline
                Style |= 4 ;FontStyle.FontStyleUnderline
            If this.Strikeout
                Style |= 8 ;FontStyle.FontStyleStrikeout

            ;create font
            hFont := 0, this.CheckStatus(DllCall("gdiplus\GdipCreateFont","UPtr",this.hFontFamily,"Float",this.Size,"Int",Style,"UInt",2,"UPtr*",hFont) ;Unit.UnitPixel
                    ,"GdipCreateFont","Could not create font")
            this.hFont := hFont
        }

        __Delete()
        {
            ;delete font
            Result := DllCall("gdiplus\GdipDeleteFont","UPtr",this.hFont)
            If Result != 0
            {
                DllCall("gdiplus\GdipDeleteFontFamily","UPtr",this.hFontFamily) ;delete font family
                DllCall("gdiplus\GdipDeleteStringFormat","UPtr",this.hFormat) ;delete string format
                this.CheckStatus(Result,"GdipDeleteFont","Could not delete font")
                Return
            }

            ;delete font family
            Result := DllCall("gdiplus\GdipDeleteFontFamily","UPtr",this.hFontFamily)
            If Result != 0 ;Status.Ok
            {
                DllCall("gdiplus\GdipDeleteStringFormat","UPtr",this.hFormat) ;delete string format
                this.CheckStatus(Result,"GdipDeleteFontFamily","Could not delete font family")
                Return
            }

            ;delete string format
            this.CheckStatus(DllCall("gdiplus\GdipDeleteStringFormat","UPtr",this.hFormat)
                ,"GdipDeleteStringFormat","Could not delete string format")
        }

        __Get(Key)
        {
            If (Key != "" && Key != "base")
                Return, this[""][Key]
        }

        __Set(Key,Value)
        {
            static AlignStyles := Object("Left",  0  ;StringAlignment.StringAlignmentNear
                                        ,"Center",1  ;StringAlignment.StringAlignmentCenter
                                        ,"Right", 2) ;StringAlignment.StringAlignmentFar
            If (Key = "Size")
            {
                If Value Is Not Number
                    throw Exception("INVALID_INPUT",-1,"Invalid size: " . Value)
                If Value <= 0
                    throw Exception("INVALID_INPUT",-1,"Invalid size: " . Value)
                this[""].Size := Value
                this.UpdateFont()
            }
            Else If (Key = "Typeface")
            {
                this[""].Typeface := Value
                this.UpdateFontFamily()
                this.UpdateFont()
            }
            Else If (Key = "Bold" || Key = "Italic" || Key = "Underline" || Key = "Strikeout")
            {
                this[""][Key] := Value
                this.UpdateFont()
            }
            Else If (Key = "Align")
            {
                If !AlignStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid alignment: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetStringFormatAlign","UPtr",this.hFormat,"UInt",AlignStyles[Value])
                    ,"GdipSetStringFormatAlign","Could not set string format alignment")
                this[""].Align := Value
            }
            Else
                this[""][Key] := Value
            Return, Value
        }

        Measure(Value,ByRef Width,ByRef Height)
        {
            VarSetCapacity(Rectangle,16,0)
            VarSetCapacity(Bounds,16)
            this.CheckStatus(DllCall("gdiplus\GdipMeasureString"
                ,"UPtr",this.base.pGraphics
                ,"WStr",Value ;string value
                ,"Int",-1 ;null terminated
                ,"UPtr",this.hFont ;font handle
                ,"UPtr",&Rectangle ;input bounds
                ,"UPtr",this.hFormat ;string format
                ,"UPtr",&Bounds ;output bounds
                ,"UPtr",0 ;output number of characters that can fit in input bounds
                ,"UPtr",0) ;output number of lines that can fit in input bounds
                ,"GdipMeasureString","Could not measure text bounds")
            Width := NumGet(Bounds,8,"Float")
            Height := NumGet(Bounds,12,"Float")
            Return, this
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }
    }
}

;FIN DE classCanvas.ahk------------------------------------------------------------------------------------------------------------
[Mod edit: [code][/code] tags added.]
Last edited by gregster on 14 Apr 2023, 22:25, edited 1 time in total.
Archa

Re: No sé cómo introducir una variable en una función de la manera correcta

14 Apr 2023, 22:20

Quería puntualizar que son dos archivos, el programa original y classCanvas.ahk. Pensaba que se separaría al postearlo.

Para que funcione, o se elimina la segunda línea "#include classCanvas.ahk", o se divide en 2 archivos.
Archa

Re: No sé cómo introducir una variable en una función de la manera correcta  Topic is solved

15 Apr 2023, 00:57

Ok, solucionado. Al parecer había que usar Arrays en lugar de strings

Para modificar el polígono usen las flechas del teclado.

Code: Select all

;INICIO DEL PROGRAMA------------------------------------------------------------------------------------------------------------

#NoEnv 

CoordMode, Mouse, Screen

RED_pen := new Canvas.Pen(0xFFFF0000, 2)

gui, -Caption +HwndMyGui1Hwnd +AlwaysOnTop +ToolWindow +E0x20
gui, Add, text, w1922 h1082 x-1 y-1 vmainbox hwndMainBox border
gui, Color, 808080 ;262626
gui, show, NA x0 y0 w1920 h1080, Ventana principal
WinSet, Transparent, 255, ahk_id %MyGui1Hwnd%
WinSet, TransColor, 808080, ahk_id %MyGui1Hwnd%

surface1 := new Canvas.Surface(1920,1080)
surface1.Clear(0xFF808080)
surface1.Smooth := "Best"
Viewport := new Canvas.Viewport(MainBox).Attach(Surface1)
ClonedSurface:=Surface1.clone().draw(Surface1)  
Viewport.Refresh()

X0 := 960
Y0 := 540

Radio := 20
Lados := 5
pi := 3.14159265

MyLabel:
{
	if (Lados < 2)
	{
		Lados := 2
	}
	if (Radio < 20)
	{
		Radio := 20
	}
	Grados := 360 / Lados
	Incremento := Grados
	IndexLados := Lados
	Array := []
	loop, %Lados%
	{
		GradosToRad := Grados * (pi / 180)
		Coseno := Cos(GradosToRad)
		Seno := Sin(GradosToRad)

		X%IndexLados% := (Coseno * Radio) + X0
		XIndexLados := X%IndexLados%

		Y%IndexLados% := (Seno * Radio) + Y0
		YIndexLados := Y%IndexLados%

		;------------------------------------------------
		;msgbox, antes de la Array

		Array.Push([XIndexLados , YIndexLados ])

		;msgbox % Array[1][1] "`t" Array[1][2] "`t" Array[1][3] "`t" Array[1][4] "`n" Array[2][1] "`t" Array[2][2] "`t" Array[2][3] "`t" Array[2][4] "`n" Array[3][1] "`t" Array[3][2] "`t" Array[3][3] "`t" Array[3][4] "`n" Array[4][1] "`t" Array[4][2] "`t" Array[4][3] "`t" Array[4][4]

		Grados := Grados + Incremento
		IndexLados := IndexLados - 1
	}

	;msgbox, X1 = %X1%`nY1 = %Y1%`n`nX2 = %X2%`nY2 = %Y2%`n`nX3 = %X3%`nY3 = %Y3%`n`nX4 = %X4%`nY4 = %Y4%`n`n

	ClonedSurface:=Surface1.clone().draw(Surface1)
	Surface1.DrawPolygon(Red_pen,Array)
	Viewport.Refresh()
	;msgbox, fin del loop
}

right::
Lados := Lados + 1
surface1.Clear(0xFFF808080)
Viewport.Refresh()
Goto MyLabel
return

left::
Lados := Lados - 1
surface1.Clear(0xFF808080)
Viewport.Refresh()
Goto MyLabel
return

up::
Radio := Radio + 10
surface1.Clear(0xFFF808080)
Viewport.Refresh()
Goto MyLabel
return

down::
Radio := Radio - 10
surface1.Clear(0xFF808080)
Viewport.Refresh()
Goto MyLabel
return

/*
Copyright 2012 Anthony Zhang <[email protected]>

This file is part of Canvas-AHK. Source code is available at <https://github.com/Uberi/Canvas-AHK>.

Canvas-AHK is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*
TODO
----

* console output module with colors by reading the bitmap, console functions at http://msdn.microsoft.com/en-us/library/windows/desktop/ms682073(v=vs.85).aspx
* consider color transformations using http://msdn.microsoft.com/en-us/library/windows/desktop/ms533875(v=vs.85).aspx
* effects support with http://msdn.microsoft.com/en-us/library/windows/desktop/ms533971(v=vs.85).aspx
    * GpStatus WINGDIPAPI GdipDrawImageFX(GpGraphics *graphics, GpImage *image, GpRectF *source, GpMatrix *xForm, CGpEffect *effect, GpImageAttributes *imageAttributes, GpUnit srcUnit)
    * GpStatus WINGDIPAPI GdipBitmapApplyEffect(GpBitmap* bitmap, CGpEffect *effect, RECT *roi, BOOL useAuxData, VOID **auxData, INT *auxDataSize)
    * Status __stdcall GdipCreateEffect(const GUID guid, CGpEffect **effect)
* opengl backend
* ASCII backend with drawing functions here: http://free.pages.at/easyfilter/bresenham.html
* combine the draw* and fill* functions: DrawPie(Pen) and FillPie(Brush) -> Pie(Pen) and Pie(Brush)
* fold pens into brushes; allow brushes to define widths, fills, etc.
* add hatch brush, texture brush, and linear/radial gradient brush capabilities to Brush class
* use CachedBitmap for animations or faster drawing: http://msdn.microsoft.com/en-us/library/ms533975(v=vs.85).aspx
*/

/*
#Warn All
#Warn LocalSameAsGlobal, Off

i := new Canvas.Surface
i.Load(A_ScriptDir . "\Earthrise.jpg")
s := new Canvas.Surface(400,400)
s.Draw(i)

f := new Canvas.Font("Georgia",18)
s.Text(new Canvas.Brush(0xFFFFFFFF),f,"Earthrise: Dawn of a new era",30,80)

Gui, +LastFound
v := new Canvas.Viewport(WinExist()).Attach(s)

p := new Canvas.Pen(0x80FF0000,10)
t := new Canvas.Pen(0xFF00FF00,1)
b := new Canvas.Brush(0xAA0000FF)

Gui, Show, w400 h400, Canvas Demo
Return

GuiClose:
ExitApp

Tab::
f.Measure("Earthrise: Dawn of a new era",W,H)
s.DrawRectangle(t,30,80,W,H)
v.Refresh()
Return

Space::
s.Clear(0xFFFFFF00)
 .Push()
 .Translate(50,50)
 .Rotate(60)
 .FillRectangle(b,0,0,50,50)
 .Pop()
 .DrawEllipse(p,70,70,100,100)
 .DrawCurve(t,[[10,10],[50,10],[10,50]],True)
 .FillPie(b,100,100,50,50,0,270)
v.Refresh()
Return
*/

class Canvas
{
    static _ := Canvas.Initialize()

    Initialize()
    {
        this.hModule := DllCall("LoadLibrary","Str","gdiplus","UPtr")

        ;initialize the GDI+ library
        VarSetCapacity(StartupInformation,A_PtrSize + 12)
        NumPut(1,StartupInformation,0,"UInt") ;GDI+ version (version 1)
        NumPut(0,StartupInformation,4) ;debug callback (disabled)
        NumPut(0,StartupInformation,A_PtrSize + 4) ;suppress background thread (disabled)
        NumPut(0,StartupInformation,A_PtrSize + 8) ;suppress external image codecs (disabled)
        Token := 0
        this.CheckStatus(DllCall("gdiplus\GdiplusStartup","UPtr*",Token,"UPtr",&StartupInformation,"UPtr",0)
            ,"GdiplusStartup","Could not initialize GDI+ library")
        this.Token := Token
    }

    __Delete()
    {
        ;shut down the GDI+ library
        DllCall("gdiplus\GdiplusShutdown","UPtr",this.Token)
    }

    Lenient()
    {
        this.Surface.CheckStatus := this.Surface.StubCheckStatus
        this.Surface.CheckPen := this.Surface.StubCheckPen
        this.Surface.CheckBrush := this.Surface.StubCheckBrush
        this.Surface.CheckFormat := this.Surface.StubCheckFormat
        this.Surface.CheckLine := this.Surface.StubCheckLine
        this.Surface.CheckRectangle := this.Surface.StubCheckRectangle
        this.Surface.CheckSector := this.Surface.StubCheckSector
        this.Surface.CheckPoint := this.Surface.StubCheckPoint
        this.Surface.CheckPoints := this.Surface.StubCheckPoints

        this.Viewport.CheckStatus := this.Viewport.StubCheckStatus

        this.Pen.CheckStatus := this.Pen.StubCheckStatus

        this.Brush.CheckStatus := this.Brush.StubCheckStatus

        this.Font.CheckStatus := this.Font.StubCheckStatus
    }

    class Viewport
    {
        __New(hWindow)
        {
            this.hWindow := hWindow

            ;create a memory device context for double buffering use
            this.hMemoryDC := DllCall("CreateCompatibleDC","UPtr",0,"UPtr")
            If !this.hMemoryDC
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not create memory device context (error in CreateCompatibleDC)")

            ;subclass window to override WM_PAINT
            this.pCallback := RegisterCallback(this.PaintCallback,"Fast",6)
            If !DllCall("Comctl32\SetWindowSubclass"
                ,"UPtr",hWindow ;window handle
                ,"UPtr",this.pCallback ;callback pointer
                ,"UPtr",hWindow ;subclass ID
                ,"UPtr",0) ;arbitrary data to pass to this particular subclass callback and ID
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not subclass window (error in SetWindowSubclass)")
        }

        __Delete()
        {
            ;remove subclass of window
            If !DllCall("Comctl32\RemoveWindowSubclass","UPtr",this.hWindow,"UPtr",this.pCallback,"UPtr",this.hWindow)
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not remove subclass from window (error in RemoveWindowSubclass)")

            ;free paint callback
            DllCall("GlobalFree","UPtr",this.pCallback,"UPtr")
        }

        Attach(Surface)
        {
            If !DllCall("Comctl32\SetWindowSubclass"
                ,"UPtr",this.hWindow ;window handle
                ,"UPtr",this.pCallback ;callback pointer
                ,"UPtr",this.hWindow ;subclass ID
                ,"UPtr",&this) ;arbitrary data to pass to this particular subclass callback and ID
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not update window subclass (error in SetWindowSubclass)")
            this.Surface := Surface
            this.Width := Surface.Width
            this.Height := Surface.Height
            Return, this
        }

        Detach()
        {
            this.Surface := False
            Return, this
        }

        Refresh(X = 0,Y = 0,W = 0,H = 0)
        {
            If (X < 0 || X > this.Width)
                throw Exception("INVALID_INPUT",A_ThisFunc,"Invalid X-axis coordinate: " . X)
            If (Y < 0 || Y > this.Height)
                throw Exception("INVALID_INPUT",A_ThisFunc,"Invalid Y-axis coordinate: " . Y)
            If (W < 0 || W > this.Width)
                throw Exception("INVALID_INPUT",A_ThisFunc,"Invalid width: " . W)
            If (H < 0 || H > this.Height)
                throw Exception("INVALID_INPUT",A_ThisFunc,"Invalid height: " . W)

            If W = 0
                W := this.Width
            If H = 0
                H := this.Height

            ;flush the GDI+ drawing batch
            this.CheckStatus(DllCall("gdiplus\GdipFlush","UPtr",this.Surface.pGraphics,"UInt",1) ;FlushIntention.FlushIntentionSync
                ,"GdipFlush","Could not flush GDI+ pending rendering operations")

            ;set up rectangle structure representing area to redraw
            VarSetCapacity(Rect,16)
            NumPut(X,Rect,0,"UInt")
            NumPut(Y,Rect,4,"UInt")
            NumPut(X + W,Rect,8,"UInt")
            NumPut(Y + H,Rect,12,"UInt")

            ;trigger redrawing of the window
            If !DllCall("InvalidateRect","UPtr",this.hWindow,"UPtr",&Rect,"UInt",0)
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not add rectangle to update region (error in InvalidateRect)")
            If !DllCall("UpdateWindow","UPtr",this.hWindow)
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not update window (error in UpdateWindow)")

            Return, this
        }

        PaintCallback(Message,wParam,lParam,hWindow,pInstance)
        {
            If Message != 0xF ;WM_PAINT
                Return, DllCall("Comctl32\DefSubclassProc","UPtr",hWindow,"UInt",Message,"UPtr",wParam,"UPtr",lParam,"UPtr") ;call the next handler in the window's subclass chain

            ;prepare window for painting
            VarSetCapacity(PaintStruct,A_PtrSize + 60)
            hWindowDC := DllCall("BeginPaint","UPtr",hWindow,"UPtr",&PaintStruct,"UPtr")
            If !hWindowDC
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not prepare window for painting (error in BeginPaint)")

            this := Object(pInstance) ;obtain the instance

            If this.Surface ;surface attached
            {
                ;obtain dimensions of update region
                X := NumGet(PaintStruct,A_PtrSize + 4,"UInt")
                Y := NumGet(PaintStruct,A_PtrSize + 8,"UInt")
                W := NumGet(PaintStruct,A_PtrSize + 12,"UInt") - X
                H := NumGet(PaintStruct,A_PtrSize + 16,"UInt") - Y

                ;create the GDI bitmap
                hBitmap := 0
                this.CheckStatus(DllCall("gdiplus\GdipCreateHBITMAPFromBitmap","UPtr",this.Surface.pBitmap,"UPtr*",hBitmap,"UInt",0x000000)
                    ,"GdipCreateHBITMAPFromBitmap","Could not convert GDI+ bitmap to GDI bitmap")

                ;select the bitmap into the memory device context
                hOriginalBitmap := DllCall("SelectObject","UPtr",this.hMemoryDC,"UPtr",hBitmap,"UPtr")
                If !hOriginalBitmap
                    throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not select bitmap into memory device context (error in SelectObject)")

                ;transfer bitmap from memory device context to window device context
                If !DllCall("BitBlt","UPtr",hWindowDC,"Int",X,"Int",Y,"Int",W,"Int",H,"UPtr",this.hMemoryDC,"Int",X,"Int",Y,"UInt",0xCC0020) ;SRCCOPY
                    throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not transfer bitmap data from memory device context to window device context (error in BitBlt)")

                ;deselect the bitmap from the memory device context
                If !DllCall("SelectObject","UPtr",this.hMemoryDC,"UPtr",hOriginalBitmap,"UPtr")
                    throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not deselect bitmap from memory device context (error in SelectObject)")

                ;delete the bitmap
                If !DllCall("DeleteObject","UPtr",hBitmap)
                    throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not delete bitmap (error in DeleteObject)")
            }

            ;finish painting window
            DllCall("EndPaint","UPtr",hWindow,"UPtr",&PaintStruct)
            Return, 0
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }
    }

    class Surface
    {
        __New(Width = 1,Height = 1)
        {
            ObjInsert(this,"",Object())

            If Width < 1
                throw Exception("INVALID_INPUT",-1,"Invalid width: " . Width)
            If Height < 1
                throw Exception("INVALID_INPUT",-1,"Invalid height: " . Height)

            pBitmap := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateBitmapFromScan0","Int",Width,"Int",Height,"Int",0,"Int",0x26200A,"UPtr",0,"UPtr*",pBitmap) ;PixelFormat32bppARGB
                ,"GdipCreateBitmapFromScan0","Could not create bitmap")
            this.pBitmap := pBitmap

            ;create a graphics object
            pGraphics := 0
            this.CheckStatus(DllCall("gdiplus\GdipGetImageGraphicsContext","UPtr",this.pBitmap,"UPtr*",pGraphics)
                ,"GdipGetImageGraphicsContext","Could not obtain graphics object")
            this.pGraphics := pGraphics

            this.States := []

            this.Width := Width
            this.Height := Height

            this.Interpolation := "None"
            this.Smooth := "None"
            this.Compositing := "Blend"
        }

        __Get(Key)
        {
            If (Key != "" && Key != "base")
                Return, this[""][Key]
        }

        __Set(Key,Value)
        {
            static InterpolationStyles := {None:   5 ;InterpolationMode.InterpolationModeNearestNeighbor
                                          ,Linear: 6 ;InterpolationMode.InterpolationModeHighQualityBilinear
                                          ,Cubic:  7} ;InterpolationMode.InterpolationModeHighQualityBicubic
            static SmoothStyles := {None: 3 ;SmoothingMode.SmoothingModeNone
                                   ,Good: 4 ;SmoothingMode.SmoothingModeAntiAlias8x4
                                   ,Best: 5} ;SmoothingMode.SmoothingModeAntiAlias8x8
            static CompositingStyles := {Blend:     0 ;CompositingMode.CompositingModeSourceOver
                                        ,Overwrite: 1} ;CompositingMode.CompositingModeSourceCopy
            If (Key = "Interpolation")
            {
                If !InterpolationStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid interpolation mode: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetInterpolationMode","UPtr",this.pGraphics,"UInt",InterpolationStyles[Value])
                    ,"GdipSetInterpolationMode","Could not set interpolation mode")
            }
            Else If (Key = "Smooth")
            {
                If !SmoothStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid smooth mode: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetSmoothingMode","UPtr",this.pGraphics,"UInt",SmoothStyles[Value])
                    ,"GdipSetSmoothingMode","Could not set smooth mode")
            }
            Else If (Key = "Compositing")
            {
                If !CompositingStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid compositing mode: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetCompositingMode","UPtr",this.pGraphics,"UInt",CompositingStyles[Value])
                    ,"GdipSetCompositingMode","Could not set compositing mode")
            }
            this[""][Key] := Value
            Return, Value
        }

        __Delete()
        {
            ;delete graphics object
            Result := DllCall("gdiplus\GdipDeleteGraphics","UPtr",this.pGraphics)
            If Result != 0 ;Status.Ok
            {
                DllCall("gdiplus\GdipDisposeImage","UPtr",this.pBitmap) ;delete bitmap object
                this.CheckStatus(Result,"GdipDeleteGraphics","Could not delete graphics object")
            }

            ;delete bitmap object
            this.CheckStatus(DllCall("gdiplus\GdipDisposeImage","UPtr",this.pBitmap)
                ,"GdipDisposeImage","Could not delete bitmap pointer")
        }

        Load(Path) ;wip: document this
        {
            Attributes := FileExist(Path)
            If !Attributes ;path does not exist
                throw Exception("INVALID_INPUT",-1,"Invalid path: " . Path)
            If InStr(Attributes,"D") ;path is not a file
                throw Exception("INVALID_INPUT",-1,"Invalid file: " . Path)

            ;create bitmap and obtain dimensions
            pBitmap := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateBitmapFromFile", "WStr",Path,"UPtr*",pBitmap) ;wip: should use higher level of exception as should CreateBitmap()
                ,"GdipCreateBitmapFromFile","Could not create bitmap from file")
            Width := 0
            this.CheckStatus(DllCall("gdiplus\GdipGetImageWidth","UPtr",pBitmap,"UInt*",Width)
                ,"GdipGetImageWidth","Could not obtain image width")
            Height := 0
            this.CheckStatus(DllCall("gdiplus\GdipGetImageHeight","UPtr",pBitmap,"UInt*",Height)
                ,"GdipGetImageHeight","Could not obtain image height")

            this.pBitmap := pBitmap
            this.Width := Width
            this.Height := Height
            Return, this
        }

        Save(Path)
        {
            ;wip: implement and document this
            Return, this
        }

        Clone(Width = "",Height = "")
        {
            Copy := new this.base(this.Width,this.Height)
            ;wip: clone the bitmap contents and settings and resize it
            Return, Copy
        }

        Clear(Color = 0x00000000)
        {
            If Color Is Not Integer
                throw Exception("INVALID_INPUT",-1,"Invalid color: " . Color)
            Return, this.CheckStatus(DllCall("gdiplus\GdipGraphicsClear","UPtr",this.pGraphics,"UInt",Color)
                ,"GdipGraphicsClear","Could not clear graphics")
        }

        Text(Brush,Font,Value,X,Y,W = "",H = "")
        {
            this.CheckBrush(Brush)
            this.CheckFont(Font)

            ;determine dimensions automatically if not specified
            If (W = "")
                W := 0
            If (H = "")
                H := 0

            ;create bounding rectangle
            this.CheckRectangle(X,Y,W,H)
            VarSetCapacity(Rectangle,16)
            NumPut(X,Rectangle,0,"Float")
            NumPut(Y,Rectangle,4,"Float")
            NumPut(W,Rectangle,8,"Float")
            NumPut(H,Rectangle,12,"Float")

            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawString"
                ,"UPtr",this.pGraphics ;graphics handle
                ,"WStr",Value ;string value
                ,"Int",-1 ;null terminated
                ,"UPtr",Font.hFont ;font handle
                ,"UPtr",&Rectangle ;bounding rectangle
                ,"UPtr",Font.hFormat ;string format
                ,"UPtr",Brush.pBrush) ;fill brush
                ,"GdipDrawString","Could not draw text")
        }

        GetPixel(X,Y,ByRef Color)
        {
            this.CheckPoint(X,Y)
            If (X < 0 || X > this.Width)
                throw Exception("INVALID_INPUT",-1,"Invalid X-axis coordinate: " . X)
            If (Y < 0 || Y > this.Height)
                throw Exception("INVALID_INPUT",-1,"Invalid Y-axis coordinate: " . Y)

            Return, this.CheckStatus(DllCall("gdiplus\GdipBitmapGetPixel","UPtr",this.pBitmap,"Int",X,"Int",Y,"UInt*",Color)
                ,"GdipBitmapGetPixel","Could not obtain pixel from bitmap")
        }

        SetPixel(X,Y,Color)
        {
            this.CheckPoint(X,Y)
            If (X < 0 || X > this.Width)
                throw Exception("INVALID_INPUT",-1,"Invalid X-axis coordinate: " . X)
            If (Y < 0 || Y > this.Height)
                throw Exception("INVALID_INPUT",-1,"Invalid Y-axis coordinate: " . Y)
            If Color Is Not Integer
                throw Exception("INVALID_INPUT",-1,"Invalid color: " . Color)

            Return, this.CheckStatus(DllCall("gdiplus\GdipBitmapSetPixel","UPtr",this.pBitmap,"Int",X,"Int",Y,"UInt",Color)
                ,"GdipBitmapSetPixel","Could not set bitmap pixel")
        }

        Draw(Surface,X = 0,Y = 0,W = "",H = "",SourceX = 0,SourceY = 0,SourceW = "",SourceH = "")
        {
            If !Surface.pBitmap
                throw Exception("INVALID_INPUT",-1,"Invalid surface: " . Surface)

            If (W = "")
                W := this.Width
            If (H = "")
                H := this.Height
            If (SourceW = "")
                SourceW := Surface.Width
            If (SourceH = "")
                SourceH := Surface.Height

            this.CheckRectangle(X,Y,W,H)
            this.CheckRectangle(SourceX,SourceY,SourceW,SourceH)

            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawImageRectRect","UPtr",this.pGraphics,"UPtr",Surface.pBitmap
                ,"Float",X,"Float",Y,"Float",W,"Float",H
                ,"Float",SourceX,"Float",SourceY,"Float",SourceW,"Float",SourceH
                ,"Int",2,"UPtr",0,"UPtr",0,"UPtr",0) ;Unit.UnitPixel
                ,"GdipDrawImageRectRect","Could not transfer image data to surface")
        }

        DrawArc(Pen,X,Y,W,H,Start,Sweep)
        {
            this.CheckPen(Pen)
            this.CheckSector(X,Y,W,H,Start,Sweep)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawArc","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X,"Float",Y,"Float",W,"Float",H,"Float",Start - 90,"Float",Sweep)
                ,"GdipDrawArc","Could not draw arc")
        }

        DrawCurve(Pen,Points,Closed = False,Tension = 1)
        {
            this.CheckPen(Pen)
            Length := this.CheckPoints(Points,PointArray)
            If Tension Not Between 0 And 1
                throw Exception("INVALID_INPUT",-1,"Invalid curve tension: " . Tension)

            If Closed
                Return, this.CheckStatus(DllCall("gdiplus\GdipDrawClosedCurve2","UPtr",this.pGraphics,"UPtr",Pen.pPen,"UPtr",&PointArray,"Int",Length,"Float",Tension)
                    ,"GdipDrawClosedCurve2","Could not draw curve")
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawCurve2","UPtr",this.pGraphics,"UPtr",Pen.pPen,"UPtr",&PointArray,"Int",Length,"Float",Tension)
                ,"GdipDrawCurve2","Could not draw curve")
        }

        DrawEllipse(Pen,X,Y,W,H)
        {
            this.CheckPen(Pen)
            this.CheckRectangle(X,Y,W,H)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawEllipse","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X,"Float",Y,"Float",W,"Float",H)
                ,"GdipDrawEllipse","Could not draw ellipse")
        }

        DrawPie(Pen,X,Y,W,H,Start,Sweep)
        {
            this.CheckPen(Pen)
            this.CheckSector(X,Y,W,H,Start,Sweep)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawPie","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X,"Float",Y,"Float",W,"Float",H,"Float",Start - 90,"Float",Sweep)
                ,"GdipDrawPie","Could not draw pie")
        }

        DrawPolygon(Pen,Points)
        {
            this.CheckPen(Pen)
            Length := this.CheckPoints(Points,PointArray)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawPolygon","UPtr",this.pGraphics,"UPtr",Pen.pPen,"UPtr",&PointArray,"Int",Length)
                ,"GdipDrawPolygon","Could not draw polygon")
        }

        DrawRectangle(Pen,X,Y,W,H)
        {
            this.CheckPen(Pen)
            this.CheckRectangle(X,Y,W,H)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawRectangle","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X,"Float",Y,"Float",W,"Float",H)
                ,"GdipDrawRectangle","Could not draw rectangle")
        }

        FillCurve(Brush,Points)
        {
            this.CheckBrush(Brush)
            Length := this.CheckPoints(Points,PointArray)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillClosedCurve","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"UPtr",&PointArray,"Int",Length)
                ,"GdipFillClosedCurve","Could not fill curve")
        }

        FillEllipse(Brush,X,Y,W,H)
        {
            this.CheckBrush(Brush)
            this.CheckRectangle(X,Y,W,H)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillEllipse","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"Float",X,"Float",Y,"Float",W,"Float",H)
                ,"GdipFillEllipse","Could not fill ellipse")
        }

        FillPie(Brush,X,Y,W,H,Start,Sweep)
        {
            this.CheckBrush(Brush)
            this.CheckSector(X,Y,W,H,Start,Sweep)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillPie","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"Float",X,"Float",Y,"Float",W,"Float",H,"Float",Start - 90,"Float",Sweep)
                ,"GdipFillPie","Could not fill pie")
        }

        FillPolygon(Brush,Points)
        {
            this.CheckBrush(Brush)
            Length := this.CheckPoints(Points,PointArray)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillPolygon","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"UPtr",&PointArray,"Int",Length)
                ,"GdipFillPolygon","Could not fill polygon")
        }

        FillRectangle(Brush,X,Y,W,H)
        {
            this.CheckBrush(Brush)
            this.CheckRectangle(X,Y,W,H)
            Return, this.CheckStatus(DllCall("gdiplus\GdipFillRectangle","UPtr",this.pGraphics,"UPtr",Brush.pBrush,"Float",X,"Float",Y,"Float",W,"Float",H)
                ,"GdipFillRectangle","Could not fill rectangle")
        }

        Line(Pen,X1,Y1,X2,Y2)
        {
            this.CheckPen(Pen)
            this.CheckLine(X1,Y1,X2,Y2)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawLine","UPtr",this.pGraphics,"UPtr",Pen.pPen,"Float",X1,"FLoat",Y1,"Float",X2,"Float",Y2)
                ,"GdipDrawLine","Could not draw line")
        }

        Lines(Pen,Points)
        {
            this.CheckPen(Pen)
            Length := this.CheckPoints(Points,PointArray)
            Return, this.CheckStatus(DllCall("gdiplus\GdipDrawLines","UPtr",this.pGraphics,"UPtr",Pen.pPen,"UPtr",&PointArray,"Int",Length)
                ,"GdipDrawLines","Could not draw lines")
        }

        Push()
        {
            State := 0
            this.CheckStatus(DllCall("gdiplus\GdipSaveGraphics","UPtr",this.pGraphics,"UInt*",State) ;GraphicsState
                ,"GdipSaveGraphics","Could not save graphics state")
            this.States.Insert(State)
            Return, this
        }

        Pop()
        {
            If !this.States.HasKey(1) ;stack is empty
                throw Exception("INVALID_INPUT",-1,"Invalid transformation stack entries")
            State := this.States.Remove()
            this.CheckStatus(DllCall("gdiplus\GdipRestoreGraphics","UPtr",this.pGraphics,"UInt",State) ;GraphicsState
                ,"GdipRestoreGraphics","Could not restore graphics state")
            Return, this
        }

        Translate(X,Y)
        {
            this.CheckPoint(X,Y)
            Return, this.CheckStatus(DllCall("gdiplus\GdipTranslateWorldTransform","UPtr",this.pGraphics,"Float",X,"Float",Y,"UInt",0) ;MatrixOrder.MatrixOrderPrepend
                ,"GdipTranslateWorldTransform","Could not apply translation matrix")
        }

        Rotate(Angle)
        {
            If Angle Is Not Number
                throw Exception("INVALID_INPUT",-1,"Invalid angle: " . Angle)
            Return, this.CheckStatus(DllCall("gdiplus\GdipRotateWorldTransform","UPtr",this.pGraphics,"Float",Angle,"UInt",0) ;MatrixOrder.MatrixOrderPrepend
                ,"GdipRotateWorldTransform","Could not apply rotation matrix")
        }

        Scale(X,Y)
        {
            this.CheckPoint(X,Y)
            Return, this.CheckStatus(DllCall("gdiplus\GdipScaleWorldTransform","UPtr",this.pGraphics,"Float",X,"Float",Y,"UInt",0) ;MatrixOrder.MatrixOrderPrepend
                ,"GdipScaleWorldTransform","Could not apply scale matrix")
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        StubCheckPen(Pen)
        {
        }

        StubCheckBrush(Brush)
        {
        }

        StubCheckFormat(Brush)
        {
        }

        StubCheckLine(X1,Y1,X2,Y2)
        {
        }

        StubCheckRectangle(X,Y,W,H)
        {
        }

        StubCheckSector(X,Y,W,H,Start,Sweep)
        {
        }

        StubCheckPoint(X,Y)
        {
        }

        StubCheckPoints(Points,ByRef PointArray)
        {
            Length := Points.MaxIndex()
            VarSetCapacity(PointArray,Length << 3)
            Offset := 0
            Loop, %Length%
            {
                Point := Points[A_Index]
                NumPut(Point[1],PointArray,Offset,"Float"), Offset += 4
                NumPut(Point[2],PointArray,Offset,"Float"), Offset += 4
            }
            Return, Length
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }

        CheckPen(Pen)
        {
            If !Pen.pPen
                throw Exception("INVALID_INPUT",-2,"Invalid pen: " . Pen)
        }

        CheckBrush(Brush)
        {
            If !Brush.pBrush
                throw Exception("INVALID_INPUT",-2,"Invalid brush: " . Brush)
        }

        CheckFont(Font)
        {
            If !(Font.hFontFamily && Font.hFont && Font.hFormat)
                throw Exception("INVALID_INPUT",-2,"Invalid font: " . Font)
        }

        CheckLine(X1,Y1,X2,Y2)
        {
            If X1 Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate 1: " . X1)
            If Y1 Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate 1: " . Y1)
            If X2 Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate 2: " . X2)
            If Y2 Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate 2: " . Y2)
        }

        CheckRectangle(X,Y,W,H)
        {
            If X Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate: " . X)
            If Y Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate: " . Y)
            If W < 0
                throw Exception("INVALID_INPUT",-2,"Invalid width: " . W)
            If H < 0
                throw Exception("INVALID_INPUT",-2,"Invalid height: " . H)
        }

        CheckSector(X,Y,W,H,Start,Sweep)
        {
            If X Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate: " . X)
            If Y Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate: " . Y)
            If W < 0
                throw Exception("INVALID_INPUT",-2,"Invalid width: " . W)
            If H < 0
                throw Exception("INVALID_INPUT",-2,"Invalid height: " . H)
            If Start Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid start angle: " . Start)
            If Sweep Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid sweep angle: " . Sweep)
        }

        CheckPoint(X,Y)
        {
            If X Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate: " . X)
            If Y Is Not Number
                throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate: " . Y)
        }

        CheckPoints(Points,ByRef PointArray)
        {
            Length := Points.MaxIndex()
            If !Length
                throw Exception("INVALID_INPUT",-2,"Invalid point set: " . Points)
            VarSetCapacity(PointArray,Length << 3)
            Offset := 0
            Loop, %Length%
            {
                Point := Points[A_Index]
                If !IsObject(Point)
                    throw Exception("INVALID_INPUT",-2,"Invalid point: " . Point)
                PointX := Point[1]
                PointY := Point[2]
                If PointX Is Not Number
                    throw Exception("INVALID_INPUT",-2,"Invalid X-axis coordinate: " . PointX)
                If PointY Is Not Number
                    throw Exception("INVALID_INPUT",-2,"Invalid Y-axis coordinate: " . PointX)

                NumPut(PointX,PointArray,Offset,"Float"), Offset += 4
                NumPut(PointY,PointArray,Offset,"Float"), Offset += 4
            }
            Return, Length
        }
    }

    class Pen
    {
        __New(Color = 0xFFFFFFFF,Width = 1)
        {
            If Color Is Not Integer
                throw Exception("INVALID_INPUT",-1,"Invalid color: " . Color)
            If Width Is Not Number
                throw Exception("INVALID_INPUT",-1,"Invalid width: " . Width)

            ObjInsert(this,"",Object())

            ;create the pen
            pPen := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreatePen1","UInt",Color,"Float",Width,"UInt",2,"UPtr*",pPen) ;Unit.UnitPixel
                ,"GdipCreatePen1","Could not create pen")
            this.pPen := pPen

            ;set properties
            this.Color := Color
            this.Width := Width
            this.Join := "Miter"
            this.Type := "Solid"
            this.StartCap := "Flat"
            this.EndCap := "Flat"
        }

        __Delete()
        {
            ;delete the pen
            this.CheckStatus(DllCall("gdiplus\GdipDeletePen","UPtr",this.pPen)
                ,"GdipDeletePen","Could not delete pen")
        }

        __Get(Key)
        {
            If (Key != "" && Key != "base")
                Return, this[""][Key]
        }

        __Set(Key,Value)
        {
            static JoinStyles := Object("Miter",0 ;LineJoin.LineJoinMiter
                                       ,"Bevel",1 ;LineJoin.LineJoinBevel
                                       ,"Round",2) ;LineJoin.LineJoinRound
            static TypeStyles := Object("Solid",0 ;DashStyleSolid
                                       ,"Dash",1 ;DashStyleDash
                                       ,"Dot",2 ;DashStyleDot
                                       ,"DashDot",3) ;DashStyleDashDot
            static CapStyles := Object("Flat",0 ;LineCap.LineCapFlat
                                      ,"Square",1 ;LineCap.LineCapSquare
                                      ,"Round",2 ;LineCap.LineCapRound
                                      ,"Triangle",3) ;LineCap.LineCapTriangle
            If (Key = "Color") ;set pen color
            {
                If Value Is Not Integer
                    throw Exception("INVALID_INPUT",-1,"Invalid color: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenColor","UPtr",this.pPen,"UInt",Value)
                    ,"GdipSetPenColor","Could not set pen color")
            }
            Else If (Key = "Width") ;set pen width
            {
                If Value Is Not Number
                    throw Exception("INVALID_INPUT",-1,"Invalid width: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenWidth","UPtr",this.pPen,"Float",Value)
                    ,"GdipSetPenWidth","Could not set pen width")
            }
            Else If (Key = "Join") ;set pen line join style
            {
                If !JoinStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid pen join: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenLineJoin","UPtr",this.pPen,"UInt",JoinStyles[Value])
                    ,"GdipSetPenLineJoin","Could not set pen join")
            }
            Else If (Key = "Type") ;set pen type
            {
                If !TypeStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid pen type: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenDashStyle","UPtr",this.pPen,"UInt",TypeStyles[Value])
                    ,"GdipSetPenDashStyle","Could not set pen type")
            }
            Else If (Key = "StartCap") ;set pen start cap
            {
                If !CapStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid pen start cap: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenStartCap","UPtr",this.pPen,"UInt",CapStyles[Value])
                    ,"GdipSetPenStartCap","Could not set pen start cap")
            }
            Else If (Key = "EndCap") ;set pen end cap
            {
                If !CapStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid pen end cap: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetPenEndCap","UPtr",this.pPen,"UInt",CapStyles[Value])
                    ,"GdipSetPenEndCap","Could not set pen end cap")
            }
            this[""][Key] := Value
            Return, Value
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }
    }

    class Brush
    {
        __New(Color = 0xFFFFFFFF)
        {
            If Color Is Not Integer
                throw Exception("INVALID_INPUT",-1,"Invalid color: " . Color)

            ObjInsert(this,"",Object())

            ;create the brush
            pBrush := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateSolidFill","UInt",Color,"UPtr*",pBrush)
                ,"GdipCreateSolidFill","Could not create brush")
            this.pBrush := pBrush

            this.Color := Color
        }

        __Delete()
        {
            ;delete the brush
            this.CheckStatus(DllCall("gdiplus\GdipDeleteBrush","UPtr",this.pBrush)
                ,"GdipDeleteBrush","Could not delete brush")
        }

        __Get(Key)
        {
            If (Key != "" && Key != "base")
                Return, this[""][Key]
        }

        __Set(Key,Value)
        {
            If (Key = "Color") ;set brush color
            {
                If Value Is Not Integer
                    throw Exception("INVALID_INPUT",-1,"Invalid color: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetSolidFillColor","UPtr",this.pBrush,"UInt",Value)
                    ,"GdipSetSolidFillColor","Could not set brush color")
            }
            this[""][Key] := Value
            Return, Value
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }
    }
    
    class Font
    {
        static _ := Canvas.Font.Initialize()

        Initialize()
        {
            ;create device context for graphics object
            this.hDC := DllCall("CreateCompatibleDC","UPtr",0,"UPtr")
            If !this.hDC
                throw Exception("INTERNAL_ERROR",A_ThisFunc,"Could not create device context (error in CreateCompatibleDC)")

            ;create a graphics object
            pGraphics := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateFromHDC","UPtr",this.hDC,"UPtr*",pGraphics)
                ,"GdipCreateFromHDC","Could not create graphics object")
            this.pGraphics := pGraphics
        }

        __New(Typeface,Size)
        {
            If Size Is Not Number
                throw Exception("INVALID_INPUT",-1,"Invalid size: " . Size)
            If Size <= 0
                throw Exception("INVALID_INPUT",-1,"Invalid size: " . Size)

            ObjInsert(this,"",Object())

            ;create string format
            hFormat := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateStringFormat","Int",0x800,"Int",0,"Ptr*",hFormat) ;StringFormat.StringFormatFlagsMeasureTrailingSpaces, LANG_NEUTRAL
                ,"GdipCreateStringFormat","Could not create string format")

            this.hFormat := hFormat

            this[""].Typeface := Typeface
            this[""].Size := Size

            this[""].Bold := False
            this[""].Italic := False
            this[""].Underline := False
            this[""].Strikeout := False

            this.UpdateFontFamily()
            this.UpdateFont()
        }

        UpdateFontFamily()
        {
            ;delete previous font family if present
            If this.hFontFamily
                this.CheckStatus(DllCall("gdiplus\GdipDeleteFontFamily","UPtr",this.hFontFamily)
                    ,"GdipDeleteFontFamily","Could not delete font family")

            ;create font family
            hFontFamily := 0
            this.CheckStatus(DllCall("gdiplus\GdipCreateFontFamilyFromName","WStr",this.Typeface,"UPtr",0,"UPtr*",hFontFamily)
                ,"GdipCreateFontFamilyFromName","Could not create font family")
            this.hFontFamily := hFontFamily
        }

        UpdateFont()
        {
            ;delete previous font if present
            If this.hFont
                this.CheckStatus(DllCall("gdiplus\GdipDeleteFont","UPtr",this.hFont)
                    ,"GdipDeleteFont","Could not delete font")

            ;determine font style
            Style := 0 ;FontStyle.FontStyleRegular
            If this.Bold
                Style |= 1 ;FontStyle.FontStyleBold
            If this.Italic
                Style |= 2 ;FontStyle.FontStyleItalic
            If this.Underline
                Style |= 4 ;FontStyle.FontStyleUnderline
            If this.Strikeout
                Style |= 8 ;FontStyle.FontStyleStrikeout

            ;create font
            hFont := 0, this.CheckStatus(DllCall("gdiplus\GdipCreateFont","UPtr",this.hFontFamily,"Float",this.Size,"Int",Style,"UInt",2,"UPtr*",hFont) ;Unit.UnitPixel
                    ,"GdipCreateFont","Could not create font")
            this.hFont := hFont
        }

        __Delete()
        {
            ;delete font
            Result := DllCall("gdiplus\GdipDeleteFont","UPtr",this.hFont)
            If Result != 0
            {
                DllCall("gdiplus\GdipDeleteFontFamily","UPtr",this.hFontFamily) ;delete font family
                DllCall("gdiplus\GdipDeleteStringFormat","UPtr",this.hFormat) ;delete string format
                this.CheckStatus(Result,"GdipDeleteFont","Could not delete font")
                Return
            }

            ;delete font family
            Result := DllCall("gdiplus\GdipDeleteFontFamily","UPtr",this.hFontFamily)
            If Result != 0 ;Status.Ok
            {
                DllCall("gdiplus\GdipDeleteStringFormat","UPtr",this.hFormat) ;delete string format
                this.CheckStatus(Result,"GdipDeleteFontFamily","Could not delete font family")
                Return
            }

            ;delete string format
            this.CheckStatus(DllCall("gdiplus\GdipDeleteStringFormat","UPtr",this.hFormat)
                ,"GdipDeleteStringFormat","Could not delete string format")
        }

        __Get(Key)
        {
            If (Key != "" && Key != "base")
                Return, this[""][Key]
        }

        __Set(Key,Value)
        {
            static AlignStyles := Object("Left",  0  ;StringAlignment.StringAlignmentNear
                                        ,"Center",1  ;StringAlignment.StringAlignmentCenter
                                        ,"Right", 2) ;StringAlignment.StringAlignmentFar
            If (Key = "Size")
            {
                If Value Is Not Number
                    throw Exception("INVALID_INPUT",-1,"Invalid size: " . Value)
                If Value <= 0
                    throw Exception("INVALID_INPUT",-1,"Invalid size: " . Value)
                this[""].Size := Value
                this.UpdateFont()
            }
            Else If (Key = "Typeface")
            {
                this[""].Typeface := Value
                this.UpdateFontFamily()
                this.UpdateFont()
            }
            Else If (Key = "Bold" || Key = "Italic" || Key = "Underline" || Key = "Strikeout")
            {
                this[""][Key] := Value
                this.UpdateFont()
            }
            Else If (Key = "Align")
            {
                If !AlignStyles.HasKey(Value)
                    throw Exception("INVALID_INPUT",-1,"Invalid alignment: " . Value)
                this.CheckStatus(DllCall("gdiplus\GdipSetStringFormatAlign","UPtr",this.hFormat,"UInt",AlignStyles[Value])
                    ,"GdipSetStringFormatAlign","Could not set string format alignment")
                this[""].Align := Value
            }
            Else
                this[""][Key] := Value
            Return, Value
        }

        Measure(Value,ByRef Width,ByRef Height)
        {
            VarSetCapacity(Rectangle,16,0)
            VarSetCapacity(Bounds,16)
            this.CheckStatus(DllCall("gdiplus\GdipMeasureString"
                ,"UPtr",this.base.pGraphics
                ,"WStr",Value ;string value
                ,"Int",-1 ;null terminated
                ,"UPtr",this.hFont ;font handle
                ,"UPtr",&Rectangle ;input bounds
                ,"UPtr",this.hFormat ;string format
                ,"UPtr",&Bounds ;output bounds
                ,"UPtr",0 ;output number of characters that can fit in input bounds
                ,"UPtr",0) ;output number of lines that can fit in input bounds
                ,"GdipMeasureString","Could not measure text bounds")
            Width := NumGet(Bounds,8,"Float")
            Height := NumGet(Bounds,12,"Float")
            Return, this
        }

        StubCheckStatus(Result,Name,Message)
        {
            Return, this
        }

        CheckStatus(Result,Name,Message)
        {
            static StatusValues := ["Status.GenericError"
                                   ,"Status.InvalidParameter"
                                   ,"Status.OutOfMemory"
                                   ,"Status.ObjectBusy"
                                   ,"Status.InsufficientBuffer"
                                   ,"Status.NotImplemented"
                                   ,"Status.Win32Error"
                                   ,"Status.WrongState"
                                   ,"Status.Aborted"
                                   ,"Status.FileNotFound"
                                   ,"Status.ValueOverflow"
                                   ,"Status.AccessDenied"
                                   ,"Status.UnknownImageFormat"
                                   ,"Status.FontFamilyNotFound"
                                   ,"Status.FontStyleNotFound"
                                   ,"Status.NotTrueTypeFont"
                                   ,"Status.UnsupportedGdiplusVersion"
                                   ,"Status.GdiplusNotInitialized"
                                   ,"Status.PropertyNotFound"
                                   ,"Status.PropertyNotSupported"
                                   ,"Status.ProfileNotFound"]
            If Result != 0 ;Status.Ok
                throw Exception("INTERNAL_ERROR",-1,Message . " (GDI+ error " . StatusValues[Result] . " in " . Name . ")")
            Return, this
        }
    }
}

[Moderator edit: Added [code][/code] tags. DO NOT POST CODE WITHOUT THEM, ESPECIALLY SUCH LARGE SCRIPTS!]

Return to “Pedir Ayuda”

Who is online

Users browsing this forum: No registered users and 35 guests