SkiaAntialiasing.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 GrapeCity.Documents.Imaging.Skia;
using GCTEXT = GrapeCity.Documents.Text;
using GCDRAW = GrapeCity.Documents.Drawing;
namespace DsImagingWeb.Demos
{
// Skia では、テキストは常にアンチエイリアス付きで描画されます。
// 一方、グラフィック描画は GcSkiaGraphics.Aliased プロパティを設定することで
// 制御できます。
// このサンプルでは、このプロパティを false(既定値)に設定した場合と
// true に設定した場合で描画される図形の違いを示します。
// 画像の下部(Aliased を false に設定して描画した部分)では、
// 一部の線がよりギザギザに見える点に注目してください。
public class SkiaAntialiasing
{
public static bool IsSkiaOnly => true;
private GCTEXT.Font _font = null;
// 多角形とその下にキャプションを描画するためのヘルパーメソッドです。
// 実際の描画を行わずにポイントの計算のみに使用することも可能です。
// startAngle は最初の点の角度で、(1,0) から時計回りです。
private PointF[] DrawPolygon(GcGraphics g, PointF center, float r, int n, float startAngle, GCDRAW.Pen pen, string caption = null)
{
PointF[] pts = new PointF[n];
for (int i = 0; i < n; ++i)
pts[i] = new PointF(center.X + (float)(r * Math.Cos(startAngle + 2 * Math.PI * i / n)), center.Y + (float)(r * Math.Sin(startAngle + 2 * Math.PI * i / n)));
if (pen != null)
g.DrawPolygon(pts, pen);
if (!string.IsNullOrEmpty(caption))
DrawCaption(g, center, r, caption);
return pts;
}
// 図形の下にキャプションを描画するためのヘルパーメソッド
private void DrawCaption(GcGraphics g, PointF center, float r, string caption)
{
g.DrawString(caption,
new TextFormat()
{
Font = _font,
FontSize = 12,
},
new RectangleF(center.X - r, center.Y - r, r * 2, r * 2),
TextAlignment.Center, ParagraphAlignment.Center, false);
}
// メインのエントリポイント
public Stream GenerateImageStream(string targetMime, Size pixelSize, float dpi, bool opaque, string[] sampleParams = null)
{
switch (targetMime)
{
case Common.Util.MimeTypes.JPEG:
case Common.Util.MimeTypes.PNG:
case Common.Util.MimeTypes.WEBP:
break;
default:
throw new Exception("This sample uses Skia to create the image, and only supports JPEG, PNG and WEBP output formats.");
}
_font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "FreeSerif.ttf"));
var Inch = dpi;
var bmp = new GcSkiaBitmap(pixelSize.Width, pixelSize.Height, opaque);
using (var g = bmp.CreateGraphics(Color.White))
{
DrawShapes(g, bmp.PixelWidth, bmp.PixelHeight, false);
DrawShapes(g, bmp.PixelWidth, bmp.PixelHeight, true);
}
var ms = new MemoryStream();
switch (targetMime)
{
case Common.Util.MimeTypes.JPEG:
bmp.SaveAsJpeg(ms);
break;
case Common.Util.MimeTypes.PNG:
bmp.SaveAsPng(ms);
break;
case Common.Util.MimeTypes.WEBP:
bmp.SaveAsWebp(ms);
break;
default:
System.Diagnostics.Debug.Assert(false);
break;
}
return ms;
}
void DrawShapes(GcGraphics g, int pixelWidth, int pixelHeight, bool aliased)
{
if (g is GcSkiaGraphics gskia)
gskia.Aliased = aliased;
// ヘルパーレイアウトグリッドを設定します。
var Inch = g.Resolution;
var grid = new
{
Cols = 4,
Rows = 3,
MarginX = Inch / 6,
MarginY = Inch / 4,
Radius = Inch * 0.6f,
StepX = (pixelWidth - Inch / 2) / 4,
StepY = (pixelHeight - Inch / 4) / 7f,
};
// 次の図形の中心の挿入ポイントです。
PointF startIp = new PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10);
if (aliased)
startIp.Y += pixelHeight / 2;
PointF ip = startIp;
// ヘッダー
var pad = 8;
var bord = new RectangleF(pad, pad + (aliased ? pixelHeight / 2 : 0), pixelWidth - pad * 2, pixelHeight / 2 - pad * 2);
g.DrawString($"Shapes (Skia) - Aliased is {aliased}",
new TextFormat()
{
Font = _font,
FontSize = 14,
},
bord,
TextAlignment.Center, ParagraphAlignment.Near);
g.DrawRoundRect(bord, pad, Color.MediumPurple, 2, DashStyle.DashDot);
// 図形の描画に使用するペンです。
var pen = new GCDRAW.Pen(Color.Orange, 1);
pen.LineJoin = PenLineJoin.Round;
int fill = 100; // 面の塗りつぶしアルファ値です。
// 円
g.DrawEllipse(new RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2), pen);
DrawCaption(g, ip, grid.Radius, "Circle");
ip.X += grid.StepX;
// 楕円
g.DrawEllipse(new RectangleF(ip.X - grid.Radius * 1.4f, ip.Y - grid.Radius / 2, grid.Radius * 2 * 1.4f, grid.Radius), pen);
DrawCaption(g, ip, grid.Radius, "Ellipse");
ip.X += grid.StepX;
// 円柱
float radX = grid.Radius / 1.4f;
float radY = grid.Radius / 6;
float height = grid.Radius * 1.8f;
g.DrawEllipse(new RectangleF(ip.X - radX, ip.Y - height / 2, radX * 2, radY * 2), pen);
g.FillEllipse(new RectangleF(ip.X - radX, ip.Y + height / 2 - radY * 2, radX * 2, radY * 2), Color.FromArgb(fill, pen.Color));
g.DrawEllipse(new RectangleF(ip.X - radX, ip.Y + height / 2 - radY * 2, radX * 2, radY * 2), pen);
g.DrawLine(new PointF(ip.X - radX, ip.Y - height / 2 + radY), new PointF(ip.X - radX, ip.Y + height / 2 - radY), pen);
g.DrawLine(new PointF(ip.X + radX, ip.Y - height / 2 + radY), new PointF(ip.X + radX, ip.Y + height / 2 - radY), pen);
DrawCaption(g, ip, grid.Radius, "Cylinder");
pen.Color = Color.Indigo;
ip.X += grid.StepX;
// 立方体
float cubex = 6;
var cubePtsFar = DrawPolygon(g, new PointF(ip.X - cubex, ip.Y - cubex), grid.Radius, 4, (float)-Math.PI / 4, pen);
var cubePtsNear = DrawPolygon(g, new PointF(ip.X + cubex, ip.Y + cubex), grid.Radius, 4, (float)-Math.PI / 4, pen);
g.DrawLine(cubePtsFar[0], cubePtsNear[0], pen);
g.DrawLine(cubePtsFar[1], cubePtsNear[1], pen);
g.DrawLine(cubePtsFar[2], cubePtsNear[2], pen);
g.DrawLine(cubePtsFar[3], cubePtsNear[3], pen);
g.FillPolygon(new PointF[] { cubePtsFar[1], cubePtsFar[2], cubePtsNear[2], cubePtsNear[1], }, Color.FromArgb(fill, pen.Color));
DrawCaption(g, ip, grid.Radius, "Cube");
pen.Color = Color.DarkGreen;
ip.X = startIp.X;
ip.Y += grid.StepY;
// 五角形
DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pen, "Pentagon");
ip.X += grid.StepX;
// 六角形
// サンプルのため、六角形を横に広く、縦に短くするための変換を適用します。
g.Transform = Matrix3x2.CreateScale(1.4f, 0.8f) * Matrix3x2.CreateTranslation(ip.X, ip.Y);
DrawPolygon(g, PointF.Empty, grid.Radius, 6, 0, pen, null);
g.Transform = Matrix3x2.Identity;
DrawCaption(g, ip, grid.Radius, "Hexagon");
ip.X += grid.StepX;
// 八角形
DrawPolygon(g, ip, grid.Radius, 8, (float)-Math.PI / 8, pen, "Octagon");
pen.Color = Color.DarkRed;
ip.X += grid.StepX;
// 三角形
DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, pen, "Triangle");
ip.X = startIp.X;
ip.Y += grid.StepY;
// 塗りつぶされた五芒星
var pts = DrawPolygon(g, ip, grid.Radius, 5, (float)-Math.PI / 2, pen, "Pentagram");
pts = new PointF[] { pts[0], pts[2], pts[4], pts[1], pts[3], };
g.FillPolygon(pts, Color.FromArgb(fill, pen.Color));
g.DrawPolygon(pts, pen);
ip.X += grid.StepX;
// 四角錐を描画するために、単純な斜投影(Oblique Projection)を設定します。
var angle = Math.PI / 6;
float s = (float)Math.Sin(angle);
float c = (float)Math.Cos(angle);
Func<float, float, float, PointF> project = (x_, y_, z_) => new PointF(x_ - c * y_ * 0.5f, -(z_ - s * y_ * 0.5f));
Func<Vector3, PointF> p3d = v_ => project(v_.X, v_.Y, v_.Z);
float hedge = grid.Radius; // 1/2 エッジ
// 正四角錐を形成する3Dポイントです。
var pts3d = new Vector3[]
{
new Vector3(-hedge, -hedge, 0),
new Vector3(hedge, -hedge, 0),
new Vector3(hedge, hedge, 0),
new Vector3(-hedge, hedge, 0),
new Vector3(0, 0, hedge * 2),
};
// 四角錐を描画するためにポイントを投影します。
pts = pts3d.Select(v_ => p3d(v_)).ToArray();
g.Transform = Matrix3x2.CreateTranslation(ip.X, ip.Y + hedge * 0.7f);
// 可視エッジ
g.DrawPolygon(new PointF[] { pts[4], pts[1], pts[2], pts[3], pts[4], pts[2] }, pen);
// 不可視エッジ
pen.Width /= 2;
pen.Color = Color.FromArgb(fill, pen.Color);
g.DrawLine(pts[0], pts[4], pen);
g.DrawLine(pts[0], pts[1], pen);
g.DrawLine(pts[0], pts[3], pen);
g.FillPolygon(pts.Take(4).ToArray(), pen.Color);
//
g.Transform = Matrix3x2.Identity;
DrawCaption(g, ip, grid.Radius, "Pyramid");
ip.X += grid.StepX;
pen.Width *= 2;
pen.Color = Color.Green;
// 円錐
float baseh = grid.Radius * 0.3f;
pts = DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, null, "Cone");
g.DrawLines(new PointF[] { pts[2], pts[0], pts[1] }, pen);
var rect = new RectangleF(pts[2].X, pts[2].Y - baseh / 2, pts[1].X - pts[2].X, baseh);
g.FillEllipse(rect, Color.FromArgb(fill, pen.Color));
g.DrawEllipse(rect, pen);
ip.X += grid.StepX;
// 台形(DrawPolygon を使用して正方形のポイントのみを取得します)。
float dx = 10;
pts = DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, null, "Trapezoid");
pts[0].X -= dx;
pts[1].X += dx;
pts[2].X -= dx;
pts[3].X += dx;
g.DrawPolygon(pts, pen);
}
}
}