SvgToGrayscale.vb
'' 
'' このコードは、DioDocs for Imaging のサンプルの一部として提供されています。
'' © MESCIUS inc. All rights reserved.
'' 
Imports System
Imports System.IO
Imports System.Drawing
Imports System.Collections.Generic
Imports System.Linq
Imports System.Numerics
Imports GrapeCity.Documents.Drawing
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Imaging
Imports GrapeCity.Documents.Svg
Imports GCTEXT = GrapeCity.Documents.Text
Imports GCDRAW = GrapeCity.Documents.Drawing

'' @このサンプルは @{SvgClipArt} と類似していますが、
'' 各 SVG 画像を読み込んだ後、すべてのストロークおよび塗りつぶしをグレースケールに変換します。
''
'' 本サンプルで使用している SVG のクリップアートは、freesvg.orgから提供されたものです。
Public Class SvgToGrayscale
    Private Sub ToGrayscale(elements As SvgElementCollection)
        For Each el In elements
            If TypeOf el Is SvgGraphicsElement Then
                Dim elg = DirectCast(el, SvgGraphicsElement)
                elg.Stroke = PaintToGrayscale(elg.Stroke)
                elg.Fill = PaintToGrayscale(elg.Fill)
            End If
            ToGrayscale(el.Children)
        Next
    End Sub

    '' SvgPaintをグレースケールに変換するための簡易的なメソッド
    '' (Yの計算式は https://goodcalculators.com/rgb-to-grayscale-conversion-calculator/ に基づいています。)
    Private Function PaintToGrayscale(src As SvgPaint) As SvgPaint
        If src Is Nothing Then
            Return Nothing
        ElseIf src.PaintType = SvgPaintType.Color Then
            Dim rgb = src.Color.Rgb
            Dim Y As Integer = CInt(Math.Round(0.299 * rgb.R + 0.587 * rgb.G + 0.114 * rgb.B))
            Return New SvgPaint(Color.FromArgb(Y, Y, Y))
        Else
            Return New SvgPaint(Color.Gray)
        End If
    End Function

    '' メインのエントリポイント
    Public Function GenerateImage(pixelSize As Size, dpi As Single, opaque As Boolean, Optional sampleParams As String() = Nothing) As GcBitmap
        Const rows As Integer = 4
        Const cols As Integer = 4
        Dim margin As Single = dpi / 2.0F
        Dim sMargin As Single = margin / 4.0F

        '' リソースフォルダから画像を読み込みます。
        Dim fnames As New List(Of String)(Directory.GetFiles(Path.Combine("Resources", "SvgClipArt"), "*", SearchOption.AllDirectories))
        fnames.Shuffle()

        Dim images As New List(Of Tuple(Of String, GcSvgDocument))()
        For Each fname In fnames.Take(rows * cols)
            Dim svg = GcSvgDocument.FromFile(fname)
            ToGrayscale(svg.RootSvg.Children)
            images.Add(Tuple.Create(Path.GetFileName(fname), svg))
        Next

        '' キャプション用のフォントと書式を設定します。
        Dim font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "FreeSans.ttf"))
        Dim tf = New TextFormat() With {.Font = font, .FontSize = sMargin * 0.65F}

        '' 4x4のレイアウトグリッドを設定します。
        Dim gapx As Single = margin / 4.0F, gapy As Single = gapx
        Dim sWidth As Single = (pixelSize.Width - margin * 2 + gapx) / cols
        Dim sHeight As Single = (pixelSize.Height - margin * 2 + gapy) / rows
        If sWidth > sHeight Then
            gapx += sWidth - sHeight
            sWidth = sHeight
        Else
            gapy += sHeight - sWidth
            sHeight = sWidth
        End If
        Dim ip As New PointF(margin, margin)

        '' 結果として出力されるビットマップ
        Dim bmp = New GcBitmap(pixelSize.Width, pixelSize.Height, opaque, dpi, dpi)

        '' グリッド内にすべてのサンプル画像を描画します。
        Using g = bmp.CreateGraphics(Color.White)
            For i As Integer = 0 To images.Count() - 1
                '' サンプル画像の周囲に境界線を描画します。
                Dim rect As New RectangleF(ip, New SizeF(sWidth - gapx, sHeight - gapy))
                g.FillRectangle(rect, Color.LightGray)
                g.DrawRectangle(rect, Color.Black, 0.5F)
                rect.Inflate(-sMargin, -sMargin)

                '' SVGを描画します。
                Dim svg = images(i).Item2
                Dim s = svg.GetIntrinsicSize(SvgLengthUnits.Points)
                If s.Width > 0 AndAlso s.Height > 0 Then
                    '' サンプル画像の比率がターゲットとなる矩形と異なる場合、
                    '' 矩形内の中央に配置されるように矩形をリサイズします。
                    Dim qSrc = s.Width / s.Height
                    Dim qTgt = rect.Width / rect.Height
                    If qSrc < qTgt Then
                        rect.Inflate(rect.Width * (qSrc / qTgt - 1) / 2.0F, 0)
                    ElseIf qSrc > qTgt Then
                        rect.Inflate(0, rect.Height * (qTgt / qSrc - 1) / 2.0F)
                    End If
                End If
                '' SVGを描画します。
                g.DrawSvg(svg, rect)

                '' 下側の余白に画像ファイル名をキャプションとして描画します。
                g.DrawString(Path.GetFileName(images(i).Item1), tf,
                             New RectangleF(rect.X, rect.Bottom, rect.Width, sMargin),
                             TextAlignment.Center, ParagraphAlignment.Near, False)

                ip.X += sWidth
                If (ip.X + sWidth > pixelSize.Width) AndAlso (i < images.Count() - 1) Then
                    ip.X = margin
                    ip.Y += sHeight
                End If
            Next
        End Using

        '' 結果を保存した後、画像を破棄します。
        images.ForEach(Sub(t_) t_.Item2.Dispose())

        Return bmp
    End Function
End Class