複数の画像ファイルを縦または横方向に連結する PowerShell モジュール

ImageMagick とかをインストールするのが面倒だったので書きました。

# 開発／動作環境

* Windows 10
* PowerShell 5.1.14393.576

# 導入の仕方

`$env:USERPROFILE\Documents\WindowsPowerShell\Modules` に `CombineImage` フォルダを作り、その中にモジュールファイル（`CombineImage.psm1`）を入れます。

# 使い方

## 構文

```
Save-CombinedImage [-Src] <String[]> [[-Dst] <String>] [-Horizontal]
```

## 使用例をいくつか

```PowerShell
# 2つの画像を縦に連結して `3.png` に保存
Save-CombinedImage -Src 1.png,2.png -Dst 3.png
```

```PowerShell
# -Src は省略可
Save-CombinedImage 1.png,2.png -Dst 3.png
```

```PowerShell
# -Dst を省略すると out.png に保存
Save-CombinedImage 1.png,2.png
```

```PowerShell
# -Horizontal で横方向に連結
Save-CombinedImage a.png,b.png,c.png -Horizontal
```

```PowerShell
# 連結するファイルをパイプで渡せる
Get-ChildItem image*.png | Save-CombinedImage -Dst combined.png
```

```PowerShell
# 画像形式を指定できる（拡張子から判断）
Get-ChildItem image*.png | Save-CombinedImage -Dst new.bmp  # BMPで保存
```

# ソースコード

~~Gist に置いています。
[:page_facing_up: CombineImage.psm1](https://gist.github.com/voidproc/c9d8f7cb64022d4d18ca2951c071127f)~~

```PowerShell:CombineImage.psm1
<#
.SYNOPSIS
画像ファイルを連結します。

.DESCRIPTION
複数の画像ファイルを縦または横方向に連結し、保存します。
保存される画像形式は、保存ファイル名の拡張子から判断されます。

.PARAMETER Src
連結するファイル名をコンマ区切りまたはパイプラインから指定します。

.PARAMETER Dst
出力するファイル名を指定します。このパラメータを省略した際の出力先は "out.png" になります。

.PARAMETER Horizontal
画像を横方向に連結します。

.EXAMPLE
    > Save-CombinedImage -Src image1.png,image2.png -Dst new.png

.EXAMPLE
    > Get-ChildItem -Path image*.png | Save-CombinedImage -Dst new.png -Horizontal
#>

function Save-CombinedImage {
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string[]]
        $Src,
        
        [string]
        $Dst = "out.png",

        [switch]
        $Horizontal
    )

    begin
    {
        $src_ = New-Object System.Collections.Generic.List[string]
    }

    process
    {
        $Src | ForEach-Object { $src_.Add($_) }
    }

    end
    {
        Add-Type -AssemblyName System.Drawing

        # カレントディレクトリを $PWD に設定
        $pcd = [System.IO.Directory]::GetCurrentDirectory()
        [System.IO.Directory]::SetCurrentDirectory($PWD)

        # 指定された画像から Bitmap を作成
        $bmp = $src_ | ForEach-Object { New-Object System.Drawing.Bitmap($_) }

        # 連結後のサイズ
        $measureW = $bmp | ForEach-Object { $_.Width } | Measure-Object -Sum -Maximum
        $measureH = $bmp | ForEach-Object { $_.Height } | Measure-Object -Sum -Maximum
        [int] $w = $measureW.Maximum
        [int] $h = $measureH.Sum
        if ($Horizontal)
        {
            $w = $measureW.Sum
            $h = $measureH.Maximum
        }

        # 連結画像を作成
        $bmpCanvas = New-Object System.Drawing.Bitmap($w, $h)
        $gfx = [System.Drawing.Graphics]::FromImage($bmpCanvas)
        $gfx.FillRectangle([System.Drawing.Brushes]::Transparent, 0, 0, $w, $h)
        $bmp | ForEach-Object { $x=0; $y=0 } {
            $gfx.DrawImage($_, $x, $y);
            if ($Horizontal) { $x += $_.Width } else { $y += $_.Height }
        }
        
        # 画像形式
        $format = switch -Regex ([System.IO.Path]::GetExtension($Dst))
        {
            "\.bmp"   { "Bmp" }
            "\.jpe?g" { "Jpeg" }
            "\.gif"   { "Gif" }
            "\.png"   { "Png" }
            default   { "Png" }
        }

        # 保存
        $bmpCanvas.Save($Dst, $format)
        
        # 破棄
        $gfx.Dispose()
        $bmpCanvas.Dispose()
        $bmp | ForEach-Object { $_.Dispose() }

        [System.IO.Directory]::SetCurrentDirectory($pcd)
    }
}

Export-ModuleMember -Function Save-CombinedImage
```

# コードを書いていて気になった点

* `Set-Location` で カレントディレクトリを変えても `[IO.Directory]::GetCurrentDirectory()` の結果が変わっていない。そのまま `New-Object System.Drawing.Bitmap()` に相対パスを渡すとファイルが見つからず失敗する。`SetCurrentDirectory($PWD)` しておく必要がある

# 参考にした記事

* [PowerShell のモジュール詳解とモジュールへのコマンドレット配置手法を考える](http://tech.guitarrapc.com/entry/2013/12/03/014013)
* [PowerShellらしい関数の書き方 [PS Advent Calendar '12]](http://winscript.jp/powershell/253)
* [Windows PowerShell: パラメーターを定義する](https://technet.microsoft.com/ja-jp/library/jj554301.aspx)
* [Powershellでα画像を合成する方法](http://qiita.com/recomemos/items/528358a1e192aecbc38c)