bookmark_borderPowershell Color Conversions

Alright. I was in a bit of a groove last night. I was up till 3, but I do have something amazing to show for it. Like literally fantastic. I have no idea how humanity got to this point without some of this code. So without further ado how about some scripts for doing color conversions in powershell. They aren’t commented amazingly. Well the first one is ok. But the other ones suck. Anyway, here it is. These are based entirely off of the wikipedia article on HSV and HSL. I did my best to use those formulas exaclty so everything *SHOULD* work.



#
# All of these functions are based on the formulas provided on WikiPedia for color conversions
# So check them out for more info https://en.wikipedia.org/wiki/HSL_and_HSV
#

#
# Converts from Hue Saturation Lightness to RGB
#
function Convert-HSLToRGB
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 360)][double]$Hue,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 1)][double]$Saturation,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 1)][double]$Lightness
    )
    process
    {
        # You will need to check wikipedia for this. Essentially
        # We take a cube, and project it on to a 2d surface.
        # Which gives us a hexagon. Seriously, look at wikipedia. 
        # It weird to think about, but imaging a rubix cube, sitting on one of its corners.
        # If you look directly down at it there will be one corner in the middle, and 6 along the side
        # The chroma is the distance from the center of the hexagon
        # The Hue is our angle along this hexagon.
        # WIKIPEDIA LOOK AT IT.

        # Calculate the chroma
        $chroma = (1 - [Math]::Abs(2.0*$Lightness - 1.0)) * $Saturation
        
        # Figure out which section of the hexagon we are in. (6 sections)
        $H = $Hue / 60.0
        $X = $chroma * (1.0 - [Math]::Abs($H % 2 - 1))

        # Get value for lightness
        $m = $Lightness - 0.5 * $chroma
        $rgb = @($m, $m, $m)

        # UGH this part.
        # ok. So this is what happens. [R, G, B] is definited peicewise.
        # 0 <= H < 1 then [C, X, 0]
        # 1 <= H < 2 then [X, C, 0]
        # 2 <= H < 3 then [0, C, X]
        # 3 <= H < 4 then [0, X, C]
        # 4 <= H < 5 then [X, 0, C]
        # 5 <= H < 6 then [C, 0, X]
        # So these calculate the index of the x value and the c value
        $xIndex = (7 - [Math]::Floor($H)) % 3
        $cIndex = [int]($H / 2) % 3

        # Add the values in
        $rgb[$xIndex] += $X
        $rgb[$cIndex] += $chroma

        # Return the value
        return [pscustomobject]@{red = [int]($rgb[0] * 255); green = [int]($rgb[1] * 255); blue = [int]($rgb[2] * 255)}
    }
}

#
# Converts from Hue Saturation Value to RGB
#
function Convert-HSVToRGB
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 360)][double]$Hue,
        [Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 1)][double]$Saturation,
        [Parameter(Mandatory = $true, Position = 2, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 1)][double]$Value
    )
    process
    {
        # This one works very similar to the funciton above, so take a look there for comments
        $chroma = $Value * $Saturation
        $H = $Hue / 60.0
        $X = $chroma * (1.0 - [Math]::Abs($H % 2 - 1))

        $m = $Value - $chroma
        $rgb = @($m, $m, $m)

        $xIndex = (7 - [Math]::Floor($H)) % 3
        $cIndex = [int]($H / 2) % 3

        $rgb[$xIndex] += $X
        $rgb[$cIndex] += $chroma

        return [pscustomobject]@{red = [int]($rgb[0] * 255); green = [int]($rgb[1] * 255); blue = [int]($rgb[2] * 255)}
    }
}

#
# Converts from RGB to Hue Saturation Value
#
function Convert-RGBToHSV
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Red,
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Green,
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Blue
    )
    process
    {
        $arry = @(($red / 255), ($green / 255), ($blue / 255))
        $max = 0
        $min = 0
        for($i = 1; $i -lt $arry.Length; $i++)
        {
            if($arry[$i] -gt $arry[$max])
            {
                $max = $i
            }

            if($arry[$i] -lt $arry[$min])
            {
                $min = $i
            }
        }
        $C = $arry[$max] - $arry[$min]
        $hue = 0
        $saturation = 0
        if(0 -ne $C)
        {
            $hue = ($arry[($max + 4) % 3] - $arry[($max+5) % 3]) / $C

            if(0 -eq $max)
            {
                $hue %= 6
            }
            else
            {
                $hue += 2*$max
            }

            $hue *= 60
            $value = $arry[$max]
            $saturation = $C / $value
        }
        return [pscustomobject]@{hue = $hue; saturation = $saturation; value = $value}
    }
}

#
# Converts from RGB to Hue Saturation Lightness
#
function Convert-RGBToHSL
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Red,
        [Parameter(Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Green,
        [Parameter(Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Blue
    )
    process
    {
        $arry = @(($red / 255), ($green / 255), ($blue / 255))
        $max = 0
        $min = 0
        for($i = 1; $i -lt $arry.Length; $i++)
        {
            if($arry[$i] -gt $arry[$max])
            {
                $max = $i
            }

            if($arry[$i] -lt $arry[$min])
            {
                $min = $i
            }
        }
        $C = $arry[$max] - $arry[$min]
        $hue = 0
        $lightness = 0
        if(0 -ne $C)
        {
            $hue = ($arry[($max + 4) % 3] - $arry[($max+5) % 3]) / $C

            if(0 -eq $max)
            {
                $hue %= 6
            }
            else
            {
                $hue += 2*$max
            }

            $hue *= 60
            $lightness = 0.5 * ($arry[$min] + $arry[$max])
            $saturation = $C / (1 - [Math]::Abs(2.0 * $lightness - 1))
        }
        return [pscustomobject]@{hue = $hue; saturation = $saturation; lightness = $lightness}
    }
}

#
# Splits an integer RGB value into 3 parts
#
function Split-RGB
{
    param
    (
        [Parameter(Mandatory=$true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)][ValidateRange(0, 0xFFFFFF)][int]$RGB
    )
    process
    {
        return [pscustomobject]@{red = ($RGB -shr 16); green = (($RGB -shr 8) -band 0xFF); blue = ($RGB -band 0xFF)}
    }
}

#
# Combines the rgb components into a single value
#
function Join-RGB
{
    param
    (
        [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Red,
        [Parameter(Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Green,
        [Parameter(Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Blue
    )
    process
    {
        return ($Red -shl 16) -bor ($Green -shl 8) -bor $Blue
    }
}

#
# Just gets an rgb color object
#
function Get-RGBColor
{
    param
    (
        [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Red,
        [Parameter(Mandatory=$true, Position=1, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Green,
        [Parameter(Mandatory=$true, Position=2, ValueFromPipelineByPropertyName=$true)][ValidateRange(0, 0xFF)][int]$Blue
    )
    process
    {
        return [pscustomobject]@{red = $Red; green = $Green; blue = $Blue}
    }
}

# Test
# Join-RGB -Red 45 -Green 32 -Blue 222 | Split-RGB | Join-RGB | Split-RGB
# Get-RGBColor 10 20 30 | Convert-RGBToHSL | Convert-HSLToRGB
# Get-RGBColor 10 20 30 | Convert-RGBToHSV | Convert-HSVToRGB
# Get-RGBColor 10 20 30 | Convert-RGBToHSV | Convert-HSVToRGB | Convert-RGBToHSL | Convert-HSLToRGB


So yea. There is it. I hope someone can make use of these things. I don’t really have I use for them, just a need to fight something throught until it is done. So my issue is that I was working on a side project, and we were using an external web service, which always returned colors in HSL. To be honest, I had no idea what that was, so I did some reading and had to write a function to convert to RGB. RGB makes SO much more sense… to me.

bookmark_borderC# solution to g()(“al”)

Here is a fun coding problem. The idea is simple. g()(“al”) outputs “goal”. g()()(“al”) outputs “gooal”. And so on. So here is what it comes down to. A call to g(), needs to return either a function, or a string. Sounds simple enough. In C# you can get it done with heavy use of the ‘dynamic’ type, and a delegate.

namespace Goal
{
    using System;
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(G()("al"));
            Console.WriteLine(G()()("al"));
            Console.WriteLine(G()()()("al"));
            Console.WriteLine(G()()()()("al"));
            Console.WriteLine(G()()()()()("al"));
            Console.ReadLine();
        }
        delegate dynamic g(string a = "o");
        static dynamic G(string o = "o", string curr = "g")
        {
            return o == "al" ? (dynamic)curr + o : new g((a) => G(a, curr + o));
        }
    }
}

There we have it! The output will be something like:

  • goal
  • gooal
  • goooal
  • gooooal
  • goooooal

bookmark_borderPowershell Text Flip

Here’s a fun piece of code. All it does is take a string, and turn it over.

function Flip-Text
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)][String]$text
    )
    process
    {
        $rightSide='abcdefghijklmnopqrstuvwxyz0123456789&amp;_<>?!()[]{}'
        $upsidedown='ɐqɔpǝɟƃɥıɾʞʃɯuodbɹsʇnʌʍxʎz012Ɛᔭ59Ɫ86⅋‾><¿¡)(][}{'
        $output = [System.Text.StringBuilder]::New($text.Length);
        for($i = $text.Length -1; $i -ge 0; $i--)
        {
            # Get the character
            $c = $text[$i]

            # Find the index of the character in the right side up array.
            $indR = $rightSide.IndexOf([char]::ToLower($c))

            if($indR -gt 0)
            {
                $replacement = $upsidedown[$indR];
                if ([char]::IsUpper($c))
                {
                    # Try to make it upper case
                    $replacement = [char]::ToUpper($replacement);
                }

                # Stringbuilder returns a value when you append. 
                # Assigning to null to prevent.
                $null = $output.Append($replacement)
            }
            else{
                $null = $output.Append($c)
            }
        }

        return $output.ToString()
    }
}

So there was the program. All it really does, it maps right side up characters to their inverse. In the spirit of the rest of this site, here is an ugly one liner which does the same thing. Pretty much it’s just the first answer all mushed together, but it sure looks cool.

function Flip-Text2
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)][String]$text
    )
    process
    {
        ($text[($text.Length-1)..0]|%{'ɐqɔpǝɟƃɥıɾʞʃɯuodbɹsʇnʌʍxʎz012Ɛᔭ59Ɫ86⅋‾><¿¡)(][}{ '[("$(97..122|%{[char]$_})$(0..9)&amp;_<>?!()[]{}"-replace' ','').IndexOf($_)]})-join''
    }
}

This one doesn’t support any upper case characters.

bookmark_borderUnload a Dll in Powershell

The easiest way to unload a .dll in PowerShell is to load (Import-Module) it up within a Job(Start-Job)/New Instance/or new session. You could also do it by creating an Appdomain, but a Job is the most PowerShell-y. Once the job completes, the .dll will get unloaded.

The issue under the hood, is actually a .Net limitation. Assemblies are attached to an Appdomain, and only get unloaded once the referencing Appdomain has also been unloaded. This is why, ‘Remove-Module’ doesn’t unload the .dll, just removes all members which are implemented by the assembly from the current session.

So this program isn’t beautiful, but its a basic demonstration of a new Job, using Start-Job, which creates a new .dll and is able to delete it afterwards. What it does, is find the C# compiler on your machine (It will fail if its not there…). Compile a simple library. Load it. Call a function. Exit to job. Then, delete the .dll that it compiled. We can’t delete a .dll while we have it loaded, so this is proof that the thing gets unloaded.

# Create a job
$job = Start-Job -Name CsharpJob -ArgumentList $pwd -ScriptBlock {
    
    # Find the C sharp compiler
    $csc = "`"$((dir C:\ -File csc.exe -Recurse | Select -First 1).FullName)`" /target:library templib.cs"

    # Get the current working directory from the arguments
    cd $args[0]

    # Write a c# program to a temp file
@"
    namespace HelloWorldNamespace
    {
        public class HelloWorldLibrary
        {
            public static string GetHelloWorld()
            {
                return "Hello World";
            }
        }
    }
"@ > "templib.cs"

    # Build the program with csc
    iex "&amp;$csc" | Out-Null

    # Import the dll we just built
    Import-Module "$pwd\templib.dll" | Out-Null

    # Call one of the methods in the libarary
    [HelloWorldNamespace.HelloWorldLibrary]::GetHelloWorld()

}

# Wait on the job to finish
Wait-Job -Job $job | Out-Null

# Write the output
Receive-Job -Job $job

# Clean up
rm templib.cs

# In a loop to to make sure this gets done
while(Test-Path templib.dll)
{
    rm templib.dll -ErrorAction Ignore | Out-Null
}

The reason for the loop at the end of the program, is that it takes a little bit of time for the OS to actually let you delete dll after it was unloaded. So this was kind of a hacky looking example, but it gets the job done!

#Other Examples which will work

## Start a new instance of powershell 

powershell
# Load your dll
exit

## Start a new PSSession (May run into authorization issues)

Enter-PSSession -ComputerName ([System.Environment]::MachineName)
# Load your dll
Exit-PSSession