Merge pull request #260 from guzba/master
binding stuff, image isTransparent isOneColor, superImage
This commit is contained in:
commit
400f66c3f4
12 changed files with 160 additions and 107 deletions
|
@ -120,7 +120,7 @@ font.size = 20
|
||||||
|
|
||||||
let text = "Typesetting is the arrangement and composition of text in graphic design and publishing in both digital and traditional medias."
|
let text = "Typesetting is the arrangement and composition of text in graphic design and publishing in both digital and traditional medias."
|
||||||
|
|
||||||
image.fillText(font.typeset(text, bounds = vec2(180, 180)), vec2(10, 10))
|
image.fillText(font.typeset(text, vec2(180, 180)), translate(vec2(10, 10)))
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ let spans = @[
|
||||||
newFont(typeface, 14, color(0.3125, 0.3125, 0.3125, 1)))
|
newFont(typeface, 14, color(0.3125, 0.3125, 0.3125, 1)))
|
||||||
]
|
]
|
||||||
|
|
||||||
image.fillText(typeset(spans, bounds = vec2(180, 180)), vec2(10, 10))
|
image.fillText(typeset(spans, vec2(180, 180)), translate(vec2(10, 10)))
|
||||||
```
|
```
|
||||||

|

|
||||||
|
|
||||||
|
|
|
@ -8,5 +8,5 @@ font.size = 20
|
||||||
|
|
||||||
let text = "Typesetting is the arrangement and composition of text in graphic design and publishing in both digital and traditional medias."
|
let text = "Typesetting is the arrangement and composition of text in graphic design and publishing in both digital and traditional medias."
|
||||||
|
|
||||||
image.fillText(font.typeset(text, bounds = vec2(180, 180)), vec2(10, 10))
|
image.fillText(font.typeset(text, vec2(180, 180)), translate(vec2(10, 10)))
|
||||||
image.writeFile("examples/text.png")
|
image.writeFile("examples/text.png")
|
||||||
|
|
|
@ -19,5 +19,5 @@ let spans = @[
|
||||||
newFont(typeface, 14, color(0.3125, 0.3125, 0.3125, 1)))
|
newFont(typeface, 14, color(0.3125, 0.3125, 0.3125, 1)))
|
||||||
]
|
]
|
||||||
|
|
||||||
image.fillText(typeset(spans, bounds = vec2(180, 180)), vec2(10, 10))
|
image.fillText(typeset(spans, vec2(180, 180)), translate(vec2(10, 10)))
|
||||||
image.writeFile("examples/text_spans.png")
|
image.writeFile("examples/text_spans.png")
|
||||||
|
|
|
@ -505,9 +505,9 @@ proc circle*(ctx: Context, cx, cy, r: float32) {.inline.} =
|
||||||
## Adds a circle to the current path.
|
## Adds a circle to the current path.
|
||||||
ctx.path.circle(cx, cy, r)
|
ctx.path.circle(cx, cy, r)
|
||||||
|
|
||||||
proc circle*(ctx: Context, center: Vec2, r: float32) {.inline.} =
|
proc circle*(ctx: Context, circle: Circle) {.inline.} =
|
||||||
## Adds a circle to the current path.
|
## Adds a circle to the current path.
|
||||||
ctx.path.circle(center, r)
|
ctx.path.circle(circle)
|
||||||
|
|
||||||
proc polygon*(ctx: Context, x, y, size: float32, sides: int) {.inline.} =
|
proc polygon*(ctx: Context, x, y, size: float32, sides: int) {.inline.} =
|
||||||
## Adds an n-sided regular polygon at (x, y) of size to the current path.
|
## Adds an n-sided regular polygon at (x, y) of size to the current path.
|
||||||
|
@ -563,7 +563,7 @@ proc strokeEllipse*(ctx: Context, center: Vec2, rx, ry: float32) =
|
||||||
proc fillCircle*(ctx: Context, circle: Circle) =
|
proc fillCircle*(ctx: Context, circle: Circle) =
|
||||||
## Draws a circle that is filled according to the current fillStyle
|
## Draws a circle that is filled according to the current fillStyle
|
||||||
let path = newPath()
|
let path = newPath()
|
||||||
path.circle(circle.pos, circle.radius)
|
path.circle(circle)
|
||||||
ctx.fill(path)
|
ctx.fill(path)
|
||||||
|
|
||||||
proc fillCircle*(ctx: Context, center: Vec2, radius: float32) =
|
proc fillCircle*(ctx: Context, center: Vec2, radius: float32) =
|
||||||
|
@ -576,7 +576,7 @@ proc strokeCircle*(ctx: Context, circle: Circle) =
|
||||||
## Draws a circle that is stroked (outlined) according to the current
|
## Draws a circle that is stroked (outlined) according to the current
|
||||||
## strokeStyle and other context settings.
|
## strokeStyle and other context settings.
|
||||||
let path = newPath()
|
let path = newPath()
|
||||||
path.circle(circle.pos, circle.radius)
|
path.circle(circle)
|
||||||
ctx.stroke(path)
|
ctx.stroke(path)
|
||||||
|
|
||||||
proc strokeCircle*(ctx: Context, center: Vec2, radius: float32) =
|
proc strokeCircle*(ctx: Context, center: Vec2, radius: float32) =
|
||||||
|
|
|
@ -437,7 +437,7 @@ proc parseSvgFont*(buf: string): Typeface =
|
||||||
proc textUber(
|
proc textUber(
|
||||||
target: Image | Mask,
|
target: Image | Mask,
|
||||||
arrangement: Arrangement,
|
arrangement: Arrangement,
|
||||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
transform = mat3(),
|
||||||
strokeWidth = 1.0,
|
strokeWidth = 1.0,
|
||||||
lineCap = lcButt,
|
lineCap = lcButt,
|
||||||
lineJoin = ljMiter,
|
lineJoin = ljMiter,
|
||||||
|
@ -518,7 +518,7 @@ proc textUber(
|
||||||
proc fillText*(
|
proc fillText*(
|
||||||
target: Image | Mask,
|
target: Image | Mask,
|
||||||
arrangement: Arrangement,
|
arrangement: Arrangement,
|
||||||
transform: Vec2 | Mat3 = vec2(0, 0)
|
transform = mat3()
|
||||||
) {.inline.} =
|
) {.inline.} =
|
||||||
## Fills the text arrangement.
|
## Fills the text arrangement.
|
||||||
textUber(
|
textUber(
|
||||||
|
@ -531,7 +531,7 @@ proc fillText*(
|
||||||
target: Image | Mask,
|
target: Image | Mask,
|
||||||
font: Font,
|
font: Font,
|
||||||
text: string,
|
text: string,
|
||||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
transform = mat3(),
|
||||||
bounds = vec2(0, 0),
|
bounds = vec2(0, 0),
|
||||||
hAlign = haLeft,
|
hAlign = haLeft,
|
||||||
vAlign = vaTop
|
vAlign = vaTop
|
||||||
|
@ -546,7 +546,7 @@ proc fillText*(
|
||||||
proc strokeText*(
|
proc strokeText*(
|
||||||
target: Image | Mask,
|
target: Image | Mask,
|
||||||
arrangement: Arrangement,
|
arrangement: Arrangement,
|
||||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
transform = mat3(),
|
||||||
strokeWidth = 1.0,
|
strokeWidth = 1.0,
|
||||||
lineCap = lcButt,
|
lineCap = lcButt,
|
||||||
lineJoin = ljMiter,
|
lineJoin = ljMiter,
|
||||||
|
@ -570,7 +570,7 @@ proc strokeText*(
|
||||||
target: Image | Mask,
|
target: Image | Mask,
|
||||||
font: Font,
|
font: Font,
|
||||||
text: string,
|
text: string,
|
||||||
transform: Vec2 | Mat3 = vec2(0, 0),
|
transform = mat3(),
|
||||||
strokeWidth = 1.0,
|
strokeWidth = 1.0,
|
||||||
bounds = vec2(0, 0),
|
bounds = vec2(0, 0),
|
||||||
hAlign = haLeft,
|
hAlign = haLeft,
|
||||||
|
|
|
@ -126,6 +126,41 @@ proc fill*(image: Image, color: SomeColor) {.inline.} =
|
||||||
## Fills the image with the parameter color.
|
## Fills the image with the parameter color.
|
||||||
fillUnsafe(image.data, color, 0, image.data.len)
|
fillUnsafe(image.data, color, 0, image.data.len)
|
||||||
|
|
||||||
|
proc isOneColor(image: Image, color: ColorRGBX): bool =
|
||||||
|
## Checks if the entire image is color.
|
||||||
|
result = true
|
||||||
|
|
||||||
|
let color = image.getRgbaUnsafe(0, 0)
|
||||||
|
|
||||||
|
var i: int
|
||||||
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
|
let colorVec = mm_set1_epi32(cast[int32](color))
|
||||||
|
for j in countup(0, image.data.len - 16, 16):
|
||||||
|
let
|
||||||
|
values0 = mm_loadu_si128(image.data[j].addr)
|
||||||
|
values1 = mm_loadu_si128(image.data[j + 4].addr)
|
||||||
|
values2 = mm_loadu_si128(image.data[j + 8].addr)
|
||||||
|
values3 = mm_loadu_si128(image.data[j + 12].addr)
|
||||||
|
values01 = mm_or_si128(values0, values1)
|
||||||
|
values23 = mm_or_si128(values2, values3)
|
||||||
|
values = mm_or_si128(values01, values23)
|
||||||
|
mask = mm_movemask_epi8(mm_cmpeq_epi8(values, colorVec))
|
||||||
|
if mask != uint16.high.int:
|
||||||
|
return false
|
||||||
|
i += 16
|
||||||
|
|
||||||
|
for j in i ..< image.data.len:
|
||||||
|
if image.data[j] != color:
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc isOneColor*(image: Image): bool =
|
||||||
|
## Checks if the entire image is the same color.
|
||||||
|
image.isOneColor(image.getRgbaUnsafe(0, 0))
|
||||||
|
|
||||||
|
proc isTransparent*(image: Image): bool =
|
||||||
|
## Checks if this image is fully transparent or not.
|
||||||
|
image.isOneColor(rgbx(0, 0, 0, 0))
|
||||||
|
|
||||||
proc flipHorizontal*(image: Image) =
|
proc flipHorizontal*(image: Image) =
|
||||||
## Flips the image around the Y axis.
|
## Flips the image around the Y axis.
|
||||||
let w = image.width div 2
|
let w = image.width div 2
|
||||||
|
@ -170,30 +205,6 @@ proc subImage*(image: Image, x, y, w, h: int): Image =
|
||||||
w * 4
|
w * 4
|
||||||
)
|
)
|
||||||
|
|
||||||
proc superImage*(image: Image, x, y, w, h: int): Image =
|
|
||||||
## Either cuts a sub image or returns a super image with padded transparency.
|
|
||||||
if x >= 0 and x + w <= image.width and y >= 0 and y + h <= image.height:
|
|
||||||
result = image.subImage(x, y, w, h)
|
|
||||||
elif abs(x) >= image.width or abs(y) >= image.height:
|
|
||||||
# Nothing to copy, just an empty new image
|
|
||||||
result = newImage(w, h)
|
|
||||||
else:
|
|
||||||
let
|
|
||||||
readOffsetX = max(x, 0)
|
|
||||||
readOffsetY = max(y, 0)
|
|
||||||
writeOffsetX = max(0 - x, 0)
|
|
||||||
writeOffsetY = max(0 - y, 0)
|
|
||||||
copyWidth = max(min(image.width, w) - abs(x), 0)
|
|
||||||
copyHeight = max(min(image.height, h) - abs(y), 0)
|
|
||||||
|
|
||||||
result = newImage(w, h)
|
|
||||||
for y2 in 0 ..< copyHeight:
|
|
||||||
copyMem(
|
|
||||||
result.data[result.dataIndex(writeOffsetX, writeOffsetY + y2)].addr,
|
|
||||||
image.data[image.dataIndex(readOffsetX, readOffsetY + y2)].addr,
|
|
||||||
copyWidth * 4
|
|
||||||
)
|
|
||||||
|
|
||||||
proc diff*(master, image: Image): (float32, Image) =
|
proc diff*(master, image: Image): (float32, Image) =
|
||||||
## Compares the parameters and returns a score and image of the difference.
|
## Compares the parameters and returns a score and image of the difference.
|
||||||
let
|
let
|
||||||
|
@ -815,7 +826,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) =
|
||||||
zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax))
|
zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax))
|
||||||
|
|
||||||
proc draw*(
|
proc draw*(
|
||||||
a, b: Image, transform: Mat3 = mat3(), blendMode = bmNormal
|
a, b: Image, transform = mat3(), blendMode = bmNormal
|
||||||
) {.inline.} =
|
) {.inline.} =
|
||||||
## Draws one image onto another using matrix with color blending.
|
## Draws one image onto another using matrix with color blending.
|
||||||
when type(transform) is Vec2:
|
when type(transform) is Vec2:
|
||||||
|
@ -824,7 +835,7 @@ proc draw*(
|
||||||
a.drawUber(b, transform, blendMode)
|
a.drawUber(b, transform, blendMode)
|
||||||
|
|
||||||
proc draw*(
|
proc draw*(
|
||||||
a, b: Mask, transform: Mat3 = mat3(), blendMode = bmMask
|
a, b: Mask, transform = mat3(), blendMode = bmMask
|
||||||
) {.inline.} =
|
) {.inline.} =
|
||||||
## Draws a mask onto a mask using a matrix with color blending.
|
## Draws a mask onto a mask using a matrix with color blending.
|
||||||
when type(transform) is Vec2:
|
when type(transform) is Vec2:
|
||||||
|
@ -833,7 +844,7 @@ proc draw*(
|
||||||
a.drawUber(b, transform, blendMode)
|
a.drawUber(b, transform, blendMode)
|
||||||
|
|
||||||
proc draw*(
|
proc draw*(
|
||||||
image: Image, mask: Mask, transform: Mat3 = mat3(), blendMode = bmMask
|
image: Image, mask: Mask, transform = mat3(), blendMode = bmMask
|
||||||
) {.inline.} =
|
) {.inline.} =
|
||||||
## Draws a mask onto an image using a matrix with color blending.
|
## Draws a mask onto an image using a matrix with color blending.
|
||||||
when type(transform) is Vec2:
|
when type(transform) is Vec2:
|
||||||
|
@ -842,7 +853,7 @@ proc draw*(
|
||||||
image.drawUber(mask, transform, blendMode)
|
image.drawUber(mask, transform, blendMode)
|
||||||
|
|
||||||
proc draw*(
|
proc draw*(
|
||||||
mask: Mask, image: Image, transform: Mat3 = mat3(), blendMode = bmMask
|
mask: Mask, image: Image, transform = mat3(), blendMode = bmMask
|
||||||
) {.inline.} =
|
) {.inline.} =
|
||||||
## Draws a image onto a mask using a matrix with color blending.
|
## Draws a image onto a mask using a matrix with color blending.
|
||||||
when type(transform) is Vec2:
|
when type(transform) is Vec2:
|
||||||
|
@ -882,5 +893,13 @@ proc shadow*(
|
||||||
result.fill(color)
|
result.fill(color)
|
||||||
result.draw(shifted, blendMode = bmMask)
|
result.draw(shifted, blendMode = bmMask)
|
||||||
|
|
||||||
|
proc superImage*(image: Image, x, y, w, h: int): Image =
|
||||||
|
## Either cuts a sub image or returns a super image with padded transparency.
|
||||||
|
if x >= 0 and x + w <= image.width and y >= 0 and y + h <= image.height:
|
||||||
|
result = image.subImage(x, y, w, h)
|
||||||
|
else:
|
||||||
|
result = newImage(w, h)
|
||||||
|
result.draw(image, translate(vec2(-x.float32, -y.float32)), bmOverwrite)
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
@ -17,13 +17,13 @@ type
|
||||||
## Line join type for strokes.
|
## Line join type for strokes.
|
||||||
ljMiter, ljRound, ljBevel
|
ljMiter, ljRound, ljBevel
|
||||||
|
|
||||||
PathCommandKind* = enum
|
PathCommandKind = enum
|
||||||
## Type of path commands
|
## Type of path commands
|
||||||
Close,
|
Close,
|
||||||
Move, Line, HLine, VLine, Cubic, SCubic, Quad, TQuad, Arc,
|
Move, Line, HLine, VLine, Cubic, SCubic, Quad, TQuad, Arc,
|
||||||
RMove, RLine, RHLine, RVLine, RCubic, RSCubic, RQuad, RTQuad, RArc
|
RMove, RLine, RHLine, RVLine, RCubic, RSCubic, RQuad, RTQuad, RArc
|
||||||
|
|
||||||
PathCommand* = object
|
PathCommand = object
|
||||||
## Binary version of an SVG command.
|
## Binary version of an SVG command.
|
||||||
kind: PathCommandKind
|
kind: PathCommandKind
|
||||||
numbers: seq[float32]
|
numbers: seq[float32]
|
||||||
|
@ -50,15 +50,12 @@ proc newPath*(): Path =
|
||||||
## Create a new Path.
|
## Create a new Path.
|
||||||
Path()
|
Path()
|
||||||
|
|
||||||
proc pixelScale(transform: Vec2 | Mat3): float32 =
|
proc pixelScale(transform: Mat3): float32 =
|
||||||
## What is the largest scale factor of this transform?
|
## What is the largest scale factor of this transform?
|
||||||
when type(transform) is Vec2:
|
max(
|
||||||
return 1.0
|
vec2(transform[0, 0], transform[0, 1]).length,
|
||||||
else:
|
vec2(transform[1, 0], transform[1, 1]).length
|
||||||
max(
|
)
|
||||||
vec2(transform[0, 0], transform[0, 1]).length,
|
|
||||||
vec2(transform[1, 0], transform[1, 1]).length
|
|
||||||
)
|
|
||||||
|
|
||||||
proc isRelative(kind: PathCommandKind): bool =
|
proc isRelative(kind: PathCommandKind): bool =
|
||||||
kind in {
|
kind in {
|
||||||
|
@ -507,12 +504,6 @@ proc rect*(path: Path, x, y, w, h: float32, clockwise = true) =
|
||||||
path.lineTo(x + w, y)
|
path.lineTo(x + w, y)
|
||||||
path.closePath()
|
path.closePath()
|
||||||
|
|
||||||
proc rect*(path: Path, pos: Vec2, wh: Vec2, clockwise = true) {.inline.} =
|
|
||||||
## Adds a rectangle.
|
|
||||||
## Clockwise param can be used to subtract a rect from a path when using
|
|
||||||
## even-odd winding rule.
|
|
||||||
path.rect(pos.x, pos.y, wh.x, wh.y, clockwise)
|
|
||||||
|
|
||||||
proc rect*(path: Path, rect: Rect, clockwise = true) {.inline.} =
|
proc rect*(path: Path, rect: Rect, clockwise = true) {.inline.} =
|
||||||
## Adds a rectangle.
|
## Adds a rectangle.
|
||||||
## Clockwise param can be used to subtract a rect from a path when using
|
## Clockwise param can be used to subtract a rect from a path when using
|
||||||
|
@ -590,14 +581,6 @@ proc roundedRect*(
|
||||||
|
|
||||||
path.closePath()
|
path.closePath()
|
||||||
|
|
||||||
proc roundedRect*(
|
|
||||||
path: Path, pos, wh: Vec2, nw, ne, se, sw: float32, clockwise = true
|
|
||||||
) {.inline.} =
|
|
||||||
## Adds a rounded rectangle.
|
|
||||||
## Clockwise param can be used to subtract a rect from a path when using
|
|
||||||
## even-odd winding rule.
|
|
||||||
path.roundedRect(pos.x, pos.y, wh.x, wh.y, nw, ne, se, sw, clockwise)
|
|
||||||
|
|
||||||
proc roundedRect*(
|
proc roundedRect*(
|
||||||
path: Path, rect: Rect, nw, ne, se, sw: float32, clockwise = true
|
path: Path, rect: Rect, nw, ne, se, sw: float32, clockwise = true
|
||||||
) {.inline.} =
|
) {.inline.} =
|
||||||
|
@ -627,10 +610,6 @@ proc circle*(path: Path, cx, cy, r: float32) {.inline.} =
|
||||||
## Adds a circle.
|
## Adds a circle.
|
||||||
path.ellipse(cx, cy, r, r)
|
path.ellipse(cx, cy, r, r)
|
||||||
|
|
||||||
proc circle*(path: Path, center: Vec2, r: float32) {.inline.} =
|
|
||||||
## Adds a circle.
|
|
||||||
path.ellipse(center.x, center.y, r, r)
|
|
||||||
|
|
||||||
proc circle*(path: Path, circle: Circle) {.inline.} =
|
proc circle*(path: Path, circle: Circle) {.inline.} =
|
||||||
## Adds a circle.
|
## Adds a circle.
|
||||||
path.ellipse(circle.pos.x, circle.pos.y, circle.radius, circle.radius)
|
path.ellipse(circle.pos.x, circle.pos.y, circle.radius, circle.radius)
|
||||||
|
@ -648,7 +627,7 @@ proc polygon*(path: Path, pos: Vec2, size: float32, sides: int) {.inline.} =
|
||||||
## Adds a n-sided regular polygon at (x, y) with the parameter size.
|
## Adds a n-sided regular polygon at (x, y) with the parameter size.
|
||||||
path.polygon(pos.x, pos.y, size, sides)
|
path.polygon(pos.x, pos.y, size, sides)
|
||||||
|
|
||||||
proc commandsToShapes*(
|
proc commandsToShapes(
|
||||||
path: Path, closeSubpaths = false, pixelScale: float32 = 1.0
|
path: Path, closeSubpaths = false, pixelScale: float32 = 1.0
|
||||||
): seq[seq[Vec2]] =
|
): seq[seq[Vec2]] =
|
||||||
## Converts SVG-like commands to sequences of vectors.
|
## Converts SVG-like commands to sequences of vectors.
|
||||||
|
@ -1750,22 +1729,16 @@ proc parseSomePath(
|
||||||
elif type(path) is Path:
|
elif type(path) is Path:
|
||||||
path.commandsToShapes(closeSubpaths, pixelScale)
|
path.commandsToShapes(closeSubpaths, pixelScale)
|
||||||
|
|
||||||
proc transform(shapes: var seq[seq[Vec2]], transform: Vec2 | Mat3) =
|
proc transform(shapes: var seq[seq[Vec2]], transform: Mat3) =
|
||||||
when type(transform) is Vec2:
|
if transform != mat3():
|
||||||
if transform != vec2():
|
for shape in shapes.mitems:
|
||||||
for shape in shapes.mitems:
|
for segment in shape.mitems:
|
||||||
for segment in shape.mitems:
|
segment = transform * segment
|
||||||
segment += transform
|
|
||||||
else:
|
|
||||||
if transform != mat3():
|
|
||||||
for shape in shapes.mitems:
|
|
||||||
for segment in shape.mitems:
|
|
||||||
segment = transform * segment
|
|
||||||
|
|
||||||
proc fillPath*(
|
proc fillPath*(
|
||||||
mask: Mask,
|
mask: Mask,
|
||||||
path: SomePath,
|
path: SomePath,
|
||||||
transform: Vec2 | Mat3 = vec2(),
|
transform = mat3(),
|
||||||
windingRule = wrNonZero,
|
windingRule = wrNonZero,
|
||||||
blendMode = bmNormal
|
blendMode = bmNormal
|
||||||
) =
|
) =
|
||||||
|
@ -1778,7 +1751,7 @@ proc fillPath*(
|
||||||
image: Image,
|
image: Image,
|
||||||
path: SomePath,
|
path: SomePath,
|
||||||
paint: Paint,
|
paint: Paint,
|
||||||
transform: Vec2 | Mat3 = vec2(),
|
transform = mat3(),
|
||||||
windingRule = wrNonZero
|
windingRule = wrNonZero
|
||||||
) =
|
) =
|
||||||
## Fills a path.
|
## Fills a path.
|
||||||
|
@ -1827,7 +1800,7 @@ proc fillPath*(
|
||||||
proc strokePath*(
|
proc strokePath*(
|
||||||
mask: Mask,
|
mask: Mask,
|
||||||
path: SomePath,
|
path: SomePath,
|
||||||
transform: Vec2 | Mat3 = vec2(),
|
transform = mat3(),
|
||||||
strokeWidth = 1.0,
|
strokeWidth = 1.0,
|
||||||
lineCap = lcButt,
|
lineCap = lcButt,
|
||||||
lineJoin = ljMiter,
|
lineJoin = ljMiter,
|
||||||
|
@ -1851,7 +1824,7 @@ proc strokePath*(
|
||||||
image: Image,
|
image: Image,
|
||||||
path: SomePath,
|
path: SomePath,
|
||||||
paint: Paint,
|
paint: Paint,
|
||||||
transform: Vec2 | Mat3 = vec2(),
|
transform = mat3(),
|
||||||
strokeWidth = 1.0,
|
strokeWidth = 1.0,
|
||||||
lineCap = lcButt,
|
lineCap = lcButt,
|
||||||
lineJoin = ljMiter,
|
lineJoin = ljMiter,
|
||||||
|
@ -1948,7 +1921,7 @@ proc overlaps(
|
||||||
proc fillOverlaps*(
|
proc fillOverlaps*(
|
||||||
path: Path,
|
path: Path,
|
||||||
test: Vec2,
|
test: Vec2,
|
||||||
transform: Vec2 | Mat3 = vec2(), ## Applied to the path, not the test point.
|
transform = mat3(), ## Applied to the path, not the test point.
|
||||||
windingRule = wrNonZero
|
windingRule = wrNonZero
|
||||||
): bool =
|
): bool =
|
||||||
## Returns whether or not the specified point is contained in the current path.
|
## Returns whether or not the specified point is contained in the current path.
|
||||||
|
@ -1959,7 +1932,7 @@ proc fillOverlaps*(
|
||||||
proc strokeOverlaps*(
|
proc strokeOverlaps*(
|
||||||
path: Path,
|
path: Path,
|
||||||
test: Vec2,
|
test: Vec2,
|
||||||
transform: Vec2 | Mat3 = vec2(), ## Applied to the path, not the test point.
|
transform = mat3(), ## Applied to the path, not the test point.
|
||||||
strokeWidth = 1.0,
|
strokeWidth = 1.0,
|
||||||
lineCap = lcButt,
|
lineCap = lcButt,
|
||||||
lineJoin = ljMiter,
|
lineJoin = ljMiter,
|
||||||
|
|
|
@ -17,6 +17,14 @@ timeIt "fill_rgba":
|
||||||
image.fill(rgba(63, 127, 191, 191))
|
image.fill(rgba(63, 127, 191, 191))
|
||||||
doAssert image[0, 0] == rgba(63, 127, 191, 191)
|
doAssert image[0, 0] == rgba(63, 127, 191, 191)
|
||||||
|
|
||||||
|
image.fill(rgba(100, 0, 100, 100))
|
||||||
|
timeIt "isOneColor":
|
||||||
|
doAssert image.isOneColor()
|
||||||
|
|
||||||
|
image.fill(rgba(0, 0, 0, 0))
|
||||||
|
timeIt "isTransparent":
|
||||||
|
doAssert image.isTransparent()
|
||||||
|
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
timeIt "subImage":
|
timeIt "subImage":
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 519 B After Width: | Height: | Size: 520 B |
|
@ -154,7 +154,7 @@ block:
|
||||||
let image = newImage(200, 100)
|
let image = newImage(200, 100)
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.fillText(font, "First line")
|
image.fillText(font, "First line")
|
||||||
image.fillText(font, "Second line", vec2(0, font.defaultLineHeight))
|
image.fillText(font, "Second line", translate(vec2(0, font.defaultLineHeight)))
|
||||||
|
|
||||||
doDiff(image, "basic7")
|
doDiff(image, "basic7")
|
||||||
|
|
||||||
|
@ -826,7 +826,7 @@ block:
|
||||||
|
|
||||||
let arrangement = typeset(spans, bounds = vec2(360, 360))
|
let arrangement = typeset(spans, bounds = vec2(360, 360))
|
||||||
|
|
||||||
image.fillText(arrangement, vec2(20, 20))
|
image.fillText(arrangement, translate(vec2(20, 20)))
|
||||||
|
|
||||||
doDiff(image, "spans5")
|
doDiff(image, "spans5")
|
||||||
|
|
||||||
|
@ -965,7 +965,7 @@ block:
|
||||||
|
|
||||||
let arrangement = typeset(spans, bounds = vec2(360, 360))
|
let arrangement = typeset(spans, bounds = vec2(360, 360))
|
||||||
|
|
||||||
image.fillText(arrangement, vec2(20, 20))
|
image.fillText(arrangement, translate(vec2(20, 20)))
|
||||||
|
|
||||||
doDiff(image, "spans6")
|
doDiff(image, "spans6")
|
||||||
|
|
||||||
|
|
|
@ -164,3 +164,36 @@ block:
|
||||||
image.fillPath(p, rgba(255, 0, 0, 255))
|
image.fillPath(p, rgba(255, 0, 0, 255))
|
||||||
|
|
||||||
newImage(newMask(image)).writeFile("tests/images/mask2image.png")
|
newImage(newMask(image)).writeFile("tests/images/mask2image.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(100, 100)
|
||||||
|
doAssert image.isOneColor()
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(100, 100)
|
||||||
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
|
doAssert image.isOneColor()
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(100, 100)
|
||||||
|
image.fill(rgba(1, 2, 3, 4))
|
||||||
|
doAssert image.isOneColor()
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(100, 100)
|
||||||
|
image[99, 99] = rgba(255, 255, 255, 255)
|
||||||
|
doAssert not image.isOneColor()
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(100, 100)
|
||||||
|
doAssert image.isTransparent()
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(100, 100)
|
||||||
|
image.fill(rgba(255, 255, 255, 0))
|
||||||
|
doAssert image.isTransparent()
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(100, 100)
|
||||||
|
image[99, 99] = rgba(255, 255, 255, 255)
|
||||||
|
doAssert not image.isTransparent()
|
||||||
|
|
|
@ -213,7 +213,9 @@ block:
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20 Z")
|
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20 Z")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcRound, ljRound)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcRound, ljRound
|
||||||
|
)
|
||||||
|
|
||||||
image.writeFile("tests/images/paths/boxRound.png")
|
image.writeFile("tests/images/paths/boxRound.png")
|
||||||
|
|
||||||
|
@ -222,7 +224,9 @@ block:
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20 Z")
|
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20 Z")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcRound, ljBevel)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcRound, ljBevel
|
||||||
|
)
|
||||||
|
|
||||||
image.writeFile("tests/images/paths/boxBevel.png")
|
image.writeFile("tests/images/paths/boxBevel.png")
|
||||||
|
|
||||||
|
@ -231,7 +235,9 @@ block:
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20 Z")
|
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20 Z")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcRound, ljMiter)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcRound, ljMiter
|
||||||
|
)
|
||||||
|
|
||||||
image.writeFile("tests/images/paths/boxMiter.png")
|
image.writeFile("tests/images/paths/boxMiter.png")
|
||||||
|
|
||||||
|
@ -240,7 +246,9 @@ block:
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20")
|
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcButt, ljBevel)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcButt, ljBevel
|
||||||
|
)
|
||||||
|
|
||||||
image.writeFile("tests/images/paths/lcButt.png")
|
image.writeFile("tests/images/paths/lcButt.png")
|
||||||
|
|
||||||
|
@ -249,7 +257,9 @@ block:
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20")
|
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcRound, ljBevel)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcRound, ljBevel
|
||||||
|
)
|
||||||
|
|
||||||
image.writeFile("tests/images/paths/lcRound.png")
|
image.writeFile("tests/images/paths/lcRound.png")
|
||||||
|
|
||||||
|
@ -258,7 +268,9 @@ block:
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20")
|
path = parsePath("M 3 3 L 20 3 L 20 20 L 3 20")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljBevel)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcSquare, ljBevel
|
||||||
|
)
|
||||||
|
|
||||||
image.writeFile("tests/images/paths/lcSquare.png")
|
image.writeFile("tests/images/paths/lcSquare.png")
|
||||||
|
|
||||||
|
@ -269,31 +281,31 @@ block:
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
|
|
||||||
image.strokePath(
|
image.strokePath(
|
||||||
path, rgba(0, 0, 0, 255), vec2(5, 5), 10, lcButt, ljBevel,
|
path, rgba(0, 0, 0, 255), translate(vec2(5, 5)), 10, lcButt, ljBevel,
|
||||||
)
|
)
|
||||||
|
|
||||||
image.strokePath(
|
image.strokePath(
|
||||||
path, rgba(0, 0, 0, 255), vec2(5, 25), 10, lcButt, ljBevel,
|
path, rgba(0, 0, 0, 255), translate(vec2(5, 25)), 10, lcButt, ljBevel,
|
||||||
dashes = @[2.float32, 2]
|
dashes = @[2.float32, 2]
|
||||||
)
|
)
|
||||||
|
|
||||||
image.strokePath(
|
image.strokePath(
|
||||||
path, rgba(0, 0, 0, 255), vec2(5, 45), 10, lcButt, ljBevel,
|
path, rgba(0, 0, 0, 255), translate(vec2(5, 45)), 10, lcButt, ljBevel,
|
||||||
dashes = @[4.float32, 4]
|
dashes = @[4.float32, 4]
|
||||||
)
|
)
|
||||||
|
|
||||||
image.strokePath(
|
image.strokePath(
|
||||||
path, rgba(0, 0, 0, 255), vec2(5, 65), 10, lcButt, ljBevel,
|
path, rgba(0, 0, 0, 255), translate(vec2(5, 65)), 10, lcButt, ljBevel,
|
||||||
dashes = @[2.float32, 4, 6, 2]
|
dashes = @[2.float32, 4, 6, 2]
|
||||||
)
|
)
|
||||||
|
|
||||||
image.strokePath(
|
image.strokePath(
|
||||||
path, rgba(0, 0, 0, 255), vec2(5, 85), 10, lcButt, ljBevel,
|
path, rgba(0, 0, 0, 255), translate(vec2(5, 85)), 10, lcButt, ljBevel,
|
||||||
dashes = @[1.float32]
|
dashes = @[1.float32]
|
||||||
)
|
)
|
||||||
|
|
||||||
image.strokePath(
|
image.strokePath(
|
||||||
path, rgba(0, 0, 0, 255), vec2(5, 105), 10, lcButt, ljBevel,
|
path, rgba(0, 0, 0, 255), translate(vec2(5, 105)), 10, lcButt, ljBevel,
|
||||||
dashes = @[1.float32, 2, 3, 4, 5, 6, 7, 8, 9]
|
dashes = @[1.float32, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -311,7 +323,7 @@ block:
|
||||||
path.lineTo(sin(th)*20, cos(th)*20)
|
path.lineTo(sin(th)*20, cos(th)*20)
|
||||||
|
|
||||||
image.strokePath(
|
image.strokePath(
|
||||||
path, rgba(0, 0, 0, 255), vec2(30, 30), 8, lcButt, ljMiter,
|
path, rgba(0, 0, 0, 255), translate(vec2(30, 30)), 8, lcButt, ljMiter,
|
||||||
miterLimit = limit
|
miterLimit = limit
|
||||||
)
|
)
|
||||||
image.writeFile(&"tests/images/paths/miterLimit_{angle.int}deg_{limit:0.2f}num.png")
|
image.writeFile(&"tests/images/paths/miterLimit_{angle.int}deg_{limit:0.2f}num.png")
|
||||||
|
@ -340,28 +352,36 @@ block:
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("M 3 3 L 3 3 L 3 3")
|
path = parsePath("M 3 3 L 3 3 L 3 3")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljMiter)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcSquare, ljMiter
|
||||||
|
)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("L 0 0 L 0 0")
|
path = parsePath("L 0 0 L 0 0")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljMiter)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcSquare, ljMiter
|
||||||
|
)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("L 1 1")
|
path = parsePath("L 1 1")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljMiter)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcSquare, ljMiter
|
||||||
|
)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
image = newImage(60, 60)
|
image = newImage(60, 60)
|
||||||
path = parsePath("L 0 0")
|
path = parsePath("L 0 0")
|
||||||
image.fill(rgba(255, 255, 255, 255))
|
image.fill(rgba(255, 255, 255, 255))
|
||||||
image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljMiter)
|
image.strokePath(
|
||||||
|
path, rgba(0, 0, 0, 255), translate(vec2(10, 10)), 10, lcSquare, ljMiter
|
||||||
|
)
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let image = newImage(100, 100)
|
let image = newImage(100, 100)
|
||||||
|
|
Loading…
Reference in a new issue