computeBounds, bindings changes
This commit is contained in:
parent
400f66c3f4
commit
fe6fb8c90e
4 changed files with 154 additions and 153 deletions
|
@ -1,4 +1,4 @@
|
|||
import chroma, vmath
|
||||
import bumpy, chroma, vmath
|
||||
|
||||
type
|
||||
PixieError* = object of ValueError ## Raised if an operation fails.
|
||||
|
@ -16,9 +16,20 @@ proc lerp*(a, b: ColorRGBX, t: float32): ColorRGBX {.inline.} =
|
|||
result.b = ((a.b.uint32 * (255 - x) + b.b.uint32 * x) div 255).uint8
|
||||
result.a = ((a.a.uint32 * (255 - x) + b.a.uint32 * x) div 255).uint8
|
||||
|
||||
func lerp*(a, b: Color, v: float32): Color {.inline.} =
|
||||
proc lerp*(a, b: Color, v: float32): Color {.inline.} =
|
||||
## Linearly interpolate between a and b using t.
|
||||
result.r = lerp(a.r, b.r, v)
|
||||
result.g = lerp(a.g, b.g, v)
|
||||
result.b = lerp(a.b, b.b, v)
|
||||
result.a = lerp(a.a, b.a, v)
|
||||
|
||||
proc snapToPixels*(rect: Rect): Rect =
|
||||
let
|
||||
xMin = rect.x
|
||||
xMax = rect.x + rect.w
|
||||
yMin = rect.y
|
||||
yMax = rect.y + rect.h
|
||||
result.x = floor(xMin)
|
||||
result.w = ceil(xMax) - result.x
|
||||
result.y = floor(yMin)
|
||||
result.h = ceil(yMax) - result.y
|
||||
|
|
|
@ -17,9 +17,9 @@ type
|
|||
lineJoin*: LineJoin
|
||||
font*: string ## File path to a .ttf or .otf file.
|
||||
fontSize*: float32
|
||||
textAlign*: HAlignMode
|
||||
textAlign*: HorizontalAlignment
|
||||
lineDash*: seq[float32]
|
||||
path: Path
|
||||
lineDash: seq[float32]
|
||||
mat: Mat3
|
||||
mask: Mask
|
||||
layer: Image
|
||||
|
@ -35,7 +35,7 @@ type
|
|||
lineJoin: LineJoin
|
||||
font: string
|
||||
fontSize*: float32
|
||||
textAlign: HAlignMode
|
||||
textAlign: HorizontalAlignment
|
||||
lineDash: seq[float32]
|
||||
mat: Mat3
|
||||
mask: Mask
|
||||
|
@ -289,6 +289,22 @@ proc quadraticCurveTo*(ctx: Context, ctrl, to: Vec2) {.inline.} =
|
|||
## Bézier curve.
|
||||
ctx.path.quadraticCurveTo(ctrl, to)
|
||||
|
||||
proc arc*(ctx: Context, x, y, r, a0, a1: float32, ccw: bool = false) =
|
||||
## Draws a circular arc.
|
||||
ctx.path.arc(x, y, r, a0, a1, ccw)
|
||||
|
||||
proc arc*(ctx: Context, pos: Vec2, r: float32, a: Vec2, ccw: bool = false) =
|
||||
## Adds a circular arc to the current sub-path.
|
||||
ctx.path.arc(pos, r, a, ccw)
|
||||
|
||||
proc arcTo*(ctx: Context, x1, y1, x2, y2, radius: float32) =
|
||||
## Draws a circular arc using the given control points and radius.
|
||||
ctx.path.arcTo(x1, y1, x2, y2, radius)
|
||||
|
||||
proc arcTo*(ctx: Context, a, b: Vec2, r: float32) =
|
||||
## Adds a circular arc using the given control points and radius.
|
||||
ctx.path.arcTo(a, b, r)
|
||||
|
||||
proc closePath*(ctx: Context) {.inline.} =
|
||||
## Attempts to add a straight line from the current point to the start of
|
||||
## the current sub-path. If the shape has already been closed or has only
|
||||
|
@ -326,6 +342,8 @@ proc fill*(ctx: Context, windingRule = wrNonZero) {.inline.} =
|
|||
## Fills the current path with the current fillStyle.
|
||||
ctx.fill(ctx.path, windingRule)
|
||||
|
||||
proc clip*(ctx: Context, windingRule = wrNonZero) {.inline.}
|
||||
|
||||
proc clip*(ctx: Context, path: Path, windingRule = wrNonZero) =
|
||||
## Turns the path into the current clipping region. The previous clipping
|
||||
## region, if any, is intersected with the current or given path to create
|
||||
|
@ -342,6 +360,8 @@ proc clip*(ctx: Context, windingRule = wrNonZero) {.inline.} =
|
|||
## to create the new clipping region.
|
||||
ctx.clip(ctx.path, windingRule)
|
||||
|
||||
proc stroke*(ctx: Context) {.inline.}
|
||||
|
||||
proc stroke*(ctx: Context, path: Path) =
|
||||
## Strokes (outlines) the current or given path with the current strokeStyle.
|
||||
if ctx.mask != nil and ctx.layer == nil:
|
||||
|
@ -491,7 +511,110 @@ proc resetTransform*(ctx: Context) {.inline.} =
|
|||
## Resets the current transform to the identity matrix.
|
||||
ctx.mat = mat3()
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, dx, dy, dWidth, dHeight: float32) =
|
||||
## Draws a source image onto the destination image.
|
||||
let
|
||||
imageMat = ctx.mat * translate(vec2(dx, dy)) * scale(vec2(
|
||||
dWidth / image.width.float32,
|
||||
dHeight / image.height.float32
|
||||
))
|
||||
savedFillStyle = ctx.fillStyle
|
||||
|
||||
ctx.fillStyle = newPaint(pkImage)
|
||||
ctx.fillStyle.image = image
|
||||
ctx.fillStyle.imageMat = imageMat
|
||||
|
||||
let path = newPath()
|
||||
path.rect(rect(dx, dy, dWidth, dHeight))
|
||||
ctx.fill(path)
|
||||
|
||||
ctx.fillStyle = savedFillStyle
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, dx, dy: float32) =
|
||||
## Draws a source image onto the destination image.
|
||||
ctx.drawImage(image, dx, dx, image.width.float32, image.height.float32)
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, pos: Vec2) =
|
||||
## Draws a source image onto the destination image.
|
||||
ctx.drawImage(image, pos.x, pos.y)
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, rect: Rect) =
|
||||
## Draws a source image onto the destination image.
|
||||
ctx.drawImage(image, rect.x, rect.y, rect.w, rect.h)
|
||||
|
||||
proc drawImage*(
|
||||
ctx: Context,
|
||||
image: Image,
|
||||
sx, sy, sWidth, sHeight,
|
||||
dx, dy, dWidth, dHeight: float32
|
||||
) =
|
||||
## Draws a source image onto the destination image.
|
||||
let image = image.subImage(sx.int, sy.int, sWidth.int, sHeight.int)
|
||||
ctx.drawImage(image, dx, dx, image.width.float32, image.height.float32)
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, src, dest: Rect) =
|
||||
## Draws a source image onto the destination image.
|
||||
ctx.drawImage(
|
||||
image,
|
||||
src.x, src.y, src.w, src.h,
|
||||
dest.x, dest.y, dest.w, dest.h
|
||||
)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, path: Path, pos: Vec2, windingRule = wrNonZero
|
||||
): bool =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
path.fillOverlaps(pos, ctx.mat, windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, path: Path, x, y: float32, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(path, vec2(x, y), windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, pos: Vec2, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(ctx.path, pos, windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, x, y: float32, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(ctx.path, vec2(x, y), windingRule)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, path: Path, pos: Vec2): bool =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
path.strokeOverlaps(
|
||||
pos,
|
||||
ctx.mat,
|
||||
ctx.lineWidth,
|
||||
ctx.lineCap,
|
||||
ctx.lineJoin,
|
||||
ctx.miterLimit,
|
||||
ctx.lineDash
|
||||
)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, path: Path, x, y: float32): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(path, vec2(x, y))
|
||||
|
||||
proc isPointInStroke*(ctx: Context, pos: Vec2): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(ctx.path, pos)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, x, y: float32): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(ctx.path, vec2(x, y))
|
||||
|
||||
#
|
||||
# Additional procs that are not part of the JS API
|
||||
#
|
||||
|
||||
proc roundedRect*(ctx: Context, x, y, w, h, nw, ne, se, sw: float32) {.inline.} =
|
||||
## Adds a rounded rectangle to the current path.
|
||||
|
@ -566,12 +689,6 @@ proc fillCircle*(ctx: Context, circle: Circle) =
|
|||
path.circle(circle)
|
||||
ctx.fill(path)
|
||||
|
||||
proc fillCircle*(ctx: Context, center: Vec2, radius: float32) =
|
||||
## Draws a circle that is filled according to the current fillStyle.
|
||||
let path = newPath()
|
||||
path.ellipse(center, radius, radius)
|
||||
ctx.fill(path)
|
||||
|
||||
proc strokeCircle*(ctx: Context, circle: Circle) =
|
||||
## Draws a circle that is stroked (outlined) according to the current
|
||||
## strokeStyle and other context settings.
|
||||
|
@ -579,13 +696,6 @@ proc strokeCircle*(ctx: Context, circle: Circle) =
|
|||
path.circle(circle)
|
||||
ctx.stroke(path)
|
||||
|
||||
proc strokeCircle*(ctx: Context, center: Vec2, radius: float32) =
|
||||
## Draws a circle that is stroked (outlined) according to the current
|
||||
## strokeStyle and other context settings.
|
||||
let path = newPath()
|
||||
path.ellipse(center, radius, radius)
|
||||
ctx.stroke(path)
|
||||
|
||||
proc fillPolygon*(ctx: Context, pos: Vec2, size: float32, sides: int) =
|
||||
## Draws an n-sided regular polygon at (x, y) of size that is filled according
|
||||
## to the current fillStyle.
|
||||
|
@ -599,120 +709,3 @@ proc strokePolygon*(ctx: Context, pos: Vec2, size: float32, sides: int) =
|
|||
let path = newPath()
|
||||
path.polygon(pos, size, sides)
|
||||
ctx.stroke(path)
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, dx, dy, dWidth, dHeight: float32) =
|
||||
## Draws a source image onto the destination image.
|
||||
let
|
||||
imageMat = ctx.mat * translate(vec2(dx, dy)) * scale(vec2(
|
||||
dWidth / image.width.float32,
|
||||
dHeight / image.height.float32
|
||||
))
|
||||
savedFillStyle = ctx.fillStyle
|
||||
|
||||
ctx.fillStyle = newPaint(pkImage)
|
||||
ctx.fillStyle.image = image
|
||||
ctx.fillStyle.imageMat = imageMat
|
||||
|
||||
let path = newPath()
|
||||
path.rect(rect(dx, dy, dWidth, dHeight))
|
||||
ctx.fill(path)
|
||||
|
||||
ctx.fillStyle = savedFillStyle
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, dx, dy: float32) =
|
||||
## Draws a source image onto the destination image.
|
||||
ctx.drawImage(image, dx, dx, image.width.float32, image.height.float32)
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, pos: Vec2) =
|
||||
## Draws a source image onto the destination image.
|
||||
ctx.drawImage(image, pos.x, pos.y)
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, rect: Rect) =
|
||||
## Draws a source image onto the destination image.
|
||||
ctx.drawImage(image, rect.x, rect.y, rect.w, rect.h)
|
||||
|
||||
proc drawImage*(
|
||||
ctx: Context,
|
||||
image: Image,
|
||||
sx, sy, sWidth, sHeight,
|
||||
dx, dy, dWidth, dHeight: float32
|
||||
) =
|
||||
## Draws a source image onto the destination image.
|
||||
let image = image.subImage(sx.int, sy.int, sWidth.int, sHeight.int)
|
||||
ctx.drawImage(image, dx, dx, image.width.float32, image.height.float32)
|
||||
|
||||
proc drawImage*(ctx: Context, image: Image, src, dest: Rect) =
|
||||
## Draws a source image onto the destination image.
|
||||
ctx.drawImage(
|
||||
image,
|
||||
src.x, src.y, src.w, src.h,
|
||||
dest.x, dest.y, dest.w, dest.h
|
||||
)
|
||||
|
||||
proc arc*(ctx: Context, x, y, r, a0, a1: float32, ccw: bool = false) =
|
||||
## Draws a circular arc.
|
||||
ctx.path.arc(x, y, r, a0, a1, ccw)
|
||||
|
||||
proc arc*(ctx: Context, pos: Vec2, r: float32, a: Vec2, ccw: bool = false) =
|
||||
## Adds a circular arc to the current sub-path.
|
||||
ctx.path.arc(pos, r, a, ccw)
|
||||
|
||||
proc arcTo*(ctx: Context, x1, y1, x2, y2, radius: float32) =
|
||||
## Draws a circular arc using the given control points and radius.
|
||||
ctx.path.arcTo(x1, y1, x2, y2, radius)
|
||||
|
||||
proc arcTo*(ctx: Context, a, b: Vec2, r: float32) =
|
||||
## Adds a circular arc using the given control points and radius.
|
||||
ctx.path.arcTo(a, b, r)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, path: Path, pos: Vec2, windingRule = wrNonZero
|
||||
): bool =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
path.fillOverlaps(pos, ctx.mat, windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, path: Path, x, y: float32, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(path, vec2(x, y), windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, pos: Vec2, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(ctx.path, pos, windingRule)
|
||||
|
||||
proc isPointInPath*(
|
||||
ctx: Context, x, y: float32, windingRule = wrNonZero
|
||||
): bool {.inline.} =
|
||||
## Returns whether or not the specified point is contained in the current path.
|
||||
ctx.isPointInPath(ctx.path, vec2(x, y), windingRule)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, path: Path, pos: Vec2): bool =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
path.strokeOverlaps(
|
||||
pos,
|
||||
ctx.mat,
|
||||
ctx.lineWidth,
|
||||
ctx.lineCap,
|
||||
ctx.lineJoin,
|
||||
ctx.miterLimit,
|
||||
ctx.lineDash
|
||||
)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, path: Path, x, y: float32): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(path, vec2(x, y))
|
||||
|
||||
proc isPointInStroke*(ctx: Context, pos: Vec2): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(ctx.path, pos)
|
||||
|
||||
proc isPointInStroke*(ctx: Context, x, y: float32): bool {.inline.} =
|
||||
## Returns whether or not the specified point is inside the area contained
|
||||
## by the stroking of a path.
|
||||
ctx.isPointInStroke(ctx.path, vec2(x, y))
|
||||
|
|
|
@ -35,12 +35,12 @@ type
|
|||
positions*: seq[Vec2] ## The positions of the glyphs for each rune.
|
||||
selectionRects*: seq[Rect] ## The selection rects for each glyph.
|
||||
|
||||
HAlignMode* = enum
|
||||
HorizontalAlignment* = enum
|
||||
haLeft
|
||||
haCenter
|
||||
haRight
|
||||
|
||||
VAlignMode* = enum
|
||||
VerticalAlignment* = enum
|
||||
vaTop
|
||||
vaMiddle
|
||||
vaBottom
|
||||
|
|
|
@ -1021,7 +1021,13 @@ proc requiresAntiAliasing(segments: seq[(Segment, int16)]): bool =
|
|||
# AA is required if all segments are not vertical or have fractional > 0
|
||||
return true
|
||||
|
||||
proc computePixelBounds(segments: seq[(Segment, int16)]): Rect =
|
||||
proc transform(shapes: var seq[seq[Vec2]], transform: Mat3) =
|
||||
if transform != mat3():
|
||||
for shape in shapes.mitems:
|
||||
for vec in shape.mitems:
|
||||
vec = transform * vec
|
||||
|
||||
proc computeBounds(segments: seq[(Segment, int16)]): Rect =
|
||||
## Compute the bounds of the segments.
|
||||
var
|
||||
xMin = float32.high
|
||||
|
@ -1035,11 +1041,6 @@ proc computePixelBounds(segments: seq[(Segment, int16)]): Rect =
|
|||
yMin = min(yMin, segment.at.y)
|
||||
yMax = max(yMax, segment.to.y)
|
||||
|
||||
xMin = floor(xMin)
|
||||
xMax = ceil(xMax)
|
||||
yMin = floor(yMin)
|
||||
yMax = ceil(yMax)
|
||||
|
||||
if xMin.isNaN() or xMax.isNaN() or yMin.isNaN() or yMax.isNaN():
|
||||
discard
|
||||
else:
|
||||
|
@ -1048,9 +1049,11 @@ proc computePixelBounds(segments: seq[(Segment, int16)]): Rect =
|
|||
result.w = xMax - xMin
|
||||
result.h = yMax - yMin
|
||||
|
||||
proc computePixelBounds*(path: Path): Rect =
|
||||
proc computeBounds*(path: Path, transform = mat3()): Rect =
|
||||
## Compute the bounds of the path.
|
||||
path.commandsToShapes().shapesToSegments().computePixelBounds()
|
||||
var shapes = path.commandsToShapes()
|
||||
shapes.transform(transform)
|
||||
computeBounds(shapes.shapesToSegments())
|
||||
|
||||
proc partitionSegments(
|
||||
segments: seq[(Segment, int16)], top, height: int
|
||||
|
@ -1482,7 +1485,7 @@ proc fillShapes(
|
|||
rgbx = color.asRgbx()
|
||||
segments = shapes.shapesToSegments()
|
||||
aa = segments.requiresAntiAliasing()
|
||||
bounds = computePixelBounds(segments)
|
||||
bounds = computeBounds(segments).snapToPixels()
|
||||
startX = max(0, bounds.x.int)
|
||||
startY = max(0, bounds.y.int)
|
||||
pathHeight = min(image.height, (bounds.y + bounds.h).int)
|
||||
|
@ -1539,7 +1542,7 @@ proc fillShapes(
|
|||
let
|
||||
segments = shapes.shapesToSegments()
|
||||
aa = segments.requiresAntiAliasing()
|
||||
bounds = computePixelBounds(segments)
|
||||
bounds = computeBounds(segments).snapToPixels()
|
||||
startX = max(0, bounds.x.int)
|
||||
startY = max(0, bounds.y.int)
|
||||
pathHeight = min(mask.height, (bounds.y + bounds.h).int)
|
||||
|
@ -1729,12 +1732,6 @@ proc parseSomePath(
|
|||
elif type(path) is Path:
|
||||
path.commandsToShapes(closeSubpaths, pixelScale)
|
||||
|
||||
proc transform(shapes: var seq[seq[Vec2]], transform: Mat3) =
|
||||
if transform != mat3():
|
||||
for shape in shapes.mitems:
|
||||
for segment in shape.mitems:
|
||||
segment = transform * segment
|
||||
|
||||
proc fillPath*(
|
||||
mask: Mask,
|
||||
path: SomePath,
|
||||
|
|
Loading…
Reference in a new issue