commit
2edcfac64f
8 changed files with 285 additions and 134 deletions
|
@ -18,6 +18,7 @@ type
|
||||||
path: Path
|
path: Path
|
||||||
mat: Mat3
|
mat: Mat3
|
||||||
mask: Mask
|
mask: Mask
|
||||||
|
layer: Image
|
||||||
stateStack: seq[ContextState]
|
stateStack: seq[ContextState]
|
||||||
|
|
||||||
ContextState = object
|
ContextState = object
|
||||||
|
@ -29,6 +30,10 @@ type
|
||||||
textAlign: HAlignMode
|
textAlign: HAlignMode
|
||||||
mat: Mat3
|
mat: Mat3
|
||||||
mask: Mask
|
mask: Mask
|
||||||
|
layer: Image
|
||||||
|
|
||||||
|
TextMetrics* = object
|
||||||
|
width*: float32
|
||||||
|
|
||||||
proc newContext*(image: Image): Context =
|
proc newContext*(image: Image): Context =
|
||||||
## Create a new Context that will draw to the parameter image.
|
## Create a new Context that will draw to the parameter image.
|
||||||
|
@ -43,6 +48,116 @@ proc newContext*(width, height: int): Context {.inline.} =
|
||||||
## Create a new Context that will draw to a new image of width and height.
|
## Create a new Context that will draw to a new image of width and height.
|
||||||
newContext(newImage(width, height))
|
newContext(newImage(width, height))
|
||||||
|
|
||||||
|
proc state(ctx: Context): ContextState =
|
||||||
|
result.fillStyle = ctx.fillStyle
|
||||||
|
result.strokeStyle = ctx.strokeStyle
|
||||||
|
result.lineWidth = ctx.lineWidth
|
||||||
|
result.lineCap = ctx.lineCap
|
||||||
|
result.lineJoin = ctx.lineJoin
|
||||||
|
result.font = ctx.font
|
||||||
|
result.textAlign = ctx.textAlign
|
||||||
|
result.mat = ctx.mat
|
||||||
|
result.mask = if ctx.mask != nil: ctx.mask.copy() else: nil
|
||||||
|
|
||||||
|
proc save*(ctx: Context) {.inline.} =
|
||||||
|
## Saves the entire state of the canvas by pushing the current state onto
|
||||||
|
## a stack.
|
||||||
|
ctx.stateStack.add(ctx.state())
|
||||||
|
|
||||||
|
proc saveLayer*(ctx: Context) =
|
||||||
|
var state = ctx.state()
|
||||||
|
state.layer = ctx.layer
|
||||||
|
ctx.stateStack.add(state)
|
||||||
|
ctx.layer = newImage(ctx.image.width, ctx.image.height)
|
||||||
|
|
||||||
|
proc restore*(ctx: Context) =
|
||||||
|
## 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.
|
||||||
|
if ctx.stateStack.len == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
let
|
||||||
|
poppedLayer = ctx.layer
|
||||||
|
poppedMask = ctx.mask
|
||||||
|
|
||||||
|
let state = ctx.stateStack.pop()
|
||||||
|
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
|
||||||
|
ctx.mat = state.mat
|
||||||
|
ctx.mask = state.mask
|
||||||
|
ctx.layer = state.layer
|
||||||
|
|
||||||
|
if poppedLayer != nil: # If there is a layer being popped
|
||||||
|
if poppedMask != nil: # If there is a mask, apply it
|
||||||
|
poppedLayer.draw(poppedMask)
|
||||||
|
if ctx.layer != nil: # If we popped to another layer, draw to it
|
||||||
|
ctx.layer.draw(poppedLayer)
|
||||||
|
else: # Otherwise draw to the root image
|
||||||
|
ctx.image.draw(poppedLayer)
|
||||||
|
|
||||||
|
proc fill(
|
||||||
|
ctx: Context, image: Image, path: Path, windingRule: WindingRule
|
||||||
|
) {.inline.} =
|
||||||
|
image.fillPath(
|
||||||
|
path,
|
||||||
|
ctx.fillStyle,
|
||||||
|
ctx.mat,
|
||||||
|
windingRule
|
||||||
|
)
|
||||||
|
|
||||||
|
proc stroke(ctx: Context, image: Image, path: Path) {.inline.} =
|
||||||
|
image.strokePath(
|
||||||
|
path,
|
||||||
|
ctx.strokeStyle,
|
||||||
|
ctx.mat,
|
||||||
|
ctx.lineWidth,
|
||||||
|
ctx.lineCap,
|
||||||
|
ctx.lineJoin
|
||||||
|
)
|
||||||
|
|
||||||
|
proc fillText(ctx: Context, image: Image, text: string, at: Vec2) {.inline.} =
|
||||||
|
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
|
||||||
|
|
||||||
|
image.fillText(
|
||||||
|
ctx.font,
|
||||||
|
text,
|
||||||
|
ctx.mat * translate(at),
|
||||||
|
hAlign = ctx.textAlign
|
||||||
|
)
|
||||||
|
|
||||||
|
proc strokeText(ctx: Context, image: Image, text: string, at: Vec2) {.inline.} =
|
||||||
|
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
|
||||||
|
|
||||||
|
image.strokeText(
|
||||||
|
ctx.font,
|
||||||
|
text,
|
||||||
|
ctx.mat * translate(at),
|
||||||
|
ctx.lineWidth,
|
||||||
|
hAlign = ctx.textAlign,
|
||||||
|
lineCap = ctx.lineCap,
|
||||||
|
lineJoin = ctx.lineJoin
|
||||||
|
)
|
||||||
|
|
||||||
proc beginPath*(ctx: Context) {.inline.} =
|
proc beginPath*(ctx: Context) {.inline.} =
|
||||||
## Starts a new path by emptying the list of sub-paths.
|
## Starts a new path by emptying the list of sub-paths.
|
||||||
ctx.path = Path()
|
ctx.path = Path()
|
||||||
|
@ -121,23 +236,14 @@ proc ellipse*(ctx: Context, x, y, rx, ry: float32) {.inline.} =
|
||||||
|
|
||||||
proc fill*(ctx: Context, path: Path, windingRule = wrNonZero) {.inline.} =
|
proc fill*(ctx: Context, path: Path, windingRule = wrNonZero) {.inline.} =
|
||||||
## Fills the path with the current fillStyle.
|
## Fills the path with the current fillStyle.
|
||||||
if ctx.mask != nil:
|
if ctx.mask != nil and ctx.layer == nil:
|
||||||
let tmp = newImage(ctx.image.width, ctx.image.height)
|
ctx.saveLayer()
|
||||||
tmp.fillPath(
|
ctx.fill(ctx.layer, path, windingRule)
|
||||||
path,
|
ctx.restore()
|
||||||
ctx.fillStyle,
|
elif ctx.layer != nil:
|
||||||
ctx.mat,
|
ctx.fill(ctx.layer, path, windingRule)
|
||||||
windingRule
|
|
||||||
)
|
|
||||||
tmp.draw(ctx.mask)
|
|
||||||
ctx.image.draw(tmp)
|
|
||||||
else:
|
else:
|
||||||
ctx.image.fillPath(
|
ctx.fill(ctx.image, path, windingRule)
|
||||||
path,
|
|
||||||
ctx.fillStyle,
|
|
||||||
ctx.mat,
|
|
||||||
windingRule
|
|
||||||
)
|
|
||||||
|
|
||||||
proc fill*(ctx: Context, windingRule = wrNonZero) {.inline.} =
|
proc fill*(ctx: Context, windingRule = wrNonZero) {.inline.} =
|
||||||
## Fills the current path with the current fillStyle.
|
## Fills the current path with the current fillStyle.
|
||||||
|
@ -163,27 +269,14 @@ proc clip*(ctx: Context, windingRule = wrNonZero) {.inline.} =
|
||||||
|
|
||||||
proc stroke*(ctx: Context, path: Path) {.inline.} =
|
proc stroke*(ctx: Context, path: Path) {.inline.} =
|
||||||
## Strokes (outlines) the current or given path with the current strokeStyle.
|
## Strokes (outlines) the current or given path with the current strokeStyle.
|
||||||
if ctx.mask != nil:
|
if ctx.mask != nil and ctx.layer == nil:
|
||||||
let tmp = newImage(ctx.image.width, ctx.image.height)
|
ctx.saveLayer()
|
||||||
tmp.strokePath(
|
ctx.stroke(ctx.layer, path)
|
||||||
path,
|
ctx.restore()
|
||||||
ctx.strokeStyle,
|
elif ctx.layer != nil:
|
||||||
ctx.mat,
|
ctx.stroke(ctx.layer, path)
|
||||||
ctx.lineWidth,
|
|
||||||
ctx.lineCap,
|
|
||||||
ctx.lineJoin
|
|
||||||
)
|
|
||||||
tmp.draw(ctx.mask)
|
|
||||||
ctx.image.draw(tmp)
|
|
||||||
else:
|
else:
|
||||||
ctx.image.strokePath(
|
ctx.stroke(ctx.image, path)
|
||||||
path,
|
|
||||||
ctx.strokeStyle,
|
|
||||||
ctx.mat,
|
|
||||||
ctx.lineWidth,
|
|
||||||
ctx.lineCap,
|
|
||||||
ctx.lineJoin
|
|
||||||
)
|
|
||||||
|
|
||||||
proc stroke*(ctx: Context) {.inline.} =
|
proc stroke*(ctx: Context) {.inline.} =
|
||||||
## Strokes (outlines) the current or given path with the current strokeStyle.
|
## Strokes (outlines) the current or given path with the current strokeStyle.
|
||||||
|
@ -193,11 +286,18 @@ proc clearRect*(ctx: Context, rect: Rect) =
|
||||||
## Erases the pixels in a rectangular area.
|
## Erases the pixels in a rectangular area.
|
||||||
var path: Path
|
var path: Path
|
||||||
path.rect(rect)
|
path.rect(rect)
|
||||||
ctx.image.fillPath(
|
if ctx.layer != nil:
|
||||||
path,
|
ctx.layer.fillPath(
|
||||||
Paint(kind: pkSolid, color:rgbx(0, 0, 0, 0), blendMode: bmOverwrite),
|
path,
|
||||||
ctx.mat
|
Paint(kind: pkSolid, color:rgbx(0, 0, 0, 0), blendMode: bmOverwrite),
|
||||||
)
|
ctx.mat
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ctx.image.fillPath(
|
||||||
|
path,
|
||||||
|
Paint(kind: pkSolid, color:rgbx(0, 0, 0, 0), blendMode: bmOverwrite),
|
||||||
|
ctx.mat
|
||||||
|
)
|
||||||
|
|
||||||
proc clearRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
|
proc clearRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
|
||||||
## Erases the pixels in a rectangular area.
|
## Erases the pixels in a rectangular area.
|
||||||
|
@ -228,33 +328,14 @@ proc strokeRect*(ctx: Context, x, y, width, height: float32) {.inline.} =
|
||||||
proc fillText*(ctx: Context, text: string, at: Vec2) =
|
proc fillText*(ctx: Context, text: string, at: Vec2) =
|
||||||
## Draws a text string at the specified coordinates, filling the string's
|
## Draws a text string at the specified coordinates, filling the string's
|
||||||
## characters with the current fillStyle
|
## characters with the current fillStyle
|
||||||
|
if ctx.mask != nil and ctx.layer == nil:
|
||||||
if ctx.font.typeface == nil:
|
ctx.saveLayer()
|
||||||
raise newException(PixieError, "No font has been set on this Context")
|
ctx.fillText(ctx.layer, text, at)
|
||||||
|
ctx.restore()
|
||||||
# Canvas positions text relative to the alphabetic baseline by default
|
elif ctx.layer != nil:
|
||||||
var at = at
|
ctx.fillText(ctx.layer, text, at)
|
||||||
at.y -= round(ctx.font.typeface.ascent * ctx.font.scale)
|
|
||||||
|
|
||||||
ctx.font.paint = ctx.fillStyle
|
|
||||||
|
|
||||||
if ctx.mask != nil:
|
|
||||||
let tmp = newImage(ctx.image.width, ctx.image.height)
|
|
||||||
tmp.fillText(
|
|
||||||
ctx.font,
|
|
||||||
text,
|
|
||||||
ctx.mat * translate(at),
|
|
||||||
hAlign = ctx.textAlign
|
|
||||||
)
|
|
||||||
tmp.draw(ctx.mask)
|
|
||||||
ctx.image.draw(tmp)
|
|
||||||
else:
|
else:
|
||||||
ctx.image.fillText(
|
ctx.fillText(ctx.image, text, at)
|
||||||
ctx.font,
|
|
||||||
text,
|
|
||||||
ctx.mat * translate(at),
|
|
||||||
hAlign = ctx.textAlign
|
|
||||||
)
|
|
||||||
|
|
||||||
proc fillText*(ctx: Context, text: string, x, y: float32) {.inline.} =
|
proc fillText*(ctx: Context, text: string, x, y: float32) {.inline.} =
|
||||||
## Draws the outlines of the characters of a text string at the specified
|
## Draws the outlines of the characters of a text string at the specified
|
||||||
|
@ -264,45 +345,29 @@ proc fillText*(ctx: Context, text: string, x, y: float32) {.inline.} =
|
||||||
proc strokeText*(ctx: Context, text: string, at: Vec2) =
|
proc strokeText*(ctx: Context, text: string, at: Vec2) =
|
||||||
## Draws the outlines of the characters of a text string at the specified
|
## Draws the outlines of the characters of a text string at the specified
|
||||||
## coordinates.
|
## coordinates.
|
||||||
|
if ctx.mask != nil and ctx.layer == nil:
|
||||||
if ctx.font.typeface == nil:
|
ctx.saveLayer()
|
||||||
raise newException(PixieError, "No font has been set on this Context")
|
ctx.strokeText(ctx.layer, text, at)
|
||||||
|
ctx.restore()
|
||||||
# Canvas positions text relative to the alphabetic baseline by default
|
elif ctx.layer != nil:
|
||||||
var at = at
|
ctx.strokeText(ctx.layer, text, at)
|
||||||
at.y -= round(ctx.font.typeface.ascent * ctx.font.scale)
|
|
||||||
|
|
||||||
ctx.font.paint = ctx.strokeStyle
|
|
||||||
|
|
||||||
if ctx.mask != nil:
|
|
||||||
let tmp = newImage(ctx.image.width, ctx.image.height)
|
|
||||||
tmp.strokeText(
|
|
||||||
ctx.font,
|
|
||||||
text,
|
|
||||||
ctx.mat * translate(at),
|
|
||||||
ctx.lineWidth,
|
|
||||||
hAlign = ctx.textAlign,
|
|
||||||
lineCap = ctx.lineCap,
|
|
||||||
lineJoin = ctx.lineJoin
|
|
||||||
)
|
|
||||||
tmp.draw(ctx.mask)
|
|
||||||
ctx.image.draw(tmp)
|
|
||||||
else:
|
else:
|
||||||
ctx.image.strokeText(
|
ctx.strokeText(ctx.image, text, at)
|
||||||
ctx.font,
|
|
||||||
text,
|
|
||||||
ctx.mat * translate(at),
|
|
||||||
ctx.lineWidth,
|
|
||||||
hAlign = ctx.textAlign,
|
|
||||||
lineCap = ctx.lineCap,
|
|
||||||
lineJoin = ctx.lineJoin
|
|
||||||
)
|
|
||||||
|
|
||||||
proc strokeText*(ctx: Context, text: string, x, y: float32) {.inline.} =
|
proc strokeText*(ctx: Context, text: string, x, y: float32) {.inline.} =
|
||||||
## Draws the outlines of the characters of a text string at the specified
|
## Draws the outlines of the characters of a text string at the specified
|
||||||
## coordinates.
|
## coordinates.
|
||||||
ctx.strokeText(text, vec2(x, y))
|
ctx.strokeText(text, vec2(x, y))
|
||||||
|
|
||||||
|
proc measureText*(ctx: Context, text: string): TextMetrics =
|
||||||
|
## Returns a TextMetrics object that contains information about the measured
|
||||||
|
## text (such as its width, for example).
|
||||||
|
if ctx.font.typeface == nil:
|
||||||
|
raise newException(PixieError, "No font has been set on this Context")
|
||||||
|
|
||||||
|
let bounds = typeset(ctx.font, text).computeBounds()
|
||||||
|
result.width = bounds.x
|
||||||
|
|
||||||
proc getTransform*(ctx: Context): Mat3 {.inline.} =
|
proc getTransform*(ctx: Context): Mat3 {.inline.} =
|
||||||
## Retrieves the current transform matrix being applied to the context.
|
## Retrieves the current transform matrix being applied to the context.
|
||||||
ctx.mat
|
ctx.mat
|
||||||
|
@ -351,37 +416,6 @@ proc resetTransform*(ctx: Context) {.inline.} =
|
||||||
## Resets the current transform to the identity matrix.
|
## Resets the current transform to the identity matrix.
|
||||||
ctx.mat = mat3()
|
ctx.mat = mat3()
|
||||||
|
|
||||||
proc save*(ctx: Context) =
|
|
||||||
## Saves the entire state of the canvas by pushing the current state onto
|
|
||||||
## a stack.
|
|
||||||
var state: ContextState
|
|
||||||
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
|
|
||||||
state.mat = ctx.mat
|
|
||||||
state.mask = if ctx.mask != nil: ctx.mask.copy() else: nil
|
|
||||||
ctx.stateStack.add(state)
|
|
||||||
|
|
||||||
proc restore*(ctx: Context) =
|
|
||||||
## 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.
|
|
||||||
if ctx.stateStack.len > 0:
|
|
||||||
let state = ctx.stateStack.pop()
|
|
||||||
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
|
|
||||||
ctx.mat = state.mat
|
|
||||||
ctx.mask = state.mask
|
|
||||||
|
|
||||||
# Additional procs that are not part of the JS API
|
# Additional procs that are not part of the JS API
|
||||||
|
|
||||||
proc roundedRect*(ctx: Context, x, y, w, h, nw, ne, se, sw: float32) {.inline.} =
|
proc roundedRect*(ctx: Context, x, y, w, h, nw, ne, se, sw: float32) {.inline.} =
|
||||||
|
|
|
@ -303,6 +303,13 @@ type
|
||||||
# featureList: FeatureList
|
# featureList: FeatureList
|
||||||
lookupList: LookupList
|
lookupList: LookupList
|
||||||
|
|
||||||
|
PostTable = ref object
|
||||||
|
version: float32
|
||||||
|
italicAngle: float32
|
||||||
|
underlinePosition: int16
|
||||||
|
underlineThickness: int16
|
||||||
|
isFixedPitch: uint32
|
||||||
|
|
||||||
OpenType* = ref object
|
OpenType* = ref object
|
||||||
buf*: string
|
buf*: string
|
||||||
version*: uint32
|
version*: uint32
|
||||||
|
@ -322,6 +329,7 @@ type
|
||||||
glyf*: GlyfTable
|
glyf*: GlyfTable
|
||||||
kern*: KernTable
|
kern*: KernTable
|
||||||
gpos*: GposTable
|
gpos*: GposTable
|
||||||
|
post*: PostTable
|
||||||
glyphPaths: Table[Rune, Path]
|
glyphPaths: Table[Rune, Path]
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
|
@ -1194,6 +1202,16 @@ proc parseGposTable(buf: string, offset: int): GPOSTable =
|
||||||
result.lookupList =
|
result.lookupList =
|
||||||
parseLookupList(buf, offset + result.lookupListOffset.int, result)
|
parseLookupList(buf, offset + result.lookupListOffset.int, result)
|
||||||
|
|
||||||
|
proc parsePostTable(buf: string, offset: int): PostTable =
|
||||||
|
buf.eofCheck(offset + 14)
|
||||||
|
|
||||||
|
result = PostTable()
|
||||||
|
result.version = buf.readFixed32(offset + 0)
|
||||||
|
result.italicAngle = buf.readFixed32(offset + 4)
|
||||||
|
result.underlinePosition = buf.readInt16(offset + 8).swap()
|
||||||
|
result.underlineThickness = buf.readInt16(offset + 10).swap()
|
||||||
|
result.isFixedPitch = buf.readUint32(offset + 12).swap()
|
||||||
|
|
||||||
proc getGlyphId(opentype: OpenType, rune: Rune): uint16 {.inline.} =
|
proc getGlyphId(opentype: OpenType, rune: Rune): uint16 {.inline.} =
|
||||||
if rune in opentype.cmap.runeToGlyphId:
|
if rune in opentype.cmap.runeToGlyphId:
|
||||||
result = opentype.cmap.runeToGlyphId[rune]
|
result = opentype.cmap.runeToGlyphId[rune]
|
||||||
|
@ -1544,7 +1562,8 @@ proc parseOpenType*(buf: string): OpenType =
|
||||||
i += 16
|
i += 16
|
||||||
|
|
||||||
const requiredTables = [
|
const requiredTables = [
|
||||||
"cmap", "head", "hhea", "hmtx", "maxp", "name", "OS/2", "loca", "glyf"
|
"cmap", "head", "hhea", "hmtx", "maxp", "name", "OS/2", "loca", "glyf",
|
||||||
|
"post"
|
||||||
]
|
]
|
||||||
for table in requiredTables:
|
for table in requiredTables:
|
||||||
if table notin result.tableRecords:
|
if table notin result.tableRecords:
|
||||||
|
@ -1571,5 +1590,7 @@ proc parseOpenType*(buf: string): OpenType =
|
||||||
if "GPOS" in result.tableRecords:
|
if "GPOS" in result.tableRecords:
|
||||||
result.gpos = parseGposTable(buf, result.tableRecords["GPOS"].offset.int)
|
result.gpos = parseGposTable(buf, result.tableRecords["GPOS"].offset.int)
|
||||||
|
|
||||||
|
result.post = parsePostTable(buf, result.tableRecords["post"].offset.int)
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -396,10 +396,8 @@ proc blur*(
|
||||||
|
|
||||||
for xx in max(x - radius, image.width) .. x + radius:
|
for xx in max(x - radius, image.width) .. x + radius:
|
||||||
values += outOfBounds * kernel[xx - x + radius]
|
values += outOfBounds * kernel[xx - x + radius]
|
||||||
|
|
||||||
blurX.setRgbaUnsafe(y, x, rgbx(values))
|
blurX.setRgbaUnsafe(y, x, rgbx(values))
|
||||||
# this would also work (see: `limitations of method call syntax`)
|
|
||||||
# `mixin rgbx`
|
|
||||||
# blurX.setRgbaUnsafe(y, x, values.rgbx)
|
|
||||||
|
|
||||||
# Blur in the Y direction.
|
# Blur in the Y direction.
|
||||||
for y in 0 ..< image.height:
|
for y in 0 ..< image.height:
|
||||||
|
|
BIN
tests/images/context/clip_1d.png
Normal file
BIN
tests/images/context/clip_1d.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
tests/images/context/clip_1e.png
Normal file
BIN
tests/images/context/clip_1e.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 618 B |
BIN
tests/images/context/clip_1f.png
Normal file
BIN
tests/images/context/clip_1f.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
BIN
tests/images/context/clip_text.png
Normal file
BIN
tests/images/context/clip_text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
|
@ -363,6 +363,76 @@ block:
|
||||||
|
|
||||||
ctx.image.writeFile("tests/images/context/clip_1c.png")
|
ctx.image.writeFile("tests/images/context/clip_1c.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let ctx = newContext(newImage(300, 150))
|
||||||
|
|
||||||
|
ctx.fillStyle = "blue"
|
||||||
|
ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32)
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.circle(100, 75, 50)
|
||||||
|
ctx.clip()
|
||||||
|
|
||||||
|
ctx.saveLayer()
|
||||||
|
|
||||||
|
ctx.fillStyle = "red"
|
||||||
|
ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32)
|
||||||
|
ctx.fillStyle = "orange"
|
||||||
|
ctx.fillRect(0, 0, 100, 100)
|
||||||
|
|
||||||
|
ctx.restore()
|
||||||
|
|
||||||
|
ctx.image.writeFile("tests/images/context/clip_1d.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let ctx = newContext(newImage(300, 150))
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.circle(100, 75, 50)
|
||||||
|
ctx.clip()
|
||||||
|
|
||||||
|
ctx.saveLayer()
|
||||||
|
|
||||||
|
ctx.fillStyle = "red"
|
||||||
|
ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32)
|
||||||
|
ctx.fillStyle = "orange"
|
||||||
|
ctx.fillRect(0, 0, 100, 100)
|
||||||
|
|
||||||
|
ctx.restore() # Pop the layer
|
||||||
|
ctx.restore() # Pop the clip
|
||||||
|
|
||||||
|
ctx.fillStyle = "blue"
|
||||||
|
ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32)
|
||||||
|
|
||||||
|
ctx.image.writeFile("tests/images/context/clip_1e.png")
|
||||||
|
|
||||||
|
|
||||||
|
block:
|
||||||
|
let ctx = newContext(newImage(300, 150))
|
||||||
|
|
||||||
|
ctx.save()
|
||||||
|
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.circle(100, 75, 50)
|
||||||
|
ctx.clip()
|
||||||
|
|
||||||
|
ctx.saveLayer()
|
||||||
|
|
||||||
|
ctx.fillStyle = "red"
|
||||||
|
ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32)
|
||||||
|
|
||||||
|
ctx.restore()
|
||||||
|
ctx.saveLayer()
|
||||||
|
|
||||||
|
ctx.fillStyle = "orange"
|
||||||
|
ctx.fillRect(0, 0, 100, 100)
|
||||||
|
|
||||||
|
ctx.restore() # Pop the layer
|
||||||
|
|
||||||
|
ctx.image.writeFile("tests/images/context/clip_1f.png")
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let ctx = newContext(newImage(300, 150))
|
let ctx = newContext(newImage(300, 150))
|
||||||
|
|
||||||
|
@ -393,3 +463,31 @@ block:
|
||||||
ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32)
|
ctx.fillRect(0, 0, ctx.image.width.float32, ctx.image.height.float32)
|
||||||
|
|
||||||
image.writeFile("tests/images/context/clip_3.png")
|
image.writeFile("tests/images/context/clip_3.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(300, 150)
|
||||||
|
|
||||||
|
let ctx = newContext(image)
|
||||||
|
ctx.font = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
||||||
|
ctx.font.size = 50
|
||||||
|
ctx.fillStyle = "blue"
|
||||||
|
|
||||||
|
ctx.saveLayer()
|
||||||
|
|
||||||
|
var circlePath: Path
|
||||||
|
circlePath.circle(150, 75, 75)
|
||||||
|
|
||||||
|
ctx.clip(circlePath)
|
||||||
|
|
||||||
|
ctx.fillText("Hello world", 50, 90)
|
||||||
|
|
||||||
|
ctx.restore()
|
||||||
|
|
||||||
|
image.writeFile("tests/images/context/clip_text.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let ctx = newContext(100, 100)
|
||||||
|
ctx.font = readFont("tests/fonts/Roboto-Regular_1.ttf")
|
||||||
|
|
||||||
|
let metrics = ctx.measureText("Hello world")
|
||||||
|
doAssert metrics.width == 61
|
||||||
|
|
Loading…
Reference in a new issue