Merge pull request #231 from treeform/masking

Fix masking, remove Intersect Mask.
This commit is contained in:
treeform 2021-06-18 16:51:05 -07:00 committed by GitHub
commit 54954d26b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 11 deletions

View file

@ -32,7 +32,6 @@ type
bmMask ## Special blend mode that is used for masking bmMask ## Special blend mode that is used for masking
bmOverwrite ## Special blend mode that just copies pixels bmOverwrite ## Special blend mode that just copies pixels
bmSubtractMask ## Inverse mask bmSubtractMask ## Inverse mask
bmIntersectMask
bmExcludeMask bmExcludeMask
Blender* = proc(backdrop, source: ColorRGBX): ColorRGBX Blender* = proc(backdrop, source: ColorRGBX): ColorRGBX
@ -441,9 +440,9 @@ proc blendIntersectMask(backdrop, source: ColorRGBX): ColorRGBX =
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 = ((backdrop.r * a) div 255).uint8 result.r = ((source.r * a) div 255).uint8
result.g = ((backdrop.g * a) div 255).uint8 result.g = ((source.g * a) div 255).uint8
result.b = ((backdrop.b * a) div 255).uint8 result.b = ((source.b * a) div 255).uint8
result.a = a.uint8 result.a = a.uint8
proc blendOverwrite(backdrop, source: ColorRGBX): ColorRGBX = proc blendOverwrite(backdrop, source: ColorRGBX): ColorRGBX =
@ -477,7 +476,6 @@ proc blender*(blendMode: BlendMode): Blender =
of bmMask: blendMask of bmMask: blendMask
of bmOverwrite: blendOverwrite of bmOverwrite: blendOverwrite
of bmSubtractMask: blendSubtractMask of bmSubtractMask: blendSubtractMask
of bmIntersectMask: blendIntersectMask
of bmExcludeMask: blendExcludeMask of bmExcludeMask: blendExcludeMask
proc maskNormal(backdrop, source: uint8): uint8 = proc maskNormal(backdrop, source: uint8): uint8 =
@ -507,7 +505,6 @@ proc masker*(blendMode: BlendMode): Masker =
of bmMask: maskMask of bmMask: maskMask
of bmOverwrite: maskOverwrite of bmOverwrite: maskOverwrite
of bmSubtractMask: maskSubtract of bmSubtractMask: maskSubtract
of bmIntersectMask: maskIntersect
of bmExcludeMask: maskExclude of bmExcludeMask: maskExclude
else: else:
raise newException(PixieError, "No masker for " & $blendMode) raise newException(PixieError, "No masker for " & $blendMode)

View file

@ -630,7 +630,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) =
# Determine where we should start and stop drawing in the y dimension # Determine where we should start and stop drawing in the y dimension
var yMin, yMax: int var yMin, yMax: int
if blendMode == bmIntersectMask: if blendMode == bmMask:
yMin = 0 yMin = 0
yMax = a.height yMax = a.height
else: else:
@ -662,7 +662,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) =
xMin = xMin.clamp(0, a.width) xMin = xMin.clamp(0, a.width)
xMax = xMax.clamp(0, a.width) xMax = xMax.clamp(0, a.width)
if blendMode == bmIntersectMask: if blendMode == bmMask:
if xMin > 0: if xMin > 0:
zeroMem(a.data[a.dataIndex(0, y)].addr, 4 * xMin) zeroMem(a.data[a.dataIndex(0, y)].addr, 4 * xMin)
@ -780,7 +780,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) =
srcPos += dx srcPos += dx
if blendMode == bmIntersectMask: if blendMode == bmMask:
if a.width - xMax > 0: if a.width - xMax > 0:
zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax)) zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax))

View file

@ -1226,6 +1226,14 @@ 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) =
## From startXY to toXY, exclusive, toXY is not cleared.
image.data.fillUnsafe(
rgbx(0, 0, 0, 0),
image.dataIndex(startX, startY),
image.dataIndex(toX, toY) - image.dataIndex(startX, startY)
)
proc fillCoverage( proc fillCoverage(
image: Image, image: Image,
rgbx: ColorRGBX, rgbx: ColorRGBX,
@ -1291,7 +1299,7 @@ proc fillCoverage(
let blender = blendMode.blender() let blender = blendMode.blender()
while x < image.width: while x < image.width:
let coverage = coverages[x] let coverage = coverages[x]
if coverage != 0: if coverage != 0 or blendMode == bmExcludeMask:
if blendMode == bmNormal and coverage == 255 and rgbx.a == 255: if blendMode == bmNormal and coverage == 255 and rgbx.a == 255:
# Skip blending # Skip blending
image.setRgbaUnsafe(x, y, rgbx) image.setRgbaUnsafe(x, y, rgbx)
@ -1304,8 +1312,13 @@ proc fillCoverage(
source.a = ((source.a.uint32 * coverage) div 255).uint8 source.a = ((source.a.uint32 * coverage) div 255).uint8
let backdrop = image.getRgbaUnsafe(x, y) let backdrop = image.getRgbaUnsafe(x, y)
image.setRgbaUnsafe(x, y, blender(backdrop, source)) image.setRgbaUnsafe(x, y, blender(backdrop, source))
elif blendMode == bmMask:
image.setRgbaUnsafe(x, y, rgbx(0, 0, 0, 0))
inc x inc x
if blendMode == bmMask:
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]) =
var x = startX var x = startX
when defined(amd64) and not defined(pixieNoSimd): when defined(amd64) and not defined(pixieNoSimd):
@ -1341,6 +1354,7 @@ proc fillHits(
blendMode: BlendMode blendMode: BlendMode
) = ) =
let blender = blendMode.blender() let blender = blendMode.blender()
var x = 0
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
@ -1349,7 +1363,7 @@ proc fillHits(
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: else:
var x = fillStart 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
@ -1370,6 +1384,10 @@ proc fillHits(
image.setRgbaUnsafe(x, y, blender(backdrop, rgbx)) image.setRgbaUnsafe(x, y, blender(backdrop, rgbx))
inc x inc x
if blendMode == bmMask:
image.clearUnsafe(0, y, startX, y)
image.clearUnsafe(x, y, image.width, y)
proc fillHits( proc fillHits(
mask: Mask, mask: Mask,
startX, y: int, startX, y: int,
@ -1438,6 +1456,10 @@ proc fillShapes(
blendMode blendMode
) )
if blendMode == bmMask:
image.clearUnsafe(0, 0, 0, startY)
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) =
# 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

View file

@ -363,3 +363,51 @@ block:
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), vec2(10, 10), 10, lcSquare, ljMiter)
block:
let image = newImage(100, 100)
image.fillPath(
"M 10 10 H 60 V 60 H 10 z",
Paint(kind: pkSolid, color: rgbx(255, 0, 0, 255), blendMode: bmNormal)
)
image.fillPath(
"M 30 30 H 80 V 80 H 30 z",
Paint(kind: pkSolid, color: rgbx(0, 255, 0, 255), blendMode: bmExcludeMask)
)
image.writeFile("tests/images/paths/rectExcludeMask.png")
block:
let image = newImage(100, 100)
image.fillPath(
"M 10.1 10.1 H 60.1 V 60.1 H 10.1 z",
Paint(kind: pkSolid, color: rgbx(255, 0, 0, 255), blendMode: bmNormal)
)
image.fillPath(
"M 30.1 30.1 H 80.1 V 80.1 H 30.1 z",
Paint(kind: pkSolid, color: rgbx(0, 255, 0, 255), blendMode: bmExcludeMask)
)
image.writeFile("tests/images/paths/rectExcludeMaskAA.png")
block:
let image = newImage(100, 100)
image.fillPath(
"M 10 10 H 60 V 60 H 10 z",
Paint(kind: pkSolid, color: rgbx(255, 0, 0, 255), blendMode: bmNormal)
)
image.fillPath(
"M 30 30 H 80 V 80 H 30 z",
Paint(kind: pkSolid, color: rgbx(0, 255, 0, 255), blendMode: bmMask)
)
image.writeFile("tests/images/paths/rectMask.png")
block:
let image = newImage(100, 100)
image.fillPath(
"M 10.1 10.1 H 60.1 V 60.1 H 10.1 z",
Paint(kind: pkSolid, color: rgbx(255, 0, 0, 255), blendMode: bmNormal)
)
image.fillPath(
"M 30.1 30.1 H 80.1 V 80.1 H 30.1 z",
Paint(kind: pkSolid, color: rgbx(0, 255, 0, 255), blendMode: bmMask)
)
image.writeFile("tests/images/paths/rectMaskAA.png")