Merge pull request #231 from treeform/masking
Fix masking, remove Intersect Mask.
This commit is contained in:
commit
54954d26b5
8 changed files with 78 additions and 11 deletions
|
@ -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)
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
BIN
tests/images/paths/rectExcludeMask.png
Normal file
BIN
tests/images/paths/rectExcludeMask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 442 B |
BIN
tests/images/paths/rectExcludeMaskAA.png
Normal file
BIN
tests/images/paths/rectExcludeMaskAA.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 508 B |
BIN
tests/images/paths/rectMask.png
Normal file
BIN
tests/images/paths/rectMask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 351 B |
BIN
tests/images/paths/rectMaskAA.png
Normal file
BIN
tests/images/paths/rectMaskAA.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 372 B |
|
@ -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")
|
||||||
|
|
Loading…
Reference in a new issue