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

namespace DsImagingWeb.Demos
{
    // このサンプルでは、DsImaging を使用してグロー効果を作成する方法を示します。
    // グロー効果は、画像内のすべての非透明領域を指定した量だけ拡張し、
    // その後ガウシアンブラーを適用して境界を滑らかにします。
    // これは、例えば MS Word にも用意されている効果の一つです。
    //
    // DsImaging でこの効果を実現するには、グローを適用したいカラー画像から
    // 作成した透明マスクに対して GrayscaleBitmap.ApplyGlow() メソッドを使用し、
    // 本コードで示すいくつかの簡単な手順を組み合わせます。
    //
    // また、内容(テキストおよび画像)をぼかした上に、
    // 元の内容を再度描画することでグロー効果を実現しています。
    // 別の方法については、内容を二重に描画せずに済む GlowAlt を参照してください。
    //
    // 同じ GrayscaleBitmap.ApplyGlow() メソッドは、
    // SoftEdges の例で示されているソフトエッジ効果の実現にも使用されます。
    public class Glow
    {
        private GCTEXT.Font _font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "FreeSans.ttf"));

        public GcBitmap GenerateImage(Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
        {
            // 透明なビットマップ上にテキストとロゴを描画します。
            var bmp = new GcBitmap(pixelSize.Width, pixelSize.Height, false);
            bmp.Clear(Color.Transparent);
            drawTextAndLogo();

            // 画像を透過マスクに変換します。
            using var gs = bmp.ToGrayscaleBitmap(ColorChannel.Alpha);

            // 光彩(Glow)エフェクトを適用します。ソフトエッジ(Soft Edges)の場合は、
            // 膨張半径を負の値に設定しますが、光彩の場合は正の値にする必要があります。
            // ここでは6に設定しています(9はぼかし半径です)。
            gs.ApplyGlow(6, 9);

            // 透過マスクからソースのGcBitmapにシャドウをマッピングします。
            // 不透明なピクセルを光彩の色(黄色)で描き、さらに透明度を適用します。
            gs.ToShadowBitmap(bmp, Color.Yellow, 0.8f);

            // 背景を塗りつぶします。
            bmp.ConvertToOpaque(Color.Gray);

            // 最後に、準備した背景の上にテキストとロゴをもう一度描画します。
            drawTextAndLogo();

            return bmp;

            void drawTextAndLogo()
            {
                var renderer = bmp.EnsureRendererCreated();
                DrawText(renderer, pixelSize, dpi);
                DrawLogo(renderer, new PointF(pixelSize.Width / 2, (int)(pixelSize.Height * 0.8)), pixelSize.Width / 2);
            }
        }

        void DrawText(BitmapRenderer renderer, Size pixelSize, float dpi)
        {
            var f1 = new TextFormat
            {
                Font = _font,
                FontSize = pixelSize.Height / 7,
                ForeColor = Color.DarkOrchid,
                FontBold = true
            };
            var f2 = new TextFormat(f1)
            {
                ForeColor = Color.White,
                StrokePen = new GCDRAW.Pen(Color.DarkOrchid, 3)
            };
            var tl = new TextLayout(dpi)
            {
                MaxWidth = pixelSize.Width,
                MaxHeight = pixelSize.Height,
                ParagraphAlignment = ParagraphAlignment.Near,
                TextAlignment = TextAlignment.Center,
                MarginTop = dpi * 2,
                LineSpacingScaleFactor = 0.7f,
            };
            tl.AppendLine("MESCIUS", f1);
            tl.AppendLine("DioDocs", f2);

            // テキストを描画します。
            renderer.SlowAntialiasing = true;
            renderer.DrawTextLayout(tl, 0, 0);
        }

        void DrawLogo(BitmapRenderer renderer, PointF center, float width)
        {
            var pb = new PathBuilder();
            float refCenterX = 400;
            float refCenterY = 300;
            float outerRadius = 250;
            float innerRadius = 100;
            int points = 5;

            for (int i = 0; i < points * 2; i++)
            {
                double angle = i * Math.PI / points - Math.PI / 2;
                float r = (i % 2 == 0) ? outerRadius : innerRadius;
                float x = refCenterX + (float)(Math.Cos(angle) * r);
                float y = refCenterY + (float)(Math.Sin(angle) * r);
                if (i == 0)
                    pb.BeginFigure(x, y);
                else
                    pb.AddLine(x, y);
            }
            pb.EndFigure(true);

            var gpFill = pb.ToPath();
            var gpStroke = gpFill.Widen(new GCDRAW.Pen(Color.Black, 20));

            // 基本となるサイズは 800 x 600 ピクセルです。
            float scale = width / 800;
            renderer.Transform = Matrix3x2.CreateScale(scale) *
                Matrix3x2.CreateTranslation(center.X - 400 * scale, center.Y - 300 * scale);
            renderer.FillPath(gpFill, Color.CornflowerBlue);
            renderer.FillPath(gpStroke, Color.DarkOrchid);
            renderer.Transform = Matrix3x2.Identity;
        }
    }
}