Search code examples
powershellsystem.drawing

Calling GetPixel from powershell with param error despite correct params


The code snippet below is the debugger output.

Exception calling "GetPixel" with "2" argument(s): "Parameter must be positive and < Height.
Parameter name: y"
At C:\dropbox\Workspace\PowerShellTest\mapGenerator\mapGenerator.ps1:51 char:16
+             if(($map.GetPixel($x, ($y+1)) -eq "ff000000") -or ($map.GetPixel(($x ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentOutOfRangeException

Hit Line breakpoint on 'C:\dropbox\Workspace\PowerShellTest\mapGenerator\mapGenerator.ps1:51'


[DBG]: PS C:\dropbox\Workspace\PowerShellTest\mapGenerator>> $y
5

[DBG]: PS C:\dropbox\Workspace\PowerShellTest\mapGenerator>> $map.Height
7

I do not get this error message at all.

"Parameter must be positive and < Height."

Ok. But parameter is (5+1), so positive, and (5+1), so <7

MCVE Code

EDIT 1: MCVE code removed - I found an error in the MCVE which, when removed, also got rid of the error message. Now to go back and see if I have the same error in the original code.

EDIT 2: This is infuriating. I inserted a write-output for the $x and $y values, just to make sure I didn't have any with bad values (E.g. checking below a pixel with coordinate 0, or above a pixel with coordinate map.height Inserting the write-output made the rest of the code run correctly. And removing the write-output makes the code stop working again. Does write-output coerce something behind the scenes?

EDIT 3: It is very difficult to make an MCVE that retains this weird behavior, so I'll give you the script it happens in.

function markCoastline ($landArray, $map)
{
       foreach ($square in $landArray)
    {

        $x = $square[0]
        $y = $square[1]

        $top = ($y -eq $map.Height-1)
        $bot = ($y -eq 0)
        $lef = ($x -eq 0)
        $rig = ($x -eq $map.Width-1)



        Write-Output $x
        Write-Output $y


        if(     $bot -and $left)
        {
            if(($map.GetPixel($x, ($y+1)).Name -eq "ff000000")-or ($map.GetPixel(($x+1), $y).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }
        }
        elseif( $top -and $left)
        {
            if(($map.GetPixel(($x+1), $y).Name -eq "ff000000") -or ($map.GetPixel($x, ($y-1)).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }
        }
        elseif( $bot -and $rig)
        {
            if(($map.GetPixel(($x-1), $y).Name -eq "ff000000") -or ($map.GetPixel($x, ($y+1)).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }
        }
        elseif( $top -and $rig)
        {
            if(($map.GetPixel(($x-1), $y).Name -eq "ff000000") -or ($map.GetPixel($x, ($y-1)).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }
        }
        elseif(  $lef)
        {
            if(($map.GetPixel($x, ($y+1)).Name -eq "ff000000") -or ($map.GetPixel(($x+1), $y).Name -eq "ff000000") -or ($map.GetPixel($x, ($y-1)).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }
        }
        elseif(  $bot)
        {
            if(($map.GetPixel($x, ($y+1)).Name -eq "ff000000") -or ($map.GetPixel(($x+1), $y).Name -eq "ff000000") -or ($map.GetPixel(($x-1), $y).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }            
        }
        elseif(  $rig)
        {
            if(($map.GetPixel($x, ($y+1)).Name -eq "ff000000")-or ($map.GetPixel(($x-1), $y).Name -eq "ff000000") -or ($map.GetPixel($x, ($y-1)).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }
        }
        elseif(  $top)
        {
            if(($map.GetPixel(($x+1), $y).Name -eq "ff000000") -or ($map.GetPixel(($x-1), $y).Name -eq "ff000000") -or ($map.GetPixel($x, ($y-1)).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }
        }
        else
        {
            if(($map.GetPixel($x, ($y+1)).Name -eq "ff000000") -or ($map.GetPixel(($x+1), $y).Name -eq "ff000000") -or ($map.GetPixel(($x-1), $y).Name -eq "ff000000") -or ($map.GetPixel($x, ($y-1)).Name -eq "ff000000"))
            {
                $map.SetPixel($x, $y, "lime")
            }
        }
    }
}

#First, get a BMP image
$openFile = New-Object -TypeName System.Windows.Forms.OpenFileDialog
    $openFile.AddExtension = $true
    $openFile.Filter = 'Bitmap Picture (*.bmp)|*.bmp|All Files|*.*'
    $openFile.Multiselect = $false
    $openFile.FilterIndex = 0
    $openFile.InitialDirectory = "$HOME\Documents"
    $openFile.RestoreDirectory = $true
    $openFile.ShowReadOnly = $true
    $openFile.ReadOnlyChecked = $false
    $openFile.Title = 'Select a seed picture (BMP only)'
$openFile.showDialog()
$map = New-Object System.Drawing.Bitmap($openFile.Filename)

$landPixels = @()
$oceanPixels = @()


#Second: Find land/ocean
for ($x = 0; $x -lt $map.Width; $x++)
{
    for ($y = 0; $y -lt $map.Height; $y++)
    {
        $current = $map.GetPixel($x, $y)

        if($current.Name -eq "ff000000")
        {
            $ocean = @($x, $y)
            $oceanPixels += , $ocean
        }
        else
        {
            $land = @($x, $y)
            $landPixels += , $land
        }
    }
}


#Third, paint coastline green:

markCoastline $landPixels $map

$map.save("b8fcf4351caf4b00af8eb0fa2e5bc17a.bmp")

There's a write-output on lines 16 and 17. Remove those and the script gives the original problem mentioned above - claims $y doesn't fit as a parameter for the GetPixel function. Keep it in and the code runs exactly as intended.


Solution

  • In two places in your code you use $left instead of $lef. And I still got that error message regardless of Write-Output. Error message just got buried under all that output, but it still here.

    I recommend to use Set-StrictMode -Version Latest and $ErrorActionPreference='Inquire' when you are debugging your code. It is easier to spot error with them.

    P.S.
    Does not use array addition it reallocate array or every addition, use lists instead:

    $landPixels = New-Object System.Collections.Generic.List[object]
    $oceanPixels = New-Object System.Collections.Generic.List[object]
    
    for ($x = 0; $x -lt $map.Width; $x++)
    {
        for ($y = 0; $y -lt $map.Height; $y++)
        {
            $current = $map.GetPixel($x, $y)
    
            if($current.Name -eq "ff000000")
            {
                $oceanPixels.Add(($x,$y))
            }
            else
            {
                $landPixels.Add(($x,$y))
            }
        }
    }
    

    P.P.S.

    $x,$y = $square
    

    P.P.P.S

    if(
        (($y+1 -lt $map.Height) -and ($map.GetPixel($x, ($y+1)).Name -eq "ff000000")) -or
        (($y-1 -gt 0)           -and ($map.GetPixel($x, ($y-1)).Name -eq "ff000000")) -or
        (($x+1 -lt $map.Width)  -and ($map.GetPixel(($x+1), $y).Name -eq "ff000000")) -or
        (($x-1 -gt 0)           -and ($map.GetPixel(($x-1), $y).Name -eq "ff000000"))
    )
    {
        $map.SetPixel($x, $y, "lime")
    }