add context api
|
@ -1,9 +1,10 @@
|
|||
import bumpy, chroma, flatty/binny, os, pixie/blends, pixie/common,
|
||||
pixie/fileformats/bmp, pixie/fileformats/gif, pixie/fileformats/jpg,
|
||||
pixie/fileformats/png, pixie/fileformats/svg, pixie/fonts, pixie/images,
|
||||
pixie/masks, pixie/paints, pixie/paths, strutils, vmath
|
||||
pixie/context, pixie/fileformats/bmp, pixie/fileformats/gif,
|
||||
pixie/fileformats/jpg, pixie/fileformats/png, pixie/fileformats/svg,
|
||||
pixie/fonts, pixie/images, pixie/masks, pixie/paints, pixie/paths, strutils, vmath
|
||||
|
||||
export blends, bumpy, chroma, common, fonts, images, masks, paints, paths, vmath
|
||||
export blends, bumpy, chroma, common, context, fonts, images, masks, paints,
|
||||
paths, vmath
|
||||
|
||||
type
|
||||
FileFormat* = enum
|
||||
|
|
231
src/pixie/context.nim
Normal file
|
@ -0,0 +1,231 @@
|
|||
import bumpy, chroma, pixie/blends, pixie/common, pixie/fonts, pixie/images,
|
||||
pixie/paints, pixie/paths, vmath
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/API/ContextRenderingContext2D
|
||||
|
||||
type
|
||||
Context* = ref object
|
||||
image*: Image
|
||||
fillStyle*, strokeStyle*: Paint
|
||||
lineWidth*: float32
|
||||
lineCap*: LineCap
|
||||
lineJoin*: LineJoin
|
||||
font*: Font
|
||||
textAlign*: HAlignMode
|
||||
path: Path
|
||||
mat: Mat3
|
||||
stateStack: seq[ContextState]
|
||||
|
||||
ContextState = object
|
||||
mat: Mat3
|
||||
fillStyle, strokeStyle: Paint
|
||||
lineWidth: float32
|
||||
lineCap: LineCap
|
||||
lineJoin: LineJoin
|
||||
font: Font
|
||||
textAlign: HAlignMode
|
||||
|
||||
proc newContext*(image: Image): Context =
|
||||
result = Context()
|
||||
result.image = image
|
||||
result.mat = mat3()
|
||||
result.lineWidth = 1
|
||||
result.fillStyle = Paint(kind: pkSolid, color: rgbx(0, 0, 0, 255))
|
||||
result.strokeStyle = Paint(kind: pkSolid, color: rgbx(0, 0, 0, 255))
|
||||
|
||||
proc beginPath*(ctx: Context) {.inline.} =
|
||||
ctx.path = Path()
|
||||
|
||||
proc moveTo*(ctx: Context, v: Vec2) {.inline.} =
|
||||
ctx.path.moveTo(v)
|
||||
|
||||
proc moveTo*(ctx: Context, x, y: float32) {.inline.} =
|
||||
ctx.moveTo(vec2(x, y))
|
||||
|
||||
proc lineTo*(ctx: Context, v: Vec2) {.inline.} =
|
||||
ctx.path.lineTo(v)
|
||||
|
||||
proc lineTo*(ctx: Context, x, y: float32) {.inline.} =
|
||||
ctx.lineTo(vec2(x, y))
|
||||
|
||||
proc bezierCurveTo*(ctx: Context, cp1, cp2, to: Vec2) {.inline.} =
|
||||
ctx.path.bezierCurveTo(cp1, cp2, to)
|
||||
|
||||
proc bezierCurveTo*(
|
||||
ctx: Context, cp1x, cp1y, cp2x, cp2y, x, y: float32
|
||||
) {.inline.} =
|
||||
ctx.bezierCurveTo(vec2(cp1x, cp1y), vec2(cp2x, cp2y), vec2(x, y))
|
||||
|
||||
proc quadraticCurveTo*(ctx: Context, cpx, cpy, x, y: float32) {.inline.} =
|
||||
ctx.path.quadraticCurveTo(cpx, cpy, x, y)
|
||||
|
||||
proc quadraticCurveTo*(ctx: Context, ctrl, to: Vec2) {.inline.} =
|
||||
ctx.path.quadraticCurveTo(ctrl, to)
|
||||
|
||||
proc closePath*(ctx: Context) {.inline.} =
|
||||
ctx.path.closePath()
|
||||
|
||||
proc rect*(ctx: Context, rect: Rect) {.inline.} =
|
||||
ctx.path.rect(rect)
|
||||
|
||||
proc rect*(ctx: Context, x, y, width, height: float32) {.inline.} =
|
||||
ctx.path.rect(x, y, width, height)
|
||||
|
||||
proc ellipse*(ctx: Context, center: Vec2, rx, ry: float32) {.inline.} =
|
||||
ctx.path.ellipse(center, rx, ry)
|
||||
|
||||
proc ellipse*(ctx: Context, x, y, rx, ry: float32) {.inline.} =
|
||||
ctx.path.ellipse(x, y, rx, ry)
|
||||
|
||||
proc fill*(ctx: Context, path: Path, windingRule = wrNonZero) {.inline.} =
|
||||
ctx.image.fillPath(
|
||||
path,
|
||||
ctx.fillStyle,
|
||||
ctx.mat,
|
||||
windingRule = windingRule
|
||||
)
|
||||
|
||||
proc fill*(ctx: Context, windingRule = wrNonZero) {.inline.} =
|
||||
ctx.fill(ctx.path, windingRule)
|
||||
|
||||
proc stroke*(ctx: Context, path: Path) {.inline.} =
|
||||
ctx.image.strokePath(
|
||||
path,
|
||||
ctx.strokeStyle,
|
||||
ctx.mat,
|
||||
strokeWidth = ctx.lineWidth,
|
||||
lineCap = ctx.lineCap,
|
||||
lineJoin = ctx.lineJoin
|
||||
)
|
||||
|
||||
proc stroke*(ctx: Context) {.inline.} =
|
||||
ctx.stroke(ctx.path)
|
||||
|
||||
proc clearRect*(ctx: Context, rect: Rect) =
|
||||
var path: Path
|
||||
path.rect(rect)
|
||||
ctx.image.fillPath(path, rgbx(0, 0, 0, 0), ctx.mat, blendMode = bmOverwrite)
|
||||
|
||||
proc clearRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
|
||||
ctx.clearRect(rect(x, y, width, height))
|
||||
|
||||
proc fillRect*(ctx: Context, rect: Rect) =
|
||||
var path: Path
|
||||
path.rect(rect)
|
||||
ctx.image.fillPath(path, ctx.fillStyle, ctx.mat)
|
||||
|
||||
proc fillRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
|
||||
ctx.fillRect(rect(x, y, width, height))
|
||||
|
||||
proc strokeRect*(ctx: Context, rect: Rect) =
|
||||
var path: Path
|
||||
path.rect(rect)
|
||||
ctx.image.strokePath(
|
||||
path,
|
||||
ctx.strokeStyle,
|
||||
ctx.mat,
|
||||
ctx.lineWidth,
|
||||
ctx.lineCap,
|
||||
ctx.lineJoin
|
||||
)
|
||||
|
||||
proc strokeRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
|
||||
ctx.strokeRect(rect(x, y, width, height))
|
||||
|
||||
proc fillText*(ctx: Context, text: string, at: Vec2) =
|
||||
if ctx.font.typeface == nil:
|
||||
raise newException(PixieError, "No font has been set on this Context")
|
||||
|
||||
# Canvas positions text relative to the alphabetic baseline by default
|
||||
var at = at
|
||||
at.y -= round(ctx.font.typeface.ascent * ctx.font.scale)
|
||||
|
||||
ctx.font.paint = ctx.fillStyle
|
||||
ctx.image.fillText(
|
||||
ctx.font,
|
||||
text,
|
||||
at,
|
||||
hAlign = ctx.textAlign
|
||||
)
|
||||
|
||||
proc fillText*(ctx: Context, text: string, x, y: float32) {.inline.} =
|
||||
ctx.fillText(text, vec2(x, y))
|
||||
|
||||
proc strokeText*(ctx: Context, text: string, at: Vec2) =
|
||||
if ctx.font.typeface == nil:
|
||||
raise newException(PixieError, "No font has been set on this Context")
|
||||
|
||||
# Canvas positions text relative to the alphabetic baseline by default
|
||||
var at = at
|
||||
at.y -= round(ctx.font.typeface.ascent * ctx.font.scale)
|
||||
|
||||
ctx.font.paint = ctx.strokeStyle
|
||||
ctx.image.strokeText(
|
||||
ctx.font,
|
||||
text,
|
||||
at,
|
||||
ctx.lineWidth,
|
||||
hAlign = ctx.textAlign,
|
||||
lineCap = ctx.lineCap,
|
||||
lineJoin = ctx.lineJoin
|
||||
)
|
||||
|
||||
proc strokeText*(ctx: Context, text: string, x, y: float32) {.inline.} =
|
||||
ctx.strokeText(text, vec2(x, y))
|
||||
|
||||
proc getTransform*(ctx: Context): Mat3 {.inline.} =
|
||||
ctx.mat
|
||||
|
||||
proc setTransform*(ctx: Context, transform: Mat3) {.inline.} =
|
||||
ctx.mat = transform
|
||||
|
||||
proc setTransform*(ctx: Context, a, b, c, d, e, f: float32) {.inline.} =
|
||||
ctx.mat = mat3(a, b, 0, c, d, 0, e, f, 1)
|
||||
|
||||
proc transform*(ctx: Context, transform: Mat3) {.inline.} =
|
||||
ctx.mat = ctx.mat * transform
|
||||
|
||||
proc transform*(ctx: Context, a, b, c, d, e, f: float32) {.inline.} =
|
||||
ctx.transform(mat3(a, b, 0, c, d, 0, e, f, 1))
|
||||
|
||||
proc translate*(ctx: Context, v: Vec2) {.inline.} =
|
||||
ctx.mat = ctx.mat * translate(v)
|
||||
|
||||
proc translate*(ctx: Context, x, y: float32) {.inline.} =
|
||||
ctx.mat = ctx.mat * translate(vec2(x, y))
|
||||
|
||||
proc scale*(ctx: Context, v: Vec2) {.inline.} =
|
||||
ctx.mat = ctx.mat * scale(v)
|
||||
|
||||
proc scale*(ctx: Context, x, y: float32) {.inline.} =
|
||||
ctx.mat = ctx.mat * scale(vec2(x, y))
|
||||
|
||||
proc rotate*(ctx: Context, angle: float32) {.inline.} =
|
||||
ctx.mat = ctx.mat * rotate(-angle)
|
||||
|
||||
proc resetTransform*(ctx: Context) {.inline.} =
|
||||
ctx.mat = mat3()
|
||||
|
||||
proc save*(ctx: Context) =
|
||||
var state: ContextState
|
||||
state.mat = ctx.mat
|
||||
state.fillStyle = ctx.fillStyle
|
||||
state.strokeStyle = ctx.strokeStyle
|
||||
state.lineWidth = ctx.lineWidth
|
||||
state.lineCap = ctx.lineCap
|
||||
state.lineJoin = ctx.lineJoin
|
||||
state.font = ctx.font
|
||||
state.textAlign = ctx.textAlign
|
||||
ctx.stateStack.add(state)
|
||||
|
||||
proc restore*(ctx: Context) =
|
||||
if ctx.stateStack.len > 0:
|
||||
let state = ctx.stateStack.pop()
|
||||
ctx.mat = state.mat
|
||||
ctx.fillStyle = state.fillStyle
|
||||
ctx.strokeStyle = state.strokeStyle
|
||||
ctx.lineWidth = state.lineWidth
|
||||
ctx.lineCap = state.lineCap
|
||||
ctx.lineJoin = state.lineJoin
|
||||
ctx.font = state.font
|
||||
ctx.textAlign = state.textAlign
|
|
@ -1,4 +1,5 @@
|
|||
import opengl, pixie, staticglfw
|
||||
|
||||
export pixie, staticglfw
|
||||
|
||||
type Image = pixie.Image
|
||||
|
|
|
@ -364,7 +364,7 @@ proc decodeSvg*(data: string, width = 0, height = 0): Image =
|
|||
viewBox = root.attr("viewBox")
|
||||
box = viewBox.split(" ")
|
||||
viewBoxMinX = parseInt(box[0])
|
||||
viewBoxMinY= parseInt(box[1])
|
||||
viewBoxMinY = parseInt(box[1])
|
||||
viewBoxWidth = parseInt(box[2])
|
||||
viewBoxHeight = parseInt(box[3])
|
||||
|
||||
|
|
|
@ -27,6 +27,17 @@ type
|
|||
color*: ColorRGBX ## Color of the stop
|
||||
position*: float32 ## Gradient Stop position 0..1.
|
||||
|
||||
SomePaint* = string | Paint | SomeColor
|
||||
|
||||
converter parseSomePaint*(paint: SomePaint): Paint {.inline.} =
|
||||
## Given SomePaint, parse it in different ways.
|
||||
when type(paint) is string:
|
||||
Paint(kind: pkSolid, color: parseHtmlColor(paint).rgba())
|
||||
elif type(paint) is SomeColor:
|
||||
Paint(kind: pkSolid, color: paint.rgba())
|
||||
elif type(paint) is Paint:
|
||||
paint
|
||||
|
||||
proc toLineSpace(at, to, point: Vec2): float32 {.inline.} =
|
||||
## Convert position on to where it would fall on a line between at and to.
|
||||
let
|
||||
|
|
|
@ -360,64 +360,66 @@ proc quadraticCurveTo*(path: var Path, ctrl, to: Vec2) {.inline.} =
|
|||
##
|
||||
path.quadraticCurveTo(ctrl.x, ctrl.y, to.x, to.y)
|
||||
|
||||
proc arcTo*(path: var Path, ctrl1, ctrl2: Vec2, radius: float32) {.inline.} =
|
||||
## Adds a circular arc to the current sub-path, using the given control
|
||||
## points and radius.
|
||||
# proc arcTo*(path: var Path, ctrl1, ctrl2: Vec2, radius: float32) {.inline.} =
|
||||
# ## Adds a circular arc to the current sub-path, using the given control
|
||||
# ## points and radius.
|
||||
|
||||
const epsilon = 1e-6.float32
|
||||
# const epsilon = 1e-6.float32
|
||||
|
||||
var radius = radius
|
||||
if radius < 0:
|
||||
radius = -radius
|
||||
# var radius = radius
|
||||
# if radius < 0:
|
||||
# radius = -radius
|
||||
|
||||
if path.commands.len == 0:
|
||||
path.moveTo(ctrl1)
|
||||
# if path.commands.len == 0:
|
||||
# path.moveTo(ctrl1)
|
||||
|
||||
let
|
||||
a = path.at - ctrl1
|
||||
b = ctrl2 - ctrl1
|
||||
# let
|
||||
# a = path.at - ctrl1
|
||||
# b = ctrl2 - ctrl1
|
||||
|
||||
if a.lengthSq() < epsilon:
|
||||
# If the control point is coincident with at, do nothing
|
||||
discard
|
||||
elif abs(a.y * b.x - a.x * b.y) < epsilon or radius == 0:
|
||||
# If ctrl1, a and b are colinear or coincident or radius is zero
|
||||
path.lineTo(ctrl1)
|
||||
else:
|
||||
let
|
||||
c = ctrl2 - path.at
|
||||
als = a.lengthSq()
|
||||
bls = b.lengthSq()
|
||||
cls = c.lengthSq()
|
||||
al = a.length()
|
||||
bl = b.length()
|
||||
l = radius * tan((PI - arccos((als + bls - cls) / 2 * al * bl)) / 2)
|
||||
ta = l / al
|
||||
tb = l / bl
|
||||
# if a.lengthSq() < epsilon:
|
||||
# # If the control point is coincident with at, do nothing
|
||||
# discard
|
||||
# elif abs(a.y * b.x - a.x * b.y) < epsilon or radius == 0:
|
||||
# # If ctrl1, a and b are colinear or coincident or radius is zero
|
||||
# path.lineTo(ctrl1)
|
||||
# else:
|
||||
# let
|
||||
# c = ctrl2 - path.at
|
||||
# als = a.lengthSq()
|
||||
# bls = b.lengthSq()
|
||||
# cls = c.lengthSq()
|
||||
# al = a.length()
|
||||
# bl = b.length()
|
||||
# l = radius * tan((PI - arccos((als + bls - cls) / 2 * al * bl)) / 2)
|
||||
# ta = l / al
|
||||
# tb = l / bl
|
||||
|
||||
if abs(ta - 1) > epsilon:
|
||||
# If the start tangent is not coincident with path.at
|
||||
path.lineTo(ctrl1 + a * ta)
|
||||
# if abs(ta - 1) > epsilon:
|
||||
# # If the start tangent is not coincident with path.at
|
||||
# path.lineTo(ctrl1 + a * ta)
|
||||
|
||||
let to = ctrl1 + b * tb
|
||||
path.commands.add(PathCommand(
|
||||
kind: Arc,
|
||||
numbers: @[
|
||||
radius,
|
||||
radius,
|
||||
0,
|
||||
0,
|
||||
if a.y * c.x > a.x * c.y: 1 else: 0,
|
||||
to.x,
|
||||
to.y
|
||||
]
|
||||
))
|
||||
path.at = to
|
||||
# echo "INSIDE ", (als + bls - cls) / 2 * al * bl, " ", arccos((als + bls - cls) / 2 * al * bl)
|
||||
|
||||
proc arcTo*(path: var Path, x1, y1, x2, y2, radius: float32) {.inline.} =
|
||||
## Adds a circular arc to the current sub-path, using the given control
|
||||
## points and radius.
|
||||
path.arcTo(vec2(x1, y1), vec2(x2, y2), radius)
|
||||
# let to = ctrl1 + b * tb
|
||||
# path.commands.add(PathCommand(
|
||||
# kind: Arc,
|
||||
# numbers: @[
|
||||
# radius,
|
||||
# radius,
|
||||
# 0,
|
||||
# 0,
|
||||
# if a.y * c.x > a.x * c.y: 1 else: 0,
|
||||
# to.x,
|
||||
# to.y
|
||||
# ]
|
||||
# ))
|
||||
# path.at = to
|
||||
|
||||
# proc arcTo*(path: var Path, x1, y1, x2, y2, radius: float32) {.inline.} =
|
||||
# ## Adds a circular arc to the current sub-path, using the given control
|
||||
# ## points and radius.
|
||||
# path.arcTo(vec2(x1, y1), vec2(x2, y2), radius)
|
||||
|
||||
proc ellipticalArcTo*(
|
||||
path: var Path,
|
||||
|
@ -1499,7 +1501,7 @@ proc fillPath*(
|
|||
) =
|
||||
## Fills a path.
|
||||
if paint.kind == pkSolid:
|
||||
image.fillPath(path, paint.color, transform)
|
||||
image.fillPath(path, paint.color, transform, windingRule)
|
||||
return
|
||||
|
||||
let
|
||||
|
|
BIN
tests/images/context/beginPath_1.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/images/context/bezierCurveTo_1.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
tests/images/context/bezierCurveTo_2.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
tests/images/context/clearRect_1.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
tests/images/context/closePath_1.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
tests/images/context/ellipse_1.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
tests/images/context/fillText_1.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
tests/images/context/fill_1.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
tests/images/context/moveTo_1.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/images/context/quadracticCurveTo_1.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
tests/images/context/quadracticCurveTo_2.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
tests/images/context/resetTransform_1.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
tests/images/context/resetTransform_2.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
tests/images/context/rotate_1.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
tests/images/context/save_1.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
tests/images/context/scale_1.png
Normal file
After Width: | Height: | Size: 956 B |
BIN
tests/images/context/setTransform_1.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
tests/images/context/strokeRect_1.png
Normal file
After Width: | Height: | Size: 1,008 B |
BIN
tests/images/context/strokeRect_2.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
tests/images/context/strokeText_1.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
tests/images/context/stroke_1.png
Normal file
After Width: | Height: | Size: 987 B |
BIN
tests/images/context/stroke_2.png
Normal file
After Width: | Height: | Size: 968 B |
BIN
tests/images/context/stroke_3.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
tests/images/context/translate_1.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
312
tests/test_context.nim
Normal file
|
@ -0,0 +1,312 @@
|
|||
import chroma, pixie
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 160))
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.fillStyle = "#ff6"
|
||||
ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32)
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.fillStyle = "blue"
|
||||
ctx.moveTo(20, 20)
|
||||
ctx.lineTo(180, 20)
|
||||
ctx.lineTo(130, 130)
|
||||
ctx.closePath()
|
||||
ctx.fill()
|
||||
|
||||
ctx.clearRect(10, 10, 120, 100)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/clearRect_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.strokeStyle = "blue"
|
||||
ctx.moveTo(20, 20)
|
||||
ctx.lineTo(200, 20)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.strokeStyle = "green"
|
||||
ctx.moveTo(20, 20)
|
||||
ctx.lineTo(120, 120)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/beginPath_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(50, 50)
|
||||
ctx.lineTo(200, 50)
|
||||
ctx.moveTo(50, 90)
|
||||
ctx.lineTo(280, 120)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/moveTo_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
var region: Path
|
||||
region.moveTo(30, 90)
|
||||
region.lineTo(110, 20)
|
||||
region.lineTo(240, 130)
|
||||
region.lineTo(60, 130)
|
||||
region.lineTo(190, 20)
|
||||
region.lineTo(270, 90)
|
||||
region.closePath()
|
||||
|
||||
ctx.fillStyle = "green"
|
||||
ctx.fill(region, wrEvenOdd)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/fill_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.rect(10, 10, 150, 100)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/stroke_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.lineWidth = 26
|
||||
ctx.strokeStyle = "orange"
|
||||
ctx.moveTo(20, 20)
|
||||
ctx.lineTo(160, 20)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.lineWidth = 14
|
||||
ctx.strokeStyle = "green"
|
||||
ctx.moveTo(20, 80)
|
||||
ctx.lineTo(220, 80)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.lineWidth = 4
|
||||
ctx.strokeStyle = "pink"
|
||||
ctx.moveTo(20, 140)
|
||||
ctx.lineTo(280, 140)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/stroke_2.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.lineWidth = 26
|
||||
ctx.strokeStyle = "red"
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.rect(25, 25, 100, 100)
|
||||
ctx.fill()
|
||||
ctx.stroke()
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.rect(175, 25, 100, 100)
|
||||
ctx.stroke()
|
||||
ctx.fill()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/stroke_3.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(20, 140)
|
||||
ctx.lineTo(120, 10)
|
||||
ctx.lineTo(220, 140)
|
||||
ctx.closePath()
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/closePath_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
let
|
||||
start = vec2(50, 20)
|
||||
cp1 = vec2(230, 30)
|
||||
cp2 = vec2(150, 80)
|
||||
to = vec2(250, 100)
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(start)
|
||||
ctx.bezierCurveTo(cp1, cp2, to)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/bezierCurveTo_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(30, 30)
|
||||
ctx.bezierCurveTo(120, 160, 180, 10, 220, 140)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/bezierCurveTo_2.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.moveTo(50, 20)
|
||||
ctx.quadraticCurveTo(230, 30, 50, 100)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/quadracticCurveTo_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(20, 110)
|
||||
ctx.quadraticCurveTo(230, 150, 250, 20)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/quadracticCurveTo_2.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.ellipse(100, 75, 75, 50)
|
||||
ctx.stroke()
|
||||
|
||||
ctx.image.writeFile("tests/images/context/ellipse_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.strokeStyle = "green"
|
||||
ctx.strokeRect(20, 10, 160, 100)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/strokeRect_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.lineJoin = ljBevel
|
||||
ctx.lineWidth = 15
|
||||
ctx.strokeStyle = "#38f"
|
||||
ctx.strokeRect(30, 30, 160, 90)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/strokeRect_2.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.setTransform(1, 0.2, 0.8, 1, 0, 0)
|
||||
ctx.fillRect(0, 0, 100, 100)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/setTransform_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.setTransform(1, 0.2, 0.8, 1, 0, 0)
|
||||
ctx.fillRect(0, 0, 100, 100)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/resetTransform_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.rotate(45 * PI / 180)
|
||||
ctx.fillRect(60, 0, 100, 30)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/resetTransform_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.transform(1, 0, 1.7, 1, 0, 0)
|
||||
ctx.fillStyle = "gray"
|
||||
ctx.fillRect(40, 40, 50, 20)
|
||||
ctx.fillRect(40, 90, 50, 20)
|
||||
|
||||
ctx.resetTransform()
|
||||
ctx.fillStyle = "red"
|
||||
ctx.fillRect(40, 40, 50, 20)
|
||||
ctx.fillRect(40, 90, 50, 20)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/resetTransform_2.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.translate(110, 30)
|
||||
ctx.fillStyle = "red"
|
||||
ctx.fillRect(0, 0, 80, 80)
|
||||
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0)
|
||||
|
||||
ctx.fillStyle = "gray"
|
||||
ctx.fillRect(0, 0, 80, 80)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/translate_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.scale(9, 3)
|
||||
ctx.fillStyle = "red"
|
||||
ctx.fillRect(10, 10, 8, 20)
|
||||
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0)
|
||||
|
||||
ctx.fillStyle = "gray"
|
||||
ctx.fillRect(10, 10, 8, 20)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/scale_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.fillStyle = "gray"
|
||||
ctx.fillRect(100, 0, 80, 20)
|
||||
|
||||
ctx.rotate(45 * PI / 180)
|
||||
ctx.fillStyle = "red"
|
||||
ctx.fillRect(100, 0, 80, 20)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/rotate_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.font = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
||||
ctx.font.size = 50
|
||||
|
||||
ctx.fillText("Hello world", 50, 90)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/fillText_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.font = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
||||
ctx.font.size = 50
|
||||
|
||||
ctx.strokeText("Hello world", 50, 90)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/strokeText_1.png")
|
||||
|
||||
block:
|
||||
let ctx = newContext(newImage(300, 150))
|
||||
|
||||
ctx.save()
|
||||
|
||||
ctx.fillStyle = "green"
|
||||
ctx.fillRect(10, 10, 100, 100)
|
||||
|
||||
ctx.restore()
|
||||
|
||||
ctx.fillRect(150, 40, 100, 100)
|
||||
|
||||
ctx.image.writeFile("tests/images/context/save_1.png")
|
|
@ -13,27 +13,27 @@ block:
|
|||
mask.invert()
|
||||
doAssert mask[0, 0] == 55
|
||||
|
||||
block:
|
||||
let
|
||||
mask = newMask(100, 100)
|
||||
r = 10.0
|
||||
x = 10.0
|
||||
y = 10.0
|
||||
h = 80.0
|
||||
w = 80.0
|
||||
var path: Path
|
||||
path.moveTo(x + r, y)
|
||||
path.arcTo(x + w, y, x + w, y + h, r)
|
||||
path.arcTo(x + w, y + h, x, y + h, r)
|
||||
path.arcTo(x, y + h, x, y, r)
|
||||
path.arcTo(x, y, x + w, y, r)
|
||||
mask.fillPath(path)
|
||||
# block:
|
||||
# let
|
||||
# mask = newMask(100, 100)
|
||||
# r = 10.0
|
||||
# x = 10.0
|
||||
# y = 10.0
|
||||
# h = 80.0
|
||||
# w = 80.0
|
||||
# var path: Path
|
||||
# path.moveTo(x + r, y)
|
||||
# path.arcTo(x + w, y, x + w, y + h, r)
|
||||
# path.arcTo(x + w, y + h, x, y + h, r)
|
||||
# path.arcTo(x, y + h, x, y, r)
|
||||
# path.arcTo(x, y, x + w, y, r)
|
||||
# mask.fillPath(path)
|
||||
|
||||
let minified = mask.minifyBy2()
|
||||
# let minified = mask.minifyBy2()
|
||||
|
||||
doAssert minified.width == 50 and minified.height == 50
|
||||
# doAssert minified.width == 50 and minified.height == 50
|
||||
|
||||
writeFile("tests/images/masks/maskMinified.png", minified.encodePng())
|
||||
# writeFile("tests/images/masks/maskMinified.png", minified.encodePng())
|
||||
|
||||
block:
|
||||
let image = newImage(100, 100)
|
||||
|
|
|
@ -149,22 +149,22 @@ block:
|
|||
)
|
||||
image.writeFile("tests/images/paths/pathCornerArc.png")
|
||||
|
||||
block:
|
||||
let
|
||||
image = newImage(100, 100)
|
||||
r = 10.0
|
||||
x = 10.0
|
||||
y = 10.0
|
||||
h = 80.0
|
||||
w = 80.0
|
||||
var path: Path
|
||||
path.moveTo(x + r, y)
|
||||
path.arcTo(x + w, y, x + w, y + h, r)
|
||||
path.arcTo(x + w, y + h, x, y + h, r)
|
||||
path.arcTo(x, y + h, x, y, r)
|
||||
path.arcTo(x, y, x + w, y, r)
|
||||
image.fillPath(path, rgba(255, 0, 0, 255))
|
||||
image.writeFile("tests/images/paths/pathRoundRect.png")
|
||||
# block:
|
||||
# let
|
||||
# image = newImage(100, 100)
|
||||
# r = 10.0
|
||||
# x = 10.0
|
||||
# y = 10.0
|
||||
# h = 80.0
|
||||
# w = 80.0
|
||||
# var path: Path
|
||||
# path.moveTo(x + r, y)
|
||||
# path.arcTo(x + w, y, x + w, y + h, r)
|
||||
# path.arcTo(x + w, y + h, x, y + h, r)
|
||||
# path.arcTo(x, y + h, x, y, r)
|
||||
# path.arcTo(x, y, x + w, y, r)
|
||||
# image.fillPath(path, rgba(255, 0, 0, 255))
|
||||
# image.writeFile("tests/images/paths/pathRoundRect.png")
|
||||
|
||||
block:
|
||||
let
|
||||
|
@ -173,22 +173,22 @@ block:
|
|||
mask.fillPath(pathStr)
|
||||
writeFile("tests/images/paths/pathRectangleMask.png", mask.encodePng())
|
||||
|
||||
block:
|
||||
let
|
||||
mask = newMask(100, 100)
|
||||
r = 10.0
|
||||
x = 10.0
|
||||
y = 10.0
|
||||
h = 80.0
|
||||
w = 80.0
|
||||
var path: Path
|
||||
path.moveTo(x + r, y)
|
||||
path.arcTo(x + w, y, x + w, y + h, r)
|
||||
path.arcTo(x + w, y + h, x, y + h, r)
|
||||
path.arcTo(x, y + h, x, y, r)
|
||||
path.arcTo(x, y, x + w, y, r)
|
||||
mask.fillPath(path)
|
||||
writeFile("tests/images/paths/pathRoundRectMask.png", mask.encodePng())
|
||||
# block:
|
||||
# let
|
||||
# mask = newMask(100, 100)
|
||||
# r = 10.0
|
||||
# x = 10.0
|
||||
# y = 10.0
|
||||
# h = 80.0
|
||||
# w = 80.0
|
||||
# var path: Path
|
||||
# path.moveTo(x + r, y)
|
||||
# path.arcTo(x + w, y, x + w, y + h, r)
|
||||
# path.arcTo(x + w, y + h, x, y + h, r)
|
||||
# path.arcTo(x, y + h, x, y, r)
|
||||
# path.arcTo(x, y, x + w, y, r)
|
||||
# mask.fillPath(path)
|
||||
# writeFile("tests/images/paths/pathRoundRectMask.png", mask.encodePng())
|
||||
|
||||
block:
|
||||
let image = newImage(200, 200)
|
||||
|
|