From 3f3db2fcec9bf14b5440c88d7a80b43ec2e91caf Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 23 Jan 2021 14:53:35 -0600 Subject: [PATCH 1/3] remove masks.nim for now --- src/pixie.nim | 16 ++---------- src/pixie/masks.nim | 60 --------------------------------------------- 2 files changed, 2 insertions(+), 74 deletions(-) delete mode 100644 src/pixie/masks.nim diff --git a/src/pixie.nim b/src/pixie.nim index c04658b..2f96229 100644 --- a/src/pixie.nim +++ b/src/pixie.nim @@ -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/svg, flatty/binny, os -export images, masks, paths, common, blends +export images, paths, common, blends type FileFormat* = enum 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 = ## Loads an image from a memory. if data.len > 8 and data.readUint64(0) == cast[uint64](pngSignature): diff --git a/src/pixie/masks.nim b/src/pixie/masks.nim deleted file mode 100644 index 16e6b25..0000000 --- a/src/pixie/masks.nim +++ /dev/null @@ -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. - "" - -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 From 95471f36a7e55b9cc9f2452be6de7ef9875560d2 Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 23 Jan 2021 14:53:43 -0600 Subject: [PATCH 2/3] better error msg --- src/pixie/images.nim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 3e36cac..c306180 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -121,9 +121,15 @@ proc subImage*(image: Image, x, y, w, h: int): Image = ## Gets a sub image from this image. 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: - 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) for y2 in 0 ..< h: From 40dfcfcde109b36481d3c5b90cc7a9246905928b Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Sat, 23 Jan 2021 21:15:39 -0600 Subject: [PATCH 3/3] 0.0.16 quadraticCurveTo, transform --- pixie.nimble | 2 +- src/pixie/paths.nim | 70 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/pixie.nimble b/pixie.nimble index 6f0215f..ed00d2d 100644 --- a/pixie.nimble +++ b/pixie.nimble @@ -1,4 +1,4 @@ -version = "0.0.15" +version = "0.0.16" author = "Andre von Houck and Ryan Oldenburg" description = "Full-featured 2d graphics library for Nim." license = "MIT" diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index 9925ca8..89a7572 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -188,6 +188,60 @@ proc `$`*(path: Path): string = if i != path.commands.len - 1 or j != command.numbers.len - 1: 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]] = ## 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 ])) -proc quadraticCurveTo*(path: Path) = - ## Adds a quadratic Bézier curve to the current path. - raise newException(ValueError, "not implemented") +proc quadraticCurveTo*(path: var Path, x1, y1, x2, y2: float32) = + ## Adds a quadratic Bézier curve to the path. This requires 2 points. + ## 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) = ## 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).