Merge pull request #69 from guzba/master
0.0.16 quadraticCurveTo, path.transform, remove masks.nim for now (unused), better error msg
This commit is contained in:
commit
a4b9e524c3
5 changed files with 78 additions and 80 deletions
|
@ -1,4 +1,4 @@
|
||||||
version = "0.0.15"
|
version = "0.0.16"
|
||||||
author = "Andre von Houck and Ryan Oldenburg"
|
author = "Andre von Houck and Ryan Oldenburg"
|
||||||
description = "Full-featured 2d graphics library for Nim."
|
description = "Full-featured 2d graphics library for Nim."
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -1,25 +1,13 @@
|
||||||
import pixie/images, pixie/masks, pixie/paths, pixie/common, pixie/blends,
|
import pixie/images, pixie/paths, pixie/common, pixie/blends,
|
||||||
pixie/fileformats/bmp, pixie/fileformats/png, pixie/fileformats/jpg,
|
pixie/fileformats/bmp, pixie/fileformats/png, pixie/fileformats/jpg,
|
||||||
pixie/fileformats/svg, flatty/binny, os
|
pixie/fileformats/svg, flatty/binny, os
|
||||||
|
|
||||||
export images, masks, paths, common, blends
|
export images, paths, common, blends
|
||||||
|
|
||||||
type
|
type
|
||||||
FileFormat* = enum
|
FileFormat* = enum
|
||||||
ffPng, ffBmp, ffJpg
|
ffPng, ffBmp, ffJpg
|
||||||
|
|
||||||
proc toMask*(image: Image): Mask =
|
|
||||||
## Converts an Image to a Mask.
|
|
||||||
result = newMask(image.width, image.height)
|
|
||||||
for i in 0 ..< image.data.len:
|
|
||||||
result.data[i] = image.data[i].a
|
|
||||||
|
|
||||||
proc toImage*(mask: Mask): Image =
|
|
||||||
## Converts a Mask to Image.
|
|
||||||
result = newImage(mask.width, mask.height)
|
|
||||||
for i in 0 ..< mask.data.len:
|
|
||||||
result.data[i].a = mask.data[i]
|
|
||||||
|
|
||||||
proc decodeImage*(data: string | seq[uint8]): Image =
|
proc decodeImage*(data: string | seq[uint8]): Image =
|
||||||
## Loads an image from a memory.
|
## Loads an image from a memory.
|
||||||
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
|
if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature):
|
||||||
|
|
|
@ -121,9 +121,15 @@ proc subImage*(image: Image, x, y, w, h: int): Image =
|
||||||
## Gets a sub image from this image.
|
## Gets a sub image from this image.
|
||||||
|
|
||||||
if x < 0 or x + w > image.width:
|
if x < 0 or x + w > image.width:
|
||||||
raise newException(PixieError, "Param x value " & $x & " is out of bounds")
|
raise newException(
|
||||||
|
PixieError,
|
||||||
|
"Params x: " & $x & " w: " & $w & " invalid, image width is " & $image.width
|
||||||
|
)
|
||||||
if y < 0 or y + h > image.height:
|
if y < 0 or y + h > image.height:
|
||||||
raise newException(PixieError, "Param y value " & $y & " is out of bounds")
|
raise newException(
|
||||||
|
PixieError,
|
||||||
|
"Params y: " & $y & " h: " & $h & " invalid, image height is " & $image.height
|
||||||
|
)
|
||||||
|
|
||||||
result = newImage(w, h)
|
result = newImage(w, h)
|
||||||
for y2 in 0 ..< h:
|
for y2 in 0 ..< h:
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
import system/memory
|
|
||||||
|
|
||||||
type
|
|
||||||
Mask* = ref object
|
|
||||||
## Main mask object that holds the mask data.
|
|
||||||
width*, height*: int
|
|
||||||
data*: seq[uint8]
|
|
||||||
|
|
||||||
proc newMask*(width, height: int): Mask =
|
|
||||||
## Creates a new mask with appropriate dimensions.
|
|
||||||
result = Mask()
|
|
||||||
result.width = width
|
|
||||||
result.height = height
|
|
||||||
result.data = newSeq[uint8](width * height)
|
|
||||||
|
|
||||||
proc inside*(mask: Mask, x, y: int): bool {.inline.} =
|
|
||||||
## Returns true if (x, y) is inside the mask.
|
|
||||||
x >= 0 and x < mask.width and y >= 0 and y < mask.height
|
|
||||||
|
|
||||||
proc copy*(mask: Mask): Mask =
|
|
||||||
## Copies an mask creating a new mask.
|
|
||||||
result = newMask(mask.width, mask.height)
|
|
||||||
result.data = mask.data
|
|
||||||
|
|
||||||
proc `$`*(mask: Mask): string =
|
|
||||||
## Display the mask size and channels.
|
|
||||||
"<Mask " & $mask.width & "x" & $mask.height & ">"
|
|
||||||
|
|
||||||
proc getUnsafe*(mask: Mask, x, y: int): uint8 {.inline.} =
|
|
||||||
## Gets a value from (x, y) coordinates.
|
|
||||||
## * No bounds checking *
|
|
||||||
## Make sure that x, y are in bounds.
|
|
||||||
## Failure in the assumptions will case unsafe memory reads.
|
|
||||||
result = mask.data[mask.width * y + x]
|
|
||||||
|
|
||||||
proc `[]`*(mask: Mask, x, y: int): uint8 {.inline.} =
|
|
||||||
## Gets the value at (x, y) or returns transparent black if outside of bounds.
|
|
||||||
if mask.inside(x, y):
|
|
||||||
return mask.getUnsafe(x, y)
|
|
||||||
|
|
||||||
proc setUnsafe*(mask: Mask, x, y: int, value: uint8) {.inline.} =
|
|
||||||
## Sets the value from (x, y) coordinates.
|
|
||||||
## * No bounds checking *
|
|
||||||
## Make sure that x, y are in bounds.
|
|
||||||
## Failure in the assumptions will case unsafe memory writes.
|
|
||||||
mask.data[mask.width * y + x] = value
|
|
||||||
|
|
||||||
proc `[]=`*(mask: Mask, x, y: int, value: uint8) {.inline.} =
|
|
||||||
## Sets the value at (x, y) or does nothing if outside of bounds.
|
|
||||||
if mask.inside(x, y):
|
|
||||||
mask.setUnsafe(x, y, value)
|
|
||||||
|
|
||||||
proc fill*(mask: Mask, value: uint8) =
|
|
||||||
## Fills the mask with the parameter value.
|
|
||||||
nimSetMem(mask.data[0].addr, value.cint, mask.data.len)
|
|
||||||
|
|
||||||
proc invert*(mask: Mask) =
|
|
||||||
## Inverts the entire mask value.
|
|
||||||
for value in mask.data.mitems:
|
|
||||||
value = 255 - value
|
|
|
@ -188,6 +188,60 @@ proc `$`*(path: Path): string =
|
||||||
if i != path.commands.len - 1 or j != command.numbers.len - 1:
|
if i != path.commands.len - 1 or j != command.numbers.len - 1:
|
||||||
result.add " "
|
result.add " "
|
||||||
|
|
||||||
|
proc transform*(path: Path, mat: Mat3) =
|
||||||
|
for command in path.commands.mitems:
|
||||||
|
case command.kind:
|
||||||
|
of Close:
|
||||||
|
discard
|
||||||
|
of Move, Line, RMove, RLine, TQuad, RTQuad:
|
||||||
|
var pos = vec2(command.numbers[0], command.numbers[1])
|
||||||
|
pos = mat * pos
|
||||||
|
command.numbers[0] = pos.x
|
||||||
|
command.numbers[1] = pos.y
|
||||||
|
of HLine, RHLine:
|
||||||
|
var pos = vec2(command.numbers[0], 0)
|
||||||
|
pos = mat * pos
|
||||||
|
command.numbers[0] = pos.x
|
||||||
|
of VLine, RVLine:
|
||||||
|
var pos = vec2(0, command.numbers[0])
|
||||||
|
pos = mat * pos
|
||||||
|
command.numbers[0] = pos.y
|
||||||
|
of Cubic, RCubic:
|
||||||
|
var
|
||||||
|
ctrl1 = vec2(command.numbers[0], command.numbers[1])
|
||||||
|
ctrl2 = vec2(command.numbers[2], command.numbers[3])
|
||||||
|
to = vec2(command.numbers[4], command.numbers[5])
|
||||||
|
ctrl1 = mat * ctrl1
|
||||||
|
ctrl2 = mat * ctrl2
|
||||||
|
to = mat * to
|
||||||
|
command.numbers[0] = ctrl1.x
|
||||||
|
command.numbers[1] = ctrl1.y
|
||||||
|
command.numbers[2] = ctrl2.x
|
||||||
|
command.numbers[3] = ctrl2.y
|
||||||
|
command.numbers[4] = to.x
|
||||||
|
command.numbers[5] = to.y
|
||||||
|
of SCubic, RSCubic, Quad, RQuad:
|
||||||
|
var
|
||||||
|
ctrl = vec2(command.numbers[0], command.numbers[1])
|
||||||
|
to = vec2(command.numbers[2], command.numbers[3])
|
||||||
|
ctrl = mat * ctrl
|
||||||
|
to = mat * to
|
||||||
|
command.numbers[0] = ctrl.x
|
||||||
|
command.numbers[1] = ctrl.y
|
||||||
|
command.numbers[2] = to.x
|
||||||
|
command.numbers[3] = to.y
|
||||||
|
of Arc, RArc:
|
||||||
|
var
|
||||||
|
radii = vec2(command.numbers[0], command.numbers[1])
|
||||||
|
to = vec2(command.numbers[5], command.numbers[6])
|
||||||
|
# Extract the scale from the matrix and only apply that to the radii
|
||||||
|
radii = scale(vec2(mat[0, 0], mat[1, 1])) * radii
|
||||||
|
to = mat * to
|
||||||
|
command.numbers[0] = radii.x
|
||||||
|
command.numbers[1] = radii.y
|
||||||
|
command.numbers[5] = to.x
|
||||||
|
command.numbers[6] = to.y
|
||||||
|
|
||||||
proc commandsToPolygons*(commands: seq[PathCommand]): seq[seq[Vec2]] =
|
proc commandsToPolygons*(commands: seq[PathCommand]): seq[seq[Vec2]] =
|
||||||
## Converts SVG-like commands to simpler polygon
|
## Converts SVG-like commands to simpler polygon
|
||||||
|
|
||||||
|
@ -855,9 +909,19 @@ proc bezierCurveTo*(path: Path, x1, y1, x2, y2, x3, y3: float32) =
|
||||||
x1, y1, x2, y2, x3, y3
|
x1, y1, x2, y2, x3, y3
|
||||||
]))
|
]))
|
||||||
|
|
||||||
proc quadraticCurveTo*(path: Path) =
|
proc quadraticCurveTo*(path: var Path, x1, y1, x2, y2: float32) =
|
||||||
## Adds a quadratic Bézier curve to the current path.
|
## Adds a quadratic Bézier curve to the path. This requires 2 points.
|
||||||
raise newException(ValueError, "not implemented")
|
## The first point is the control point and the second is the end point.
|
||||||
|
## The starting point is the last point in the current path, which can be
|
||||||
|
## changed using moveTo() before creating the curve.
|
||||||
|
path.commands.add(PathCommand(
|
||||||
|
kind: Quad,
|
||||||
|
numbers: @[x1, y1, x2, y2]
|
||||||
|
))
|
||||||
|
path.at = vec2(x2, y2)
|
||||||
|
|
||||||
|
proc quadraticCurveTo*(path: var Path, ctrl, to: Vec2) {.inline.} =
|
||||||
|
path.quadraticCurveTo(ctrl.x, ctrl.y, to.x, to.y)
|
||||||
|
|
||||||
proc arc*(path: Path) =
|
proc arc*(path: Path) =
|
||||||
## Adds an arc to the path which is centered at (x, y) position with radius r starting at startAngle and ending at endAngle going in the given direction by anticlockwise (defaulting to clockwise).
|
## Adds an arc to the path which is centered at (x, y) position with radius r starting at startAngle and ending at endAngle going in the given direction by anticlockwise (defaulting to clockwise).
|
||||||
|
|
Loading…
Reference in a new issue