Julia / Plots によるプロット画像化の過程を追う

# 知りたいこと

```julia
using Plots

plt = plot([sin, cos])
savefig(plt, "sincos.png")
```

![sincos.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202534/e15cf7fd-e841-7ff8-1db0-ba76374ad88c.png)


上の例において、プロットを画像化する際の処理を詳しく調べたい。

`plt` の内部構造も気になるところだが、その点については機会を改めたい。

# output.jl

https://github.com/JuliaPlots/Plots.jl/blob/master/src/output.jl

## savefig

https://github.com/JuliaPlots/Plots.jl/blob/master/src/output.jl#L111

```julia
savefig(plt, "sincos.png")
```

ファイル名の拡張子に応じて、対応する関数 (この場合は `png`) に処理を受け渡す。


## png

https://github.com/JuliaPlots/Plots.jl/blob/master/src/output.jl#L4

```julia
png(plt, "sincos.png")
```

PNG の MIME を持たせて `Base.show` に処理を受け渡す。

## Base.show

https://github.com/JuliaPlots/Plots.jl/blob/master/src/output.jl#L210
（様々な MIME に対してメタプログラミングで定義されている）

```julia
io = open("sincos.png", "w")
show(io, MIME("image/png"), plt)
```

本来は、名前の通りに表示を目的とした関数である。
https://docs.julialang.org/en/v1/base/io-network/#Base.show-Tuple{IO,Any,Any}

ファイルへの書き込みは IO にデータを流し込むという処理であり、表示と共通化できるので、 `Base.show` でまとめて請け負っているのだろう。

`prepare_output` （ビューポイントの設定？）の後、内部関数 `_show` に処理を受け渡す。

--
【補足】
例えば、

```julia
plt = plot([sin, cos])
```

などで直接プロットが表示される場合も、暗黙的に呼ばれる `display(plt)` からこの `show` に処理が移譲される。

# backends/gr.jl

内部関数 `_show` について説明していく。
https://github.com/JuliaPlots/Plots.jl/blob/master/src/backends/gr.jl#L1968

## バックエンドに応じた分岐

この `_show` で、バックエンドごとに処理が分岐する。バックエンドの情報を `plt` 変数の型パラメータで保持し、多重ディスパッチしている。

```julia
typeof(plt)
# Result: Plots.Plot{Plots.GRBackend}

_show(io, MIME("image/png"), plt)

# backends/gr.jl
function _show(io::IO, ::MIME{Symbol("image/png")}, plt::Plot{GRBackend})
    # 省略
end
```

本記事ではバックエンドを GR として進めていくが、他のバックエンドでもそれぞれ `_show` が定義されている。プロット時には、バックエンドに対応した `_show` メソッドが呼ばれることになる。各バックエンドでの `_show` の定義は backends ディレクトリに含まれている。

## _show の定義を得る

そのままでは読みにくいので、 `quote` で囲んで補間しよう。
https://docs.julialang.org/en/v1/manual/metaprogramming/#Interpolation-1

```julia
mime, fmt = "image/png" => "png"

quote
    function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
        GR.emergencyclosegks()
        filepath = tempname() * "." * $fmt
        env = get(ENV, "GKSwstype", "0")
        ENV["GKSwstype"] = $fmt
        ENV["GKS_FILEPATH"] = filepath
        gr_display(plt, $fmt)
        GR.emergencyclosegks()
        write(io, read(filepath, String))
        rm(filepath)
        if env != "0"
            ENV["GKSwstype"] = env
        else
            pop!(ENV,"GKSwstype")
        end
    end
end
```

出力の `quote` を外して、適当にコメントを追加した。

```julia
function _show(io::IO, ::MIME{Symbol("image/png")}, plt::Plot{GRBackend})
    GR.emergencyclosegks()
    filepath = tempname() * "." * "png"  # データのやり取りに使用する一時ファイル名
    env = get(ENV, "GKSwstype", "0")     # 設定の退避
    ENV["GKSwstype"] = "png"             # フォーマットを環境変数で設定
    ENV["GKS_FILEPATH"] = filepath       # 出力ファイル名も同様
    gr_display(plt, "png")               # プロットを画像化して一時ファイルに保存
    GR.emergencyclosegks()
    write(io, read(filepath, String))    # 画像を読み出して IO に出力
    rm(filepath)                         # 一時ファイルを削除
    if env != "0"
        ENV["GKSwstype"] = env           # 設定を元に戻す
    else
        pop!(ENV, "GKSwstype")
    end
end
```

GR の環境変数を用いた設定一覧
https://gr-framework.org/environment_variables.html