''
'' This code is part of Document Solutions for Imaging demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System.Drawing
Imports System.IO
Imports System.Linq
Imports System.Numerics
Imports GrapeCity.Documents.Drawing
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Imaging
Imports GrapeCity.Documents.Imaging.Skia
Imports GCTEXT = GrapeCity.Documents.Text
Imports GCDRAW = GrapeCity.Documents.Drawing
'' Text is always rendered with anti-aliasing in Skia.
'' Graphics rendering on the other hand can be controlled by
'' setting the GcSkiaGraphics.Aliased property.
'' This example shows the difference between shapes drawn
'' with that property set to false (which is the default)
'' and true. Note the more jagged appearance of some lines
'' in the lower part of the image (drawn with Aliased set
'' to false).
Public Class SkiaAntialiasing
Public Shared ReadOnly Property IsSkiaOnly As Boolean = True
Private _font As GCTEXT.Font = Nothing
'' Helper method to draw a polygon and a caption beneath it.
'' Can also be used to just calculate the points without actual drawing.
'' startAngle is for the first point, clockwise from (1,0).
Private Function DrawPolygon(g As GcGraphics, center As PointF, r As Single, n As Integer, startAngle As Single, pen As GCDRAW.Pen, Optional caption As String = Nothing) As PointF()
Dim pts As PointF() = New PointF(n - 1) {}
For i = 0 To n - 1
pts(i) = New PointF(center.X + CSng(r * Math.Cos(startAngle + 2 * Math.PI * i / n)), center.Y + CSng(r * Math.Sin(startAngle + 2 * Math.PI * i / n)))
Next
If pen IsNot Nothing Then
g.DrawPolygon(pts, pen)
End If
If Not String.IsNullOrEmpty(caption) Then
DrawCaption(g, center, r, caption)
End If
Return pts
End Function
'' Helper method to draw a caption beneath a shape:
Private Sub DrawCaption(g As GcGraphics, center As PointF, r As Single, caption As String)
g.DrawString(caption,
New TextFormat() With
{
.Font = _font,
.FontSize = 12
},
New RectangleF(center.X - r, center.Y - r, r * 2, r * 2),
TextAlignment.Center, ParagraphAlignment.Center, False)
End Sub
'' Main entry point.
Public Function GenerateImageStream(targetMime As String, pixelSize As Size, dpi As Single, opaque As Boolean, Optional sampleParams As String() = Nothing) As Stream
Select Case targetMime
Case Util.MimeTypes.JPEG, Util.MimeTypes.PNG, Util.MimeTypes.WEBP
'' OK
Case Else
Throw New Exception("This sample uses Skia to create the image, and only supports JPEG, PNG and WEBP output formats.")
End Select
_font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "FreeSerif.ttf"))
Dim Inch = dpi
Dim bmp = New GcSkiaBitmap(pixelSize.Width, pixelSize.Height, opaque)
Using g = bmp.CreateGraphics(Color.White)
DrawShapes(g, bmp.PixelWidth, bmp.PixelHeight, False)
DrawShapes(g, bmp.PixelWidth, bmp.PixelHeight, True)
End Using
'' Done:
Dim ms = New MemoryStream()
Select Case targetMime
Case Util.MimeTypes.JPEG
bmp.SaveAsJpeg(ms)
Case Util.MimeTypes.PNG
bmp.SaveAsPng(ms)
Case Util.MimeTypes.WEBP
bmp.SaveAsWebp(ms)
Case Else
System.Diagnostics.Debug.Assert(False)
End Select
Return ms
End Function
Private Sub DrawShapes(g As GcGraphics, pixelWidth As Integer, pixelHeight As Integer, aliased As Boolean)
Dim gskia = TryCast(g, GcSkiaGraphics)
If gskia IsNot Nothing Then
gskia.Aliased = aliased
End If
'' Set up the helper layout grid:
Dim Inch = g.Resolution
Dim grid = New With
{
.Cols = 4,
.Rows = 3,
.MarginX = Inch / 6,
.MarginY = Inch / 4,
.Radius = Inch * 0.6F,
.StepX = (pixelWidth - Inch / 2) / 4,
.StepY = (pixelHeight - Inch / 4) / 7F
}
'' Insertion point of the next figure's center:
Dim startIp As PointF = New PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10)
If aliased Then
startIp.Y += pixelHeight \ 2
End If
Dim ip As PointF = startIp
'' Header:
Dim pad = 8
Dim bord = New RectangleF(pad, pad + If(aliased, pixelHeight \ 2, 0), pixelWidth - pad * 2, pixelHeight \ 2 - pad * 2)
g.DrawString($"Shapes (Skia) - Aliased is {aliased}",
New TextFormat() With
{
.Font = _font,
.FontSize = 14
},
bord,
TextAlignment.Center, ParagraphAlignment.Near)
g.DrawRoundRect(bord, pad, Color.MediumPurple, 2, DashStyle.DashDot)
'' Pen used to draw shapes:
Dim pen = New GCDRAW.Pen(Color.Orange, 1)
pen.LineJoin = PenLineJoin.Round
Dim fill = 100 '' Surfaces fill alpha
'' Circle:
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
'' Ellipse:
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
'' Cylinder:
Dim radX As Single = grid.Radius / 1.4F
Dim radY As Single = grid.Radius / 6
Dim height As Single = 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
'' Cube:
Dim cubex As Single = 6
Dim cubePtsFar = DrawPolygon(g, New PointF(ip.X - cubex, ip.Y - cubex), grid.Radius, 4, CSng(-Math.PI) / 4, pen)
Dim cubePtsNear = DrawPolygon(g, New PointF(ip.X + cubex, ip.Y + cubex), grid.Radius, 4, CSng(-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
'' Pentagon:
DrawPolygon(g, ip, grid.Radius, 5, CSng(-Math.PI) / 2, pen, "Pentagon")
ip.X += grid.StepX
'' Hexagon:
'' For sample sake, we apply a transform to make the hexagon wider and shorter:
g.Transform = Matrix3x2.CreateScale(1.4F, 0.8F) * Matrix3x2.CreateTranslation(ip.X, ip.Y)
DrawPolygon(g, PointF.Empty, grid.Radius, 6, 0, pen, Nothing)
g.Transform = Matrix3x2.Identity
DrawCaption(g, ip, grid.Radius, "Hexagon")
ip.X += grid.StepX
'' Octagon:
DrawPolygon(g, ip, grid.Radius, 8, CSng(-Math.PI) / 8, pen, "Octagon")
pen.Color = Color.DarkRed
ip.X += grid.StepX
'' Triangle:
DrawPolygon(g, ip, grid.Radius, 3, CSng(-Math.PI) / 2, pen, "Triangle")
ip.X = startIp.X
ip.Y += grid.StepY
'' Filled pentagram:
Dim pts = DrawPolygon(g, ip, grid.Radius, 5, CSng(-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
'' Set up a simple kind of oblique projection to draw a pyramid:
Dim angle = Math.PI / 6
Dim s As Single = CSng(Math.Sin(angle))
Dim c As Single = CSng(Math.Cos(angle))
Dim project As Func(Of Single, Single, Single, PointF) = Function(x_, y_, z_) New PointF(x_ - c * y_ * 0.5F, -(z_ - s * y_ * 0.5F))
Dim p3d As Func(Of Vector3, PointF) = Function(v_) project(v_.X, v_.Y, v_.Z)
Dim hedge As Single = grid.Radius '' 1/2 edge
'' Debug - draw the 3 axis:
'' g.DrawLine(project(0, 0, 0), project(100, 0, 0), Color.Red);
'' g.DrawLine(project(0, 0, 0), project(0, 100, 0), Color.Green);
'' g.DrawLine(project(0, 0, 0), project(0, 0, 100), Color.Blue);
'' 3d points forming a square pyramid:
Dim 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)
}
'' project the points to draw the pyramid:
pts = pts3d.Select(Function(v_) p3d(v_)).ToArray()
g.Transform = Matrix3x2.CreateTranslation(ip.X, ip.Y + hedge * 0.7F)
'' Visible edges:
g.DrawPolygon(New PointF() {pts(4), pts(1), pts(2), pts(3), pts(4), pts(2)}, pen)
'' Invisible edges:
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
'' Cone:
Dim baseh As Single = grid.Radius * 0.3F
pts = DrawPolygon(g, ip, grid.Radius, 3, CSng(-Math.PI) / 2, Nothing, "Cone")
g.DrawLines(New PointF() {pts(2), pts(0), pts(1)}, pen)
Dim 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
'' Trapezoid (use DrawPolygon to just get the points of the square):
Dim dx As Single = 10
pts = DrawPolygon(g, ip, grid.Radius, 4, CSng(-Math.PI) / 4, Nothing, "Trapezoid")
pts(0).X -= dx
pts(1).X += dx
pts(2).X -= dx
pts(3).X += dx
g.DrawPolygon(pts, pen)
End Sub
End Class