From b34f4260de9e8088d3721578df88184b8eea5913 Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 18 Jun 2021 15:19:37 -0700 Subject: [PATCH 1/5] Fix blending with bmMask. --- src/pixie/images.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 2ecbc0f..e0f9bf0 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -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 var yMin, yMax: int - if blendMode == bmIntersectMask: + if blendMode == bmMask: yMin = 0 yMax = a.height else: @@ -662,7 +662,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) = xMin = xMin.clamp(0, a.width) xMax = xMax.clamp(0, a.width) - if blendMode == bmIntersectMask: + if blendMode == bmMask: if xMin > 0: 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 - if blendMode == bmIntersectMask: + if blendMode == bmMask: if a.width - xMax > 0: zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax)) From cd5f699774bd5d6fb2ee46aaebda733b846e83ac Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 18 Jun 2021 15:21:35 -0700 Subject: [PATCH 2/5] Remove bmIntersectMask (its just mask) --- src/pixie/blends.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pixie/blends.nim b/src/pixie/blends.nim index 60dcfd6..57be2f0 100644 --- a/src/pixie/blends.nim +++ b/src/pixie/blends.nim @@ -32,7 +32,6 @@ type bmMask ## Special blend mode that is used for masking bmOverwrite ## Special blend mode that just copies pixels bmSubtractMask ## Inverse mask - bmIntersectMask bmExcludeMask Blender* = proc(backdrop, source: ColorRGBX): ColorRGBX @@ -477,7 +476,6 @@ proc blender*(blendMode: BlendMode): Blender = of bmMask: blendMask of bmOverwrite: blendOverwrite of bmSubtractMask: blendSubtractMask - of bmIntersectMask: blendIntersectMask of bmExcludeMask: blendExcludeMask proc maskNormal(backdrop, source: uint8): uint8 = @@ -507,7 +505,6 @@ proc masker*(blendMode: BlendMode): Masker = of bmMask: maskMask of bmOverwrite: maskOverwrite of bmSubtractMask: maskSubtract - of bmIntersectMask: maskIntersect of bmExcludeMask: maskExclude else: raise newException(PixieError, "No masker for " & $blendMode) From ff0a69b9de63997c7e9f536514ca098e2d64aa40 Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 18 Jun 2021 15:48:05 -0700 Subject: [PATCH 3/5] done --- src/pixie/paths.nim | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index 0af752d..d4a9320 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -1304,8 +1304,23 @@ proc fillCoverage( source.a = ((source.a.uint32 * coverage) div 255).uint8 let backdrop = image.getRgbaUnsafe(x, y) image.setRgbaUnsafe(x, y, blender(backdrop, source)) + elif blendMode == bmMask: + image.setRgbaUnsafe(x, y, rgbx(0, 0, 0, 0)) inc x + if blendMode == bmMask: + image.data.fillUnsafe( + rgbx(0, 0, 0, 0), + image.dataIndex(0, y), + image.dataIndex(startX, y) - image.dataIndex(0, y) + ) + # if x < image.width: + # image.data.fillUnsafe( + # rgbx(0, 0, 0, 0), + # image.dataIndex(x, y), + # image.dataIndex(image.width, y) + # ) + proc fillCoverage(mask: Mask, startX, y: int, coverages: seq[uint8]) = var x = startX when defined(amd64) and not defined(pixieNoSimd): @@ -1438,6 +1453,18 @@ proc fillShapes( blendMode ) + if blendMode == bmMask: + image.data.fillUnsafe( + rgbx(0, 0, 0, 0), + image.dataIndex(0, 0), + image.dataIndex(0, startY) + ) + image.data.fillUnsafe( + rgbx(0, 0, 0, 0), + image.dataIndex(0, pathHeight), + image.dataIndex(0, image.height) + ) + proc fillShapes(mask: Mask, shapes: seq[seq[Vec2]], windingRule: WindingRule) = # Figure out the total bounds of all the shapes, # rasterize only within the total bounds From 1ee3d3902ae0669d1039a61d2d8746280fe8d2cb Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 18 Jun 2021 16:39:55 -0700 Subject: [PATCH 4/5] Fixing blends --- src/pixie/blends.nim | 6 +++--- src/pixie/paths.nim | 41 +++++++++++++++++-------------------- tests/test_paths.nim | 48 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/src/pixie/blends.nim b/src/pixie/blends.nim index 57be2f0..3fdd211 100644 --- a/src/pixie/blends.nim +++ b/src/pixie/blends.nim @@ -440,9 +440,9 @@ proc blendIntersectMask(backdrop, source: ColorRGBX): ColorRGBX = proc blendExcludeMask(backdrop, source: ColorRGBX): ColorRGBX = let a = max(backdrop.a, source.a).uint32 - min(backdrop.a, source.a) - result.r = ((backdrop.r * a) div 255).uint8 - result.g = ((backdrop.g * a) div 255).uint8 - result.b = ((backdrop.b * a) div 255).uint8 + result.r = ((source.r * a) div 255).uint8 + result.g = ((source.g * a) div 255).uint8 + result.b = ((source.b * a) div 255).uint8 result.a = a.uint8 proc blendOverwrite(backdrop, source: ColorRGBX): ColorRGBX = diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index d4a9320..fbeccea 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -1226,6 +1226,14 @@ proc computeCoverages( for j in i ..< fillStart + fillLen: 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( image: Image, rgbx: ColorRGBX, @@ -1291,7 +1299,7 @@ proc fillCoverage( let blender = blendMode.blender() while x < image.width: let coverage = coverages[x] - if coverage != 0: + if coverage != 0 or blendMode == bmExcludeMask: if blendMode == bmNormal and coverage == 255 and rgbx.a == 255: # Skip blending image.setRgbaUnsafe(x, y, rgbx) @@ -1309,17 +1317,7 @@ proc fillCoverage( inc x if blendMode == bmMask: - image.data.fillUnsafe( - rgbx(0, 0, 0, 0), - image.dataIndex(0, y), - image.dataIndex(startX, y) - image.dataIndex(0, y) - ) - # if x < image.width: - # image.data.fillUnsafe( - # rgbx(0, 0, 0, 0), - # image.dataIndex(x, y), - # image.dataIndex(image.width, y) - # ) + image.clearUnsafe(0, y, startX, y) proc fillCoverage(mask: Mask, startX, y: int, coverages: seq[uint8]) = var x = startX @@ -1356,6 +1354,7 @@ proc fillHits( blendMode: BlendMode ) = let blender = blendMode.blender() + var x = 0 for (prevAt, at, count) in hits.walk(numHits, windingRule, y, image.wh): let fillStart = prevAt.int @@ -1364,7 +1363,7 @@ proc fillHits( if blendMode == bmNormal and rgbx.a == 255: fillUnsafe(image.data, rgbx, image.dataIndex(fillStart, y), fillLen) else: - var x = fillStart + x = fillStart when defined(amd64) and not defined(pixieNoSimd): if blendMode.hasSimdBlender(): # When supported, SIMD blend as much as possible @@ -1385,6 +1384,10 @@ proc fillHits( image.setRgbaUnsafe(x, y, blender(backdrop, rgbx)) inc x + if blendMode == bmMask: + image.clearUnsafe(0, y, startX, y) + image.clearUnsafe(x, y, image.width, y) + proc fillHits( mask: Mask, startX, y: int, @@ -1454,16 +1457,8 @@ proc fillShapes( ) if blendMode == bmMask: - image.data.fillUnsafe( - rgbx(0, 0, 0, 0), - image.dataIndex(0, 0), - image.dataIndex(0, startY) - ) - image.data.fillUnsafe( - rgbx(0, 0, 0, 0), - image.dataIndex(0, pathHeight), - image.dataIndex(0, image.height) - ) + image.clearUnsafe(0, 0, 0, startY) + image.clearUnsafe(0, pathHeight, 0, image.height) proc fillShapes(mask: Mask, shapes: seq[seq[Vec2]], windingRule: WindingRule) = # Figure out the total bounds of all the shapes, diff --git a/tests/test_paths.nim b/tests/test_paths.nim index f21aeaf..e0b17e7 100644 --- a/tests/test_paths.nim +++ b/tests/test_paths.nim @@ -363,3 +363,51 @@ block: path = parsePath("L 0 0") image.fill(rgba(255, 255, 255, 255)) 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") From 8178670619c8029de9d96d72f45d8b3fab1a5378 Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 18 Jun 2021 16:42:24 -0700 Subject: [PATCH 5/5] add images --- tests/images/paths/rectExcludeMask.png | Bin 0 -> 442 bytes tests/images/paths/rectExcludeMaskAA.png | Bin 0 -> 508 bytes tests/images/paths/rectMask.png | Bin 0 -> 351 bytes tests/images/paths/rectMaskAA.png | Bin 0 -> 372 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/images/paths/rectExcludeMask.png create mode 100644 tests/images/paths/rectExcludeMaskAA.png create mode 100644 tests/images/paths/rectMask.png create mode 100644 tests/images/paths/rectMaskAA.png diff --git a/tests/images/paths/rectExcludeMask.png b/tests/images/paths/rectExcludeMask.png new file mode 100644 index 0000000000000000000000000000000000000000..19bf54c12cbf5705708b5ea6e916ea2cf79bfb3a GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^DIm!*slpmBkE-J&p-HOvwTg%?Bh{o)t_8G~Kr9 z?e*(%TMtXwSjwcdRqXogE{RPo7VRLR%7Ghwt=D#vNvg`Vf z13eojIXbbutaPgCTQfyj`^kw{mYZn6ESWmd)cwqV0tUD%Ax9o@cg8w1QchCRlvNshHLIe$%!Csj=;mGyKx+q|rFnGH9xvX@u literal 0 HcmV?d00001 diff --git a/tests/images/paths/rectExcludeMaskAA.png b/tests/images/paths/rectExcludeMaskAA.png new file mode 100644 index 0000000000000000000000000000000000000000..2f8d4a2cb2d6547b18a19cfae9c4556a21fb5342 GIT binary patch literal 508 zcmeAS@N?(olHy`uVBq!ia0vp^DImUwukF2 zy6+v3?z+Z2TT$%jGryY^;^Oub#pq8TUz-)3PgohbdV=qWORX z%P|E5wnjsa9>)Y8rp=`j);}rr2y-@)`SH_2?fnKm`)zziD$_mv>ekuBl(Qv5)nUh*yPEf z#hXZ0YBK$#)a0*I;#a26T)t-0tQXQ>-tJGG|H=7vZ>_E33?At$mzovo$6SqD@kndyUUhQ^u?#f%jz*uGQ MboFyt=akR{0Ab9`N&o-= literal 0 HcmV?d00001 diff --git a/tests/images/paths/rectMask.png b/tests/images/paths/rectMask.png new file mode 100644 index 0000000000000000000000000000000000000000..b72d82d2fc4ffaeb2f4ec50933921e183449543d GIT binary patch literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^DImRxA5&e!1y`4++kDa{0&IPru#lIE9owdK3(sj&R^(6fqUOC zgtcWGq8l=D%D*M7n<1gww%go-J%9ow~-S z&sDE6ULVmu|8SFz@${LN|MND@U#%d`a`Ra$*`_OdoJn-NHuJaDX1NV-&VOHjxM_p$ z1PR9^mQFzxH^Pi3>u=u8{4V^{o3K)#aWnW8Rc08u6{1-oD!M