paths.nim masks take blendMode for masking, tests, fixes and etc
|
@ -1,4 +1,4 @@
|
||||||
version = "2.0.5"
|
version = "2.1.0"
|
||||||
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"
|
||||||
|
|
|
@ -435,9 +435,6 @@ proc blendSubtractMask(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
result.b = ((backdrop.b * a) div 255).uint8
|
result.b = ((backdrop.b * a) div 255).uint8
|
||||||
result.a = a.uint8
|
result.a = a.uint8
|
||||||
|
|
||||||
proc blendIntersectMask(backdrop, source: ColorRGBX): ColorRGBX =
|
|
||||||
blendMask(backdrop, source)
|
|
||||||
|
|
||||||
proc blendExcludeMask(backdrop, source: ColorRGBX): ColorRGBX =
|
proc blendExcludeMask(backdrop, source: ColorRGBX): ColorRGBX =
|
||||||
let a = max(backdrop.a, source.a).uint32 - min(backdrop.a, source.a)
|
let a = max(backdrop.a, source.a).uint32 - min(backdrop.a, source.a)
|
||||||
result.r = ((source.r * a) div 255).uint8
|
result.r = ((source.r * a) div 255).uint8
|
||||||
|
@ -489,9 +486,6 @@ proc maskMask(backdrop, source: uint8): uint8 =
|
||||||
proc maskSubtract(backdrop, source: uint8): uint8 =
|
proc maskSubtract(backdrop, source: uint8): uint8 =
|
||||||
((backdrop.uint32 * (255 - source)) div 255).uint8
|
((backdrop.uint32 * (255 - source)) div 255).uint8
|
||||||
|
|
||||||
proc maskIntersect(backdrop, source: uint8): uint8 =
|
|
||||||
maskMask(backdrop, source)
|
|
||||||
|
|
||||||
proc maskExclude(backdrop, source: uint8): uint8 =
|
proc maskExclude(backdrop, source: uint8): uint8 =
|
||||||
max(backdrop, source) - min(backdrop, source)
|
max(backdrop, source) - min(backdrop, source)
|
||||||
|
|
||||||
|
@ -592,7 +586,7 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
div255 = mm_set1_epi16(cast[int16](0x8081))
|
div255 = mm_set1_epi16(cast[int16](0x8081))
|
||||||
|
|
||||||
var
|
var
|
||||||
sourceEven = mm_slli_epi16(mm_andnot_si128(oddMask, source), 8)
|
sourceEven = mm_slli_epi16(source, 8)
|
||||||
sourceOdd = mm_and_si128(source, oddMask)
|
sourceOdd = mm_and_si128(source, oddMask)
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -600,7 +594,7 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
oddK = mm_sub_epi16(v255high, sourceOdd)
|
oddK = mm_sub_epi16(v255high, sourceOdd)
|
||||||
|
|
||||||
var
|
var
|
||||||
backdropEven = mm_slli_epi16(mm_andnot_si128(oddMask, backdrop), 8)
|
backdropEven = mm_slli_epi16(backdrop, 8)
|
||||||
backdropOdd = mm_and_si128(backdrop, oddMask)
|
backdropOdd = mm_and_si128(backdrop, oddMask)
|
||||||
|
|
||||||
# backdrop * k
|
# backdrop * k
|
||||||
|
@ -625,11 +619,11 @@ when defined(amd64) and not defined(pixieNoSimd):
|
||||||
let
|
let
|
||||||
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
oddMask = mm_set1_epi16(cast[int16](0xff00))
|
||||||
div255 = mm_set1_epi16(cast[int16](0x8081))
|
div255 = mm_set1_epi16(cast[int16](0x8081))
|
||||||
sourceEven = mm_slli_epi16(mm_andnot_si128(oddMask, source), 8)
|
sourceEven = mm_slli_epi16(source, 8)
|
||||||
sourceOdd = mm_and_si128(source, oddMask)
|
sourceOdd = mm_and_si128(source, oddMask)
|
||||||
|
|
||||||
var
|
var
|
||||||
backdropEven = mm_slli_epi16(mm_andnot_si128(oddMask, backdrop), 8)
|
backdropEven = mm_slli_epi16(backdrop, 8)
|
||||||
backdropOdd = mm_and_si128(backdrop, oddMask)
|
backdropOdd = mm_and_si128(backdrop, oddMask)
|
||||||
|
|
||||||
# backdrop * source
|
# backdrop * source
|
||||||
|
|
|
@ -1137,7 +1137,9 @@ iterator walk(
|
||||||
# between zero and nonzero (or the last hit)
|
# between zero and nonzero (or the last hit)
|
||||||
count += winding
|
count += winding
|
||||||
continue
|
continue
|
||||||
if at > 0:
|
if at <= 0:
|
||||||
|
count += winding
|
||||||
|
continue
|
||||||
if shouldFill(windingRule, count):
|
if shouldFill(windingRule, count):
|
||||||
yield (prevAt, at, count)
|
yield (prevAt, at, count)
|
||||||
prevAt = at
|
prevAt = at
|
||||||
|
@ -1226,13 +1228,15 @@ proc computeCoverages(
|
||||||
for j in i ..< fillStart + fillLen:
|
for j in i ..< fillStart + fillLen:
|
||||||
coverages[j] += sampleCoverage
|
coverages[j] += sampleCoverage
|
||||||
|
|
||||||
proc clearUnsafe(image: Image, startX, startY, toX, toY: int) =
|
proc clearUnsafe(target: Image | Mask, startX, startY, toX, toY: int) =
|
||||||
## From startXY to toXY, exclusive, toXY is not cleared.
|
## Clears data from [start, to).
|
||||||
image.data.fillUnsafe(
|
let
|
||||||
rgbx(0, 0, 0, 0),
|
start = target.dataIndex(startX, startY)
|
||||||
image.dataIndex(startX, startY),
|
len = target.dataIndex(toX, toY) - start
|
||||||
image.dataIndex(toX, toY) - image.dataIndex(startX, startY)
|
when type(target) is Image:
|
||||||
)
|
target.data.fillUnsafe(rgbx(0, 0, 0, 0), start, len)
|
||||||
|
else: # target is Mask
|
||||||
|
target.data.fillUnsafe(0, start, len)
|
||||||
|
|
||||||
proc fillCoverage(
|
proc fillCoverage(
|
||||||
image: Image,
|
image: Image,
|
||||||
|
@ -1258,7 +1262,7 @@ proc fillCoverage(
|
||||||
let
|
let
|
||||||
index = image.dataIndex(x, y)
|
index = image.dataIndex(x, y)
|
||||||
eqZero = mm_cmpeq_epi16(coverage, mm_setzero_si128())
|
eqZero = mm_cmpeq_epi16(coverage, mm_setzero_si128())
|
||||||
if mm_movemask_epi8(eqZero) != 0xffff:
|
if mm_movemask_epi8(eqZero) != 0xffff: # or blendMode == bmExcludeMask:
|
||||||
# If the coverages are not all zero
|
# If the coverages are not all zero
|
||||||
if mm_movemask_epi8(mm_cmpeq_epi32(coverage, first32)) == 0xffff:
|
if mm_movemask_epi8(mm_cmpeq_epi32(coverage, first32)) == 0xffff:
|
||||||
# Coverages are all 255
|
# Coverages are all 255
|
||||||
|
@ -1294,6 +1298,8 @@ proc fillCoverage(
|
||||||
image.data[index].addr,
|
image.data[index].addr,
|
||||||
blenderSimd(backdrop, source)
|
blenderSimd(backdrop, source)
|
||||||
)
|
)
|
||||||
|
elif blendMode == bmMask:
|
||||||
|
mm_storeu_si128(image.data[index].addr, mm_setzero_si128())
|
||||||
x += 4
|
x += 4
|
||||||
|
|
||||||
let blender = blendMode.blender()
|
let blender = blendMode.blender()
|
||||||
|
@ -1319,31 +1325,45 @@ proc fillCoverage(
|
||||||
if blendMode == bmMask:
|
if blendMode == bmMask:
|
||||||
image.clearUnsafe(0, y, startX, y)
|
image.clearUnsafe(0, y, startX, y)
|
||||||
|
|
||||||
proc fillCoverage(mask: Mask, startX, y: int, coverages: seq[uint8]) =
|
proc fillCoverage(
|
||||||
|
mask: Mask,
|
||||||
|
startX, y: int,
|
||||||
|
coverages: seq[uint8],
|
||||||
|
blendMode: BlendMode
|
||||||
|
) =
|
||||||
var x = startX
|
var x = startX
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
# When supported, SIMD blend as much as possible
|
if blendMode.hasSimdMasker():
|
||||||
let maskerSimd = bmNormal.maskerSimd()
|
let maskerSimd = blendMode.maskerSimd()
|
||||||
for _ in countup(x, coverages.len - 16, 16):
|
for _ in countup(x, coverages.len - 16, 16):
|
||||||
let
|
let
|
||||||
|
index = mask.dataIndex(x, y)
|
||||||
coverage = mm_loadu_si128(coverages[x].unsafeAddr)
|
coverage = mm_loadu_si128(coverages[x].unsafeAddr)
|
||||||
eqZero = mm_cmpeq_epi16(coverage, mm_setzero_si128())
|
eqZero = mm_cmpeq_epi16(coverage, mm_setzero_si128())
|
||||||
if mm_movemask_epi8(eqZero) != 0xffff:
|
if mm_movemask_epi8(eqZero) != 0xffff: # or blendMode == bmExcludeMask:
|
||||||
# If the coverages are not all zero
|
# If the coverages are not all zero
|
||||||
let backdrop = mm_loadu_si128(mask.data[mask.dataIndex(x, y)].addr)
|
let backdrop = mm_loadu_si128(mask.data[index].addr)
|
||||||
mm_storeu_si128(
|
mm_storeu_si128(
|
||||||
mask.data[mask.dataIndex(x, y)].addr,
|
mask.data[index].addr,
|
||||||
maskerSimd(backdrop, coverage)
|
maskerSimd(backdrop, coverage)
|
||||||
)
|
)
|
||||||
|
elif blendMode == bmMask:
|
||||||
|
mm_storeu_si128(mask.data[index].addr, mm_setzero_si128())
|
||||||
x += 16
|
x += 16
|
||||||
|
|
||||||
|
let masker = blendMode.masker()
|
||||||
while x < mask.width:
|
while x < mask.width:
|
||||||
let coverage = coverages[x]
|
let coverage = coverages[x]
|
||||||
if coverage != 0:
|
if coverage != 0 or blendMode == bmExcludeMask:
|
||||||
let backdrop = mask.getValueUnsafe(x, y)
|
let backdrop = mask.getValueUnsafe(x, y)
|
||||||
mask.setValueUnsafe(x, y, blendAlpha(backdrop, coverage))
|
mask.setValueUnsafe(x, y, masker(backdrop, coverage))
|
||||||
|
elif blendMode == bmMask:
|
||||||
|
mask.setValueUnsafe(x, y, 0)
|
||||||
inc x
|
inc x
|
||||||
|
|
||||||
|
if blendMode == bmMask:
|
||||||
|
mask.clearUnsafe(0, y, startX, y)
|
||||||
|
|
||||||
proc fillHits(
|
proc fillHits(
|
||||||
image: Image,
|
image: Image,
|
||||||
rgbx: ColorRGBX,
|
rgbx: ColorRGBX,
|
||||||
|
@ -1354,16 +1374,21 @@ proc fillHits(
|
||||||
blendMode: BlendMode
|
blendMode: BlendMode
|
||||||
) =
|
) =
|
||||||
let blender = blendMode.blender()
|
let blender = blendMode.blender()
|
||||||
var x = 0
|
var filledTo: int
|
||||||
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, image.wh):
|
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, image.wh):
|
||||||
let
|
let
|
||||||
fillStart = prevAt.int
|
fillStart = prevAt.int
|
||||||
fillLen = at.int - fillStart
|
fillLen = at.int - fillStart
|
||||||
if fillLen > 0:
|
if fillLen <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
filledTo = fillStart + fillLen
|
||||||
|
|
||||||
if blendMode == bmNormal and rgbx.a == 255:
|
if blendMode == bmNormal and rgbx.a == 255:
|
||||||
fillUnsafe(image.data, rgbx, image.dataIndex(fillStart, y), fillLen)
|
fillUnsafe(image.data, rgbx, image.dataIndex(fillStart, y), fillLen)
|
||||||
else:
|
continue
|
||||||
x = fillStart
|
|
||||||
|
var x = fillStart
|
||||||
when defined(amd64) and not defined(pixieNoSimd):
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
if blendMode.hasSimdBlender():
|
if blendMode.hasSimdBlender():
|
||||||
# When supported, SIMD blend as much as possible
|
# When supported, SIMD blend as much as possible
|
||||||
|
@ -1379,28 +1404,59 @@ proc fillHits(
|
||||||
blenderSimd(backdrop, vColor)
|
blenderSimd(backdrop, vColor)
|
||||||
)
|
)
|
||||||
x += 4
|
x += 4
|
||||||
while x < fillStart + fillLen:
|
|
||||||
|
for x in x ..< fillStart + fillLen:
|
||||||
let backdrop = image.getRgbaUnsafe(x, y)
|
let backdrop = image.getRgbaUnsafe(x, y)
|
||||||
image.setRgbaUnsafe(x, y, blender(backdrop, rgbx))
|
image.setRgbaUnsafe(x, y, blender(backdrop, rgbx))
|
||||||
inc x
|
|
||||||
|
|
||||||
if blendMode == bmMask:
|
if blendMode == bmMask:
|
||||||
image.clearUnsafe(0, y, startX, y)
|
image.clearUnsafe(0, y, startX, y)
|
||||||
image.clearUnsafe(x, y, image.width, y)
|
image.clearUnsafe(filledTo, y, image.width, y)
|
||||||
|
|
||||||
proc fillHits(
|
proc fillHits(
|
||||||
mask: Mask,
|
mask: Mask,
|
||||||
startX, y: int,
|
startX, y: int,
|
||||||
hits: seq[(float32, int16)],
|
hits: seq[(float32, int16)],
|
||||||
numHits: int,
|
numHits: int,
|
||||||
windingRule: WindingRule
|
windingRule: WindingRule,
|
||||||
|
blendMode: BlendMode
|
||||||
) =
|
) =
|
||||||
|
let masker = blendMode.masker()
|
||||||
|
var filledTo: int
|
||||||
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, mask.wh):
|
for (prevAt, at, count) in hits.walk(numHits, windingRule, y, mask.wh):
|
||||||
let
|
let
|
||||||
fillStart = prevAt.int
|
fillStart = prevAt.int
|
||||||
fillLen = at.int - fillStart
|
fillLen = at.int - fillStart
|
||||||
if fillLen > 0:
|
if fillLen <= 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
filledTo = fillStart + fillLen
|
||||||
|
|
||||||
|
if blendMode == bmNormal:
|
||||||
fillUnsafe(mask.data, 255, mask.dataIndex(fillStart, y), fillLen)
|
fillUnsafe(mask.data, 255, mask.dataIndex(fillStart, y), fillLen)
|
||||||
|
continue
|
||||||
|
|
||||||
|
var x = fillStart
|
||||||
|
when defined(amd64) and not defined(pixieNoSimd):
|
||||||
|
if blendMode.hasSimdMasker():
|
||||||
|
let
|
||||||
|
maskerSimd = blendMode.maskerSimd()
|
||||||
|
vValue = mm_set1_epi8(cast[int8](255))
|
||||||
|
for _ in countup(fillStart, fillLen - 16, 16):
|
||||||
|
let backdrop = mm_loadu_si128(mask.data[mask.dataIndex(x, y)].addr)
|
||||||
|
mm_storeu_si128(
|
||||||
|
mask.data[mask.dataIndex(x, y)].addr,
|
||||||
|
maskerSimd(backdrop, vValue)
|
||||||
|
)
|
||||||
|
x += 16
|
||||||
|
|
||||||
|
for x in x ..< fillStart + fillLen:
|
||||||
|
let backdrop = mask.getValueUnsafe(x, y)
|
||||||
|
mask.setValueUnsafe(x, y, masker(backdrop, 255))
|
||||||
|
|
||||||
|
if blendMode == bmMask:
|
||||||
|
mask.clearUnsafe(0, y, startX, y)
|
||||||
|
mask.clearUnsafe(filledTo, y, mask.width, y)
|
||||||
|
|
||||||
proc fillShapes(
|
proc fillShapes(
|
||||||
image: Image,
|
image: Image,
|
||||||
|
@ -1460,7 +1516,12 @@ proc fillShapes(
|
||||||
image.clearUnsafe(0, 0, 0, startY)
|
image.clearUnsafe(0, 0, 0, startY)
|
||||||
image.clearUnsafe(0, pathHeight, 0, image.height)
|
image.clearUnsafe(0, pathHeight, 0, image.height)
|
||||||
|
|
||||||
proc fillShapes(mask: Mask, shapes: seq[seq[Vec2]], windingRule: WindingRule) =
|
proc fillShapes(
|
||||||
|
mask: Mask,
|
||||||
|
shapes: seq[seq[Vec2]],
|
||||||
|
windingRule: WindingRule,
|
||||||
|
blendMode: BlendMode
|
||||||
|
) =
|
||||||
# Figure out the total bounds of all the shapes,
|
# Figure out the total bounds of all the shapes,
|
||||||
# rasterize only within the total bounds
|
# rasterize only within the total bounds
|
||||||
let
|
let
|
||||||
|
@ -1469,8 +1530,7 @@ proc fillShapes(mask: Mask, shapes: seq[seq[Vec2]], windingRule: WindingRule) =
|
||||||
bounds = computePixelBounds(segments)
|
bounds = computePixelBounds(segments)
|
||||||
startX = max(0, bounds.x.int)
|
startX = max(0, bounds.x.int)
|
||||||
startY = max(0, bounds.y.int)
|
startY = max(0, bounds.y.int)
|
||||||
stopY = min(mask.height, (bounds.y + bounds.h).int)
|
pathHeight = min(mask.height, (bounds.y + bounds.h).int)
|
||||||
pathHeight = stopY - startY
|
|
||||||
partitioning = partitionSegments(segments, startY, pathHeight)
|
partitioning = partitionSegments(segments, startY, pathHeight)
|
||||||
|
|
||||||
var
|
var
|
||||||
|
@ -1478,7 +1538,7 @@ proc fillShapes(mask: Mask, shapes: seq[seq[Vec2]], windingRule: WindingRule) =
|
||||||
hits = newSeq[(float32, int16)](4)
|
hits = newSeq[(float32, int16)](4)
|
||||||
numHits: int
|
numHits: int
|
||||||
|
|
||||||
for y in startY ..< stopY:
|
for y in startY ..< pathHeight:
|
||||||
computeCoverages(
|
computeCoverages(
|
||||||
coverages,
|
coverages,
|
||||||
hits,
|
hits,
|
||||||
|
@ -1490,9 +1550,13 @@ proc fillShapes(mask: Mask, shapes: seq[seq[Vec2]], windingRule: WindingRule) =
|
||||||
windingRule
|
windingRule
|
||||||
)
|
)
|
||||||
if aa:
|
if aa:
|
||||||
mask.fillCoverage(startX, y, coverages)
|
mask.fillCoverage(startX, y, coverages, blendMode)
|
||||||
else:
|
else:
|
||||||
mask.fillHits(startX, y, hits, numHits, windingRule)
|
mask.fillHits(startX, y, hits, numHits, windingRule, blendMode)
|
||||||
|
|
||||||
|
if blendMode == bmMask:
|
||||||
|
mask.clearUnsafe(0, 0, 0, startY)
|
||||||
|
mask.clearUnsafe(0, pathHeight, 0, mask.height)
|
||||||
|
|
||||||
proc miterLimitToAngle*(limit: float32): float32 =
|
proc miterLimitToAngle*(limit: float32): float32 =
|
||||||
## Converts miter-limit-ratio to miter-limit-angle.
|
## Converts miter-limit-ratio to miter-limit-angle.
|
||||||
|
@ -1668,12 +1732,13 @@ proc fillPath*(
|
||||||
mask: Mask,
|
mask: Mask,
|
||||||
path: SomePath,
|
path: SomePath,
|
||||||
transform: Vec2 | Mat3 = vec2(),
|
transform: Vec2 | Mat3 = vec2(),
|
||||||
windingRule = wrNonZero
|
windingRule = wrNonZero,
|
||||||
|
blendMode = bmNormal
|
||||||
) =
|
) =
|
||||||
## Fills a path.
|
## Fills a path.
|
||||||
var shapes = parseSomePath(path, true, transform.pixelScale())
|
var shapes = parseSomePath(path, true, transform.pixelScale())
|
||||||
shapes.transform(transform)
|
shapes.transform(transform)
|
||||||
mask.fillShapes(shapes, windingRule)
|
mask.fillShapes(shapes, windingRule, blendMode)
|
||||||
|
|
||||||
proc fillPath*(
|
proc fillPath*(
|
||||||
image: Image,
|
image: Image,
|
||||||
|
@ -1721,7 +1786,8 @@ proc strokePath*(
|
||||||
lineCap = lcButt,
|
lineCap = lcButt,
|
||||||
lineJoin = ljMiter,
|
lineJoin = ljMiter,
|
||||||
miterLimit = defaultMiterLimit,
|
miterLimit = defaultMiterLimit,
|
||||||
dashes: seq[float32] = @[]
|
dashes: seq[float32] = @[],
|
||||||
|
blendMode = bmNormal
|
||||||
) =
|
) =
|
||||||
## Strokes a path.
|
## Strokes a path.
|
||||||
var strokeShapes = strokeShapes(
|
var strokeShapes = strokeShapes(
|
||||||
|
@ -1733,7 +1799,7 @@ proc strokePath*(
|
||||||
dashes
|
dashes
|
||||||
)
|
)
|
||||||
strokeShapes.transform(transform)
|
strokeShapes.transform(transform)
|
||||||
mask.fillShapes(strokeShapes, wrNonZero)
|
mask.fillShapes(strokeShapes, wrNonZero, blendMode)
|
||||||
|
|
||||||
proc strokePath*(
|
proc strokePath*(
|
||||||
image: Image,
|
image: Image,
|
||||||
|
|
Before Width: | Height: | Size: 208 B After Width: | Height: | Size: 232 B |
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 269 B |
Before Width: | Height: | Size: 208 B After Width: | Height: | Size: 188 B |
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 207 B |
|
@ -415,23 +415,23 @@ block:
|
||||||
block:
|
block:
|
||||||
let mask = newMask(100, 100)
|
let mask = newMask(100, 100)
|
||||||
mask.fillPath("M 10 10 H 60 V 60 H 10 z")
|
mask.fillPath("M 10 10 H 60 V 60 H 10 z")
|
||||||
mask.fillPath("M 30 30 H 80 V 80 H 30 z")#, blendMode = bmExcludeMask)
|
mask.fillPath("M 30 30 H 80 V 80 H 30 z", blendMode = bmExcludeMask)
|
||||||
writeFile("tests/images/paths/maskRectExcludeMask.png", mask.encodePng())
|
writeFile("tests/images/paths/maskRectExcludeMask.png", mask.encodePng())
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let mask = newMask(100, 100)
|
let mask = newMask(100, 100)
|
||||||
mask.fillPath("M 10.1 10.1 H 60.1 V 60.1 H 10.1 z")
|
mask.fillPath("M 10.1 10.1 H 60.1 V 60.1 H 10.1 z")
|
||||||
mask.fillPath("M 30.1 30.1 H 80.1 V 80.1 H 30.1 z")#, blendMode = bmExcludeMask)
|
mask.fillPath("M 30.1 30.1 H 80.1 V 80.1 H 30.1 z", blendMode = bmExcludeMask)
|
||||||
writeFile("tests/images/paths/maskRectExcludeMaskAA.png", mask.encodePng())
|
writeFile("tests/images/paths/maskRectExcludeMaskAA.png", mask.encodePng())
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let mask = newMask(100, 100)
|
let mask = newMask(100, 100)
|
||||||
mask.fillPath("M 10 10 H 60 V 60 H 10 z")
|
mask.fillPath("M 10 10 H 60 V 60 H 10 z")
|
||||||
mask.fillPath("M 30 30 H 80 V 80 H 30 z")#, blendMode = bmMask)
|
mask.fillPath("M 30 30 H 80 V 80 H 30 z", blendMode = bmMask)
|
||||||
writeFile("tests/images/paths/maskRectMask.png", mask.encodePng())
|
writeFile("tests/images/paths/maskRectMask.png", mask.encodePng())
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let mask = newMask(100, 100)
|
let mask = newMask(100, 100)
|
||||||
mask.fillPath("M 10.1 10.1 H 60.1 V 60.1 H 10.1 z")
|
mask.fillPath("M 10.1 10.1 H 60.1 V 60.1 H 10.1 z")
|
||||||
mask.fillPath("M 30.1 30.1 H 80.1 V 80.1 H 30.1 z")#, blendMode = bmMask)
|
mask.fillPath("M 30.1 30.1 H 80.1 V 80.1 H 30.1 z", blendMode = bmMask)
|
||||||
writeFile("tests/images/paths/maskRectMaskAA.png", mask.encodePng())
|
writeFile("tests/images/paths/maskRectMaskAA.png", mask.encodePng())
|
||||||
|
|