rui_orig/canvas.go

859 lines
31 KiB
Go

package rui
import (
"math"
"strconv"
"strings"
)
// LineJoin is the type for setting the shape used to join two line segments where they meet.
type LineJoin int
// LineCap is the type for setting the shape used to draw the end points of lines.
type LineCap int
// Constants related to canvas view operations
const (
// MiterJoin - Connected segments are joined by extending their outside edges
// to connect at a single point, with the effect of filling an additional
// lozenge-shaped area. This setting is affected by the miterLimit property
MiterJoin LineJoin = 0
// RoundJoin - rounds off the corners of a shape by filling an additional sector
// of disc centered at the common endpoint of connected segments.
// The radius for these rounded corners is equal to the line width.
RoundJoin LineJoin = 1
// BevelJoin - Fills an additional triangular area between the common endpoint
// of connected segments, and the separate outside rectangular corners of each segment.
BevelJoin LineJoin = 2
// ButtCap - the ends of lines are squared off at the endpoints. Default value.
ButtCap LineCap = 0
// RoundCap - the ends of lines are rounded.
RoundCap LineCap = 1
// SquareCap - the ends of lines are squared off by adding a box with an equal width
// and half the height of the line's thickness.
SquareCap LineCap = 2
// AlphabeticBaseline - the text baseline is the normal alphabetic baseline. Default value.
AlphabeticBaseline = 0
// TopBaseline - the text baseline is the top of the em square.
TopBaseline = 1
// MiddleBaseline - the text baseline is the middle of the em square.
MiddleBaseline = 2
// BottomBaseline - the text baseline is the bottom of the bounding box.
// This differs from the ideographic baseline in that the ideographic baseline doesn't consider descenders.
BottomBaseline = 3
// HangingBaseline - the text baseline is the hanging baseline. (Used by Tibetan and other Indic scripts.)
HangingBaseline = 4
// IdeographicBaseline - the text baseline is the ideographic baseline; this is
// the bottom of the body of the characters, if the main body of characters protrudes
// beneath the alphabetic baseline. (Used by Chinese, Japanese, and Korean scripts.)
IdeographicBaseline = 5
// StartAlign - the text is aligned at the normal start of the line (left-aligned
// for left-to-right locales, right-aligned for right-to-left locales).
StartAlign = 3
// EndAlign - the text is aligned at the normal end of the line (right-aligned
// for left-to-right locales, left-aligned for right-to-left locales).
EndAlign = 4
)
// GradientPoint defined by an offset and a color, to a linear or radial gradient
type GradientPoint struct {
// Offset - a number between 0 and 1, inclusive, representing the position of the color stop
Offset float64
// Color - the color of the stop
Color Color
}
// FontParams defined optionally font properties
type FontParams struct {
// Italic - if true then a font is italic
Italic bool
// SmallCaps - if true then a font uses small-caps glyphs
SmallCaps bool
// Weight - a font weight. Valid values: 0...9, there
// 0 - a weight does not specify;
// 1 - a minimal weight;
// 4 - a normal weight;
// 7 - a bold weight;
// 9 - a maximal weight.
Weight int
// LineHeight - the height (relative to the font size of the element itself) of a line box.
LineHeight SizeUnit
}
// TextMetrics is the result of the Canvas.TextMetrics function
type TextMetrics struct {
// Width is the calculated width of a segment of inline text in pixels
Width float64
// Ascent is the distance from the horizontal baseline to the top of the bounding rectangle used to render the text, in pixels.
Ascent float64
// Descent is the distance from the horizontal baseline to the bottom of the bounding rectangle used to render the text, in pixels.
Descent float64
// Left is the distance to the left side of the bounding rectangle of the given text, in pixels;
// positive numbers indicating a distance going left from the given alignment point.
Left float64
// Right is the distance to the right side of the bounding rectangle of the given text, CSS pixels.
Right float64
}
// Canvas is a drawing interface used by the [CanvasView]
type Canvas interface {
// View return the view for the drawing
View() CanvasView
// Width returns the width in pixels of the canvas area
Width() float64
// Height returns the height in pixels of the canvas area
Height() float64
// Save saves the entire state of the canvas by pushing the current state onto a stack.
Save()
// Restore restores the most recently saved canvas state by popping the top entry
// in the drawing state stack. If there is no saved state, this method does nothing.
Restore()
// ClipPath turns the rectangle into the current clipping region. It replaces any previous clipping region.
ClipRect(x, y, width, height float64)
// ClipPath turns the path into the current clipping region. It replaces any previous clipping region.
ClipPath(path Path)
// SetScale adds a scaling transformation to the canvas units horizontally and/or vertically.
// x - scaling factor in the horizontal direction. A negative value flips pixels across
// the vertical axis. A value of 1 results in no horizontal scaling;
// y - scaling factor in the vertical direction. A negative value flips pixels across
// the horizontal axis. A value of 1 results in no vertical scaling.
SetScale(x, y float64)
// SetTranslation adds a translation transformation to the current matrix.
// x - distance to move in the horizontal direction. Positive values are to the right, and negative to the left;
// y - distance to move in the vertical direction. Positive values are down, and negative are up.
SetTranslation(x, y float64)
// SetRotation adds a rotation to the transformation matrix.
// angle - the rotation angle, clockwise in radians
SetRotation(angle float64)
// SetTransformation multiplies the current transformation with the matrix described by the arguments
// of this method. This lets you scale, rotate, translate (move), and skew the context.
// The transformation matrix is described by:
// ⎡ xScale xSkew dx ⎤
// ⎢ ySkew yScale dy ⎥
// ⎣ 0 0 1 ⎦
// xScale, yScale - horizontal and vertical scaling. A value of 1 results in no scaling;
// xSkew, ySkew - horizontal and vertical skewing;
// dx, dy - horizontal and vertical translation (moving).
SetTransformation(xScale, yScale, xSkew, ySkew, dx, dy float64)
// ResetTransformation resets the current transform to the identity matrix
ResetTransformation()
// SetSolidColorFillStyle sets the color to use inside shapes
SetSolidColorFillStyle(color Color)
// SetSolidColorStrokeStyle sets color to use for the strokes (outlines) around shapes
SetSolidColorStrokeStyle(color Color)
// SetLinearGradientFillStyle sets a gradient along the line connecting two given coordinates to use inside shapes
// x0, y0 - coordinates of the start point;
// x1, y1 - coordinates of the end point;
// startColor, endColor - the start and end color
// stopPoints - the array of stop points
SetLinearGradientFillStyle(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint)
// SetLinearGradientStrokeStyle sets a gradient along the line connecting two given coordinates to use for the strokes (outlines) around shapes
// x0, y0 - coordinates of the start point;
// x1, y1 - coordinates of the end point;
// color0, color1 - the start and end color
// stopPoints - the array of stop points
SetLinearGradientStrokeStyle(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint)
// SetRadialGradientFillStyle sets a radial gradient using the size and coordinates of two circles
// to use inside shapes
// x0, y0 - coordinates of the center of the start circle;
// r0 - the radius of the start circle;
// x1, y1 - coordinates the center of the end circle;
// r1 - the radius of the end circle;
// color0, color1 - the start and end color
// stopPoints - the array of stop points
SetRadialGradientFillStyle(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint)
// SetRadialGradientStrokeStyle sets a radial gradient using the size and coordinates of two circles
// to use for the strokes (outlines) around shapes
// x0, y0 - coordinates of the center of the start circle;
// r0 - the radius of the start circle;
// x1, y1 - coordinates the center of the end circle;
// r1 - the radius of the end circle;
// color0, color1 - the start and end color
// stopPoints - the array of stop points
SetRadialGradientStrokeStyle(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint)
// SetImageFillStyle set the image as the filling pattern.
// repeat - indicating how to repeat the pattern's image. Possible values are:
// NoRepeat (0) - neither direction,
// RepeatXY (1) - both directions,
// RepeatX (2) - horizontal only,
// RepeatY (3) - vertical only.
SetImageFillStyle(image Image, repeat int)
// SetLineWidth the line width, in coordinate space units. Zero, negative, Infinity, and NaN values are ignored.
SetLineWidth(width float64)
// SetLineJoin sets the shape used to join two line segments where they meet.
// Valid values: MiterJoin (0), RoundJoin (1), BevelJoin (2). All other values are ignored.
SetLineJoin(join LineJoin)
// SetLineJoin sets the shape used to draw the end points of lines.
// Valid values: ButtCap (0), RoundCap (1), SquareCap (2). All other values are ignored.
SetLineCap(cap LineCap)
// SetLineDash sets the line dash pattern used when stroking lines.
// dash - an array of values that specify alternating lengths of lines and gaps which describe the pattern.
// offset - the line dash offset
SetLineDash(dash []float64, offset float64)
// SetFont sets the current text style to use when drawing text
SetFont(name string, size SizeUnit)
// SetFontWithParams sets the current text style to use when drawing text
SetFontWithParams(name string, size SizeUnit, params FontParams)
// TextWidth calculates metrics of the text drawn by a given font
TextMetrics(text string, fontName string, fontSize SizeUnit, fontParams FontParams) TextMetrics
// SetTextBaseline sets the current text baseline used when drawing text. Valid values:
// AlphabeticBaseline (0), TopBaseline (1), MiddleBaseline (2), BottomBaseline (3),
// HangingBaseline (4), and IdeographicBaseline (5). All other values are ignored.
SetTextBaseline(baseline int)
// SetTextAlign sets the current text alignment used when drawing text. Valid values:
// LeftAlign (0), RightAlign (1), CenterAlign (2), StartAlign (3), and EndAlign(4). All other values are ignored.
SetTextAlign(align int)
// SetShadow sets shadow parameters:
// offsetX, offsetY - the distance that shadows will be offset horizontally and vertically;
// blur - the amount of blur applied to shadows. Must be non-negative;
// color - the color of shadows.
SetShadow(offsetX, offsetY, blur float64, color Color)
// ResetShadow sets shadow parameters to default values (invisible shadow)
ResetShadow()
// ClearRect erases the pixels in a rectangular area by setting them to transparent black
ClearRect(x, y, width, height float64)
// FillRect draws a rectangle that is filled according to the current FillStyle.
FillRect(x, y, width, height float64)
// StrokeRect draws a rectangle that is stroked (outlined) according to the current strokeStyle
// and other context settings
StrokeRect(x, y, width, height float64)
// FillAndStrokeRect draws a rectangle that is filled according to the current FillStyle and
// is stroked (outlined) according to the current strokeStyle and other context settings
FillAndStrokeRect(x, y, width, height float64)
// FillRoundedRect draws a rounded rectangle that is filled according to the current FillStyle.
FillRoundedRect(x, y, width, height, r float64)
// StrokeRoundedRect draws a rounded rectangle that is stroked (outlined) according
// to the current strokeStyle and other context settings
StrokeRoundedRect(x, y, width, height, r float64)
// FillAndStrokeRoundedRect draws a rounded rectangle that is filled according to the current FillStyle
// and is stroked (outlined) according to the current strokeStyle and other context settings
FillAndStrokeRoundedRect(x, y, width, height, r float64)
// FillEllipse draws a ellipse that is filled according to the current FillStyle.
// x, y - coordinates of the ellipse's center;
// radiusX - the ellipse's major-axis radius. Must be non-negative;
// radiusY - the ellipse's minor-axis radius. Must be non-negative;
// rotation - the rotation of the ellipse, expressed in radians.
FillEllipse(x, y, radiusX, radiusY, rotation float64)
// StrokeRoundedRect draws a ellipse that is stroked (outlined) according
// to the current strokeStyle and other context settings
StrokeEllipse(x, y, radiusX, radiusY, rotation float64)
// FillAndStrokeEllipse draws a ellipse that is filled according to the current FillStyle
// and is stroked (outlined) according to the current strokeStyle and other context settings
FillAndStrokeEllipse(x, y, radiusX, radiusY, rotation float64)
// NewPath creates a new Path object
NewPath() Path
// NewPathFromSvg creates a new Path and initialize it by a string consisting of SVG path data
NewPathFromSvg(data string) Path
// FillPath draws a path that is filled according to the current FillStyle.
FillPath(path Path)
// StrokePath draws a path that is stroked (outlined) according to the current strokeStyle
// and other context settings
StrokePath(path Path)
// FillAndStrokeRect draws a path that is filled according to the current FillStyle and
// is stroked (outlined) according to the current strokeStyle and other context settings
FillAndStrokePath(path Path)
// DrawLine draws a line according to the current strokeStyle and other context settings
DrawLine(x0, y0, x1, y1 float64)
// FillText draws a text string at the specified coordinates, filling the string's characters
// with the current FillStyle
FillText(x, y float64, text string)
// StrokeText strokes — that is, draws the outlines of — the characters of a text string
// at the specified coordinates
StrokeText(x, y float64, text string)
// DrawImage draws the image at the (x, y) position
DrawImage(x, y float64, image Image)
// DrawImageInRect draws the image in the rectangle (x, y, width, height), scaling in height and width if necessary
DrawImageInRect(x, y, width, height float64, image Image)
// DrawImageFragment draws the fragment (described by srcX, srcY, srcWidth, srcHeight) of image
// in the rectangle (dstX, dstY, dstWidth, dstHeight), scaling in height and width if necessary
DrawImageFragment(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight float64, image Image)
finishDraw()
}
type canvasData struct {
view CanvasView
session Session
}
func newCanvas(view CanvasView) Canvas {
session := view.Session()
if !session.canvasStart(view.htmlID()) {
return nil
}
canvas := new(canvasData)
canvas.view = view
canvas.session = session
return canvas
}
func (canvas *canvasData) finishDraw() {
canvas.session.canvasFinish()
}
func (canvas *canvasData) View() CanvasView {
return canvas.view
}
func (canvas *canvasData) Width() float64 {
if canvas.view != nil {
return canvas.view.Frame().Width
}
return 0
}
func (canvas *canvasData) Height() float64 {
if canvas.view != nil {
return canvas.view.Frame().Height
}
return 0
}
func (canvas *canvasData) Save() {
canvas.session.callCanvasFunc("save")
}
func (canvas *canvasData) Restore() {
canvas.session.callCanvasFunc("restore")
}
func (canvas *canvasData) ClipRect(x, y, width, height float64) {
canvas.session.callCanvasFunc("beginPath")
canvas.session.callCanvasFunc("rect", x, y, width, height)
canvas.session.callCanvasFunc("clip")
}
func (canvas *canvasData) ClipPath(path Path) {
canvas.session.callCanvasFunc("clip", path.obj())
}
func (canvas *canvasData) SetScale(x, y float64) {
canvas.session.callCanvasFunc("scale", x, y)
}
func (canvas *canvasData) SetTranslation(x, y float64) {
canvas.session.callCanvasFunc("translate", x, y)
}
func (canvas *canvasData) SetRotation(angle float64) {
canvas.session.callCanvasFunc("rotate", angle)
}
func (canvas *canvasData) SetTransformation(xScale, yScale, xSkew, ySkew, dx, dy float64) {
canvas.session.callCanvasFunc("transform", xScale, ySkew, xSkew, yScale, dx, dy)
}
func (canvas *canvasData) ResetTransformation() {
canvas.session.callCanvasFunc("resetTransform")
canvas.session.callCanvasFunc("scale", canvas.session.PixelRatio(), canvas.session.PixelRatio())
//canvas.session.callCanvasFunc("scale", angle)
// TODO canvas.script.WriteString("\nctx.resetTransform();\nctx.scale(dpr, dpr);")
}
func (canvas *canvasData) SetSolidColorFillStyle(color Color) {
canvas.session.updateCanvasProperty("fillStyle", color.cssString())
}
func (canvas *canvasData) SetSolidColorStrokeStyle(color Color) {
canvas.session.updateCanvasProperty("strokeStyle", color.cssString())
}
func (canvas *canvasData) createLinearGradient(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint) any {
gradient := canvas.session.createCanvasVar("createLinearGradient", x0, y0, x1, y1)
canvas.session.callCanvasVarFunc(gradient, "addColorStop", 0, color0.cssString())
for _, point := range stopPoints {
if point.Offset >= 0 && point.Offset <= 1 {
canvas.session.callCanvasVarFunc(gradient, "addColorStop", point.Offset, point.Color.cssString())
}
}
canvas.session.callCanvasVarFunc(gradient, "addColorStop", 1, color1.cssString())
return gradient
}
func (canvas *canvasData) SetLinearGradientFillStyle(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint) {
gradient := canvas.createLinearGradient(x0, y0, color0, x1, y1, color1, stopPoints)
canvas.session.updateCanvasProperty("fillStyle", gradient)
}
func (canvas *canvasData) SetLinearGradientStrokeStyle(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint) {
gradient := canvas.createLinearGradient(x0, y0, color0, x1, y1, color1, stopPoints)
canvas.session.updateCanvasProperty("strokeStyle", gradient)
}
func (canvas *canvasData) createRadialGradient(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint) any {
gradient := canvas.session.createCanvasVar("createRadialGradient", x0, y0, r0, x1, y1, r1)
canvas.session.callCanvasVarFunc(gradient, "addColorStop", 0, color0.cssString())
for _, point := range stopPoints {
if point.Offset >= 0 && point.Offset <= 1 {
canvas.session.callCanvasVarFunc(gradient, "addColorStop", point.Offset, point.Color.cssString())
}
}
canvas.session.callCanvasVarFunc(gradient, "addColorStop", 1, color1.cssString())
return gradient
}
func (canvas *canvasData) SetRadialGradientFillStyle(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint) {
gradient := canvas.createRadialGradient(x0, y0, r0, color0, x1, y1, r1, color1, stopPoints)
canvas.session.updateCanvasProperty("fillStyle", gradient)
}
func (canvas *canvasData) SetRadialGradientStrokeStyle(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint) {
gradient := canvas.createRadialGradient(x0, y0, r0, color0, x1, y1, r1, color1, stopPoints)
canvas.session.updateCanvasProperty("strokeStyle", gradient)
}
func (canvas *canvasData) SetImageFillStyle(image Image, repeat int) {
if image == nil || image.LoadingStatus() != ImageReady {
return
}
var repeatText string
switch repeat {
case NoRepeat:
repeatText = "no-repeat"
case RepeatXY:
repeatText = "repeat"
case RepeatX:
repeatText = "repeat-x"
case RepeatY:
repeatText = "repeat-y"
default:
return
}
canvas.session.callCanvasImageFunc(image.URL(), "fillStyle", "createPattern", repeatText)
}
func (canvas *canvasData) SetLineWidth(width float64) {
if width > 0 {
canvas.session.updateCanvasProperty("lineWidth", width)
}
}
func (canvas *canvasData) SetLineJoin(join LineJoin) {
switch join {
case MiterJoin:
canvas.session.updateCanvasProperty("lineJoin", "miter")
case RoundJoin:
canvas.session.updateCanvasProperty("lineJoin", "round")
case BevelJoin:
canvas.session.updateCanvasProperty("lineJoin", "bevel")
}
}
func (canvas *canvasData) SetLineCap(cap LineCap) {
switch cap {
case ButtCap:
canvas.session.updateCanvasProperty("lineCap", "butt")
case RoundCap:
canvas.session.updateCanvasProperty("lineCap", "round")
case SquareCap:
canvas.session.updateCanvasProperty("lineCap", "square")
}
}
func (canvas *canvasData) SetLineDash(dash []float64, offset float64) {
/*buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
lead := '['
for _, val := range dash {
buffer.WriteRune(lead)
lead = ','
buffer.WriteString(fmt.Sprintf("%g", val))
}
buffer.WriteRune(']')
canvas.session.callCanvasFunc("setLineDash", buffer.String())
*/
canvas.session.callCanvasFunc("setLineDash", dash)
if offset >= 0 {
canvas.session.updateCanvasProperty("lineDashOffset", offset)
}
}
/*
func (canvas *canvasData) convertFont(name string) string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
for i, font := range strings.Split(name, ",") {
font = strings.Trim(font, " \n\"'")
if i > 0 {
buffer.WriteRune(',')
}
if strings.Contains(font, " ") {
buffer.WriteRune('"')
buffer.WriteString(font)
buffer.WriteRune('"')
} else {
buffer.WriteString(font)
}
}
return buffer.String()
}
*/
func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params FontParams) string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
if params.Italic {
buffer.WriteString("italic ")
}
if params.SmallCaps {
buffer.WriteString("small-caps ")
}
if params.Weight > 0 && params.Weight <= 9 {
switch params.Weight {
case 4:
buffer.WriteString("normal ")
case 7:
buffer.WriteString("bold ")
default:
buffer.WriteString(strconv.Itoa(params.Weight * 100))
buffer.WriteRune(' ')
}
}
buffer.WriteString(size.cssString("1rem", canvas.View().Session()))
switch params.LineHeight.Type {
case Auto:
case SizeInPercent:
if params.LineHeight.Value != 100 {
buffer.WriteString("/")
buffer.WriteString(strconv.FormatFloat(params.LineHeight.Value/100, 'g', -1, 64))
}
case SizeInFraction:
if params.LineHeight.Value != 1 {
buffer.WriteString("/")
buffer.WriteString(strconv.FormatFloat(params.LineHeight.Value, 'g', -1, 64))
}
default:
buffer.WriteString("/")
buffer.WriteString(params.LineHeight.cssString("", canvas.View().Session()))
}
names := strings.Split(name, ",")
lead := " "
for _, font := range names {
font = strings.Trim(font, " \n\"'")
buffer.WriteString(lead)
lead = ","
if strings.Contains(font, " ") {
buffer.WriteRune('"')
buffer.WriteString(font)
buffer.WriteRune('"')
} else {
buffer.WriteString(font)
}
}
return buffer.String()
}
func (canvas *canvasData) SetFont(name string, size SizeUnit) {
canvas.session.updateCanvasProperty("font", canvas.fontWithParams(name, size, FontParams{}))
}
func (canvas *canvasData) SetFontWithParams(name string, size SizeUnit, params FontParams) {
canvas.session.updateCanvasProperty("font", canvas.fontWithParams(name, size, params))
}
func (canvas *canvasData) TextMetrics(text string, fontName string, fontSize SizeUnit, fontParams FontParams) TextMetrics {
return canvas.session.canvasTextMetrics(canvas.view.htmlID(), canvas.fontWithParams(fontName, fontSize, fontParams), text)
}
func (canvas *canvasData) SetTextBaseline(baseline int) {
switch baseline {
case AlphabeticBaseline:
canvas.session.updateCanvasProperty("textBaseline", "alphabetic")
case TopBaseline:
canvas.session.updateCanvasProperty("textBaseline", "top")
case MiddleBaseline:
canvas.session.updateCanvasProperty("textBaseline", "middle")
case BottomBaseline:
canvas.session.updateCanvasProperty("textBaseline", "bottom")
case HangingBaseline:
canvas.session.updateCanvasProperty("textBaseline", "hanging")
case IdeographicBaseline:
canvas.session.updateCanvasProperty("textBaseline", "ideographic")
}
}
func (canvas *canvasData) SetTextAlign(align int) {
switch align {
case LeftAlign:
canvas.session.updateCanvasProperty("textAlign", "left")
case RightAlign:
canvas.session.updateCanvasProperty("textAlign", "right")
case CenterAlign:
canvas.session.updateCanvasProperty("textAlign", "center")
case StartAlign:
canvas.session.updateCanvasProperty("textAlign", "start")
case EndAlign:
canvas.session.updateCanvasProperty("textAlign", "end")
}
}
func (canvas *canvasData) SetShadow(offsetX, offsetY, blur float64, color Color) {
if color.Alpha() > 0 && blur >= 0 {
canvas.session.updateCanvasProperty("shadowColor", color.cssString())
canvas.session.updateCanvasProperty("shadowOffsetX", offsetX)
canvas.session.updateCanvasProperty("shadowOffsetY", offsetY)
canvas.session.updateCanvasProperty("shadowBlur", blur)
}
}
func (canvas *canvasData) ResetShadow() {
canvas.session.updateCanvasProperty("shadowColor", "rgba(0,0,0,0)")
canvas.session.updateCanvasProperty("shadowOffsetX", 0)
canvas.session.updateCanvasProperty("shadowOffsetY", 0)
canvas.session.updateCanvasProperty("shadowBlur", 0)
}
func (canvas *canvasData) ClearRect(x, y, width, height float64) {
canvas.session.callCanvasFunc("clearRect", x, y, width, height)
}
func (canvas *canvasData) FillRect(x, y, width, height float64) {
canvas.session.callCanvasFunc("fillRect", x, y, width, height)
}
func (canvas *canvasData) StrokeRect(x, y, width, height float64) {
canvas.session.callCanvasFunc("strokeRect", x, y, width, height)
}
func (canvas *canvasData) FillAndStrokeRect(x, y, width, height float64) {
canvas.FillRect(x, y, width, height)
canvas.StrokeRect(x, y, width, height)
}
func (canvas *canvasData) createRoundedRect(x, y, width, height, r float64) {
left := strconv.FormatFloat(x, 'g', -1, 64)
top := strconv.FormatFloat(y, 'g', -1, 64)
right := strconv.FormatFloat(x+width, 'g', -1, 64)
bottom := strconv.FormatFloat(y+height, 'g', -1, 64)
leftR := strconv.FormatFloat(x+r, 'g', -1, 64)
topR := strconv.FormatFloat(y+r, 'g', -1, 64)
rightR := strconv.FormatFloat(x+width-r, 'g', -1, 64)
bottomR := strconv.FormatFloat(y+height-r, 'g', -1, 64)
radius := strconv.FormatFloat(r, 'g', -1, 64)
canvas.session.callCanvasFunc("beginPath")
canvas.session.callCanvasFunc("moveTo", left, topR)
canvas.session.callCanvasFunc("arc", leftR, topR, radius, math.Pi, math.Pi*3/2)
canvas.session.callCanvasFunc("lineTo", rightR, top)
canvas.session.callCanvasFunc("arc", rightR, topR, radius, math.Pi*3/2, math.Pi*2)
canvas.session.callCanvasFunc("lineTo", right, bottomR)
canvas.session.callCanvasFunc("arc", rightR, bottomR, radius, 0, math.Pi/2)
canvas.session.callCanvasFunc("lineTo", leftR, bottom)
canvas.session.callCanvasFunc("arc", leftR, bottomR, radius, math.Pi/2, math.Pi)
canvas.session.callCanvasFunc("closePath")
}
func (canvas *canvasData) FillRoundedRect(x, y, width, height, r float64) {
canvas.createRoundedRect(x, y, width, height, r)
canvas.session.callCanvasFunc("fill")
}
func (canvas *canvasData) StrokeRoundedRect(x, y, width, height, r float64) {
canvas.createRoundedRect(x, y, width, height, r)
canvas.session.callCanvasFunc("stroke")
}
func (canvas *canvasData) FillAndStrokeRoundedRect(x, y, width, height, r float64) {
canvas.createRoundedRect(x, y, width, height, r)
canvas.session.callCanvasFunc("fill")
canvas.session.callCanvasFunc("stroke")
}
func (canvas *canvasData) createEllipse(x, y, radiusX, radiusY, rotation float64) {
canvas.session.callCanvasFunc("beginPath")
canvas.session.callCanvasFunc("moveTo", x+radiusX, y)
canvas.session.callCanvasFunc("ellipse", x, y, radiusX, radiusY, rotation, 0, math.Pi*2)
//canvas.session.callCanvasFunc("closePath")
}
func (canvas *canvasData) FillEllipse(x, y, radiusX, radiusY, rotation float64) {
if radiusX >= 0 && radiusY >= 0 {
canvas.createEllipse(x, y, radiusX, radiusY, rotation)
canvas.session.callCanvasFunc("fill")
}
}
func (canvas *canvasData) StrokeEllipse(x, y, radiusX, radiusY, rotation float64) {
if radiusX >= 0 && radiusY >= 0 {
canvas.createEllipse(x, y, radiusX, radiusY, rotation)
canvas.session.callCanvasFunc("stroke")
}
}
func (canvas *canvasData) FillAndStrokeEllipse(x, y, radiusX, radiusY, rotation float64) {
if radiusX >= 0 && radiusY >= 0 {
canvas.createEllipse(x, y, radiusX, radiusY, rotation)
canvas.session.callCanvasFunc("fill")
canvas.session.callCanvasFunc("stroke")
}
}
/*
func (canvas *canvasData) writePointArgs(x, y float64) {
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
}
func (canvas *canvasData) writeStringArgs(text string, script *strings.Builder) {
//rText := []rune(text)
for _, ch := range text {
switch ch {
case '\t':
script.WriteString(`\t`)
case '\n':
script.WriteString(`\n`)
case '\r':
script.WriteString(`\r`)
case '\\':
script.WriteString(`\\`)
case '"':
script.WriteString(`\"`)
case '\'':
script.WriteString(`\'`)
default:
if ch < ' ' {
script.WriteString(fmt.Sprintf("\\x%02X", int(ch)))
} else {
script.WriteRune(ch)
}
}
}
}
*/
func (canvas *canvasData) FillText(x, y float64, text string) {
canvas.session.callCanvasFunc("fillText", text, x, y)
}
func (canvas *canvasData) StrokeText(x, y float64, text string) {
canvas.session.callCanvasFunc("strokeText", text, x, y)
}
func (canvas *canvasData) FillPath(path Path) {
canvas.session.callCanvasFunc("fill", path.obj())
}
func (canvas *canvasData) StrokePath(path Path) {
canvas.session.callCanvasFunc("stroke", path.obj())
}
func (canvas *canvasData) FillAndStrokePath(path Path) {
canvas.session.callCanvasFunc("fill", path.obj())
canvas.session.callCanvasFunc("stroke", path.obj())
}
func (canvas *canvasData) DrawLine(x0, y0, x1, y1 float64) {
canvas.session.callCanvasFunc("beginPath")
canvas.session.callCanvasFunc("moveTo", x0, y0)
canvas.session.callCanvasFunc("lineTo", x1, y1)
canvas.session.callCanvasFunc("stroke")
}
func (canvas *canvasData) DrawImage(x, y float64, image Image) {
if image == nil || image.LoadingStatus() != ImageReady {
return
}
canvas.session.callCanvasImageFunc(image.URL(), "", "drawImage", x, y)
}
func (canvas *canvasData) DrawImageInRect(x, y, width, height float64, image Image) {
if image == nil || image.LoadingStatus() != ImageReady {
return
}
canvas.session.callCanvasImageFunc(image.URL(), "", "drawImage", x, y, width, height)
}
func (canvas *canvasData) DrawImageFragment(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight float64, image Image) {
if image == nil || image.LoadingStatus() != ImageReady {
return
}
canvas.session.callCanvasImageFunc(image.URL(), "", "drawImage", srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight)
}