diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index f579926..f23a534 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -1937,150 +1937,134 @@ proc fillShapes( if maybeLeftMaxX > maybeRightMaxX: swap left, right - let requiresAntiAliasing = - left.segment.requiresAntiAliasing or - right.segment.requiresAntiAliasing + # We have 2 non-intersecting lines that require anti-aliasing + # Use trapezoid coverage at the edges and fill in the middle - if requiresAntiAliasing: - # We have 2 non-intersecting lines that require anti-aliasing - # Use trapezoid coverage at the edges and fill in the middle + when allowSimd and defined(amd64): + let vecRgbx = mm_set_ps( + rgbx.a.float32, + rgbx.b.float32, + rgbx.g.float32, + rgbx.r.float32 + ) - when allowSimd and defined(amd64): - let vecRgbx = mm_set_ps( - rgbx.a.float32, - rgbx.b.float32, - rgbx.g.float32, - rgbx.r.float32 - ) + proc solveX(entry: PartitionEntry, y: float32): float32 = + if entry.m == 0: + entry.b + else: + (y - entry.b) / entry.m - proc solveX(entry: PartitionEntry, y: float32): float32 = - if entry.m == 0: - entry.b - else: - (y - entry.b) / entry.m + proc solveY(entry: PartitionEntry, x: float32): float32 = + entry.m * x + entry.b - proc solveY(entry: PartitionEntry, x: float32): float32 = - entry.m * x + entry.b + var + leftTop = vec2(0, y.float32) + leftBottom = vec2(0, (y + 1).float32) + leftTop.x = left.solveX(leftTop.y.float32) + leftBottom.x = left.solveX(leftBottom.y) - var - leftTop = vec2(0, y.float32) - leftBottom = vec2(0, (y + 1).float32) - leftTop.x = left.solveX(leftTop.y.float32) - leftBottom.x = left.solveX(leftBottom.y) + var + rightTop = vec2(0, y.float32) + rightBottom = vec2(0, (y + 1).float32) + rightTop.x = right.solveX(rightTop.y) + rightBottom.x = right.solveX(rightBottom.y) - var - rightTop = vec2(0, y.float32) - rightBottom = vec2(0, (y + 1).float32) - rightTop.x = right.solveX(rightTop.y) - rightBottom.x = right.solveX(rightBottom.y) + let + leftMaxX = max(leftTop.x, leftBottom.x) + rightMinX = min(rightTop.x, rightBottom.x) + leftCoverEnd = leftMaxX.ceil.int + rightCoverBegin = rightMinX.trunc.int - let - leftMaxX = max(leftTop.x, leftBottom.x) - rightMinX = min(rightTop.x, rightBottom.x) - leftCoverEnd = leftMaxX.ceil.int - rightCoverBegin = rightMinX.trunc.int + if leftCoverEnd < rightCoverBegin: + # Only take this shortcut if the partial coverage areas on the + # left and the right do not overlap - if leftCoverEnd < rightCoverBegin: - # Only take this shortcut if the partial coverage areas on the - # left and the right do not overlap - - let blender = blendMode.blender() - - block: # Left-side partial coverage - let - inverted = leftTop.x < leftBottom.x - sliverStart = min(leftTop.x, leftBottom.x) - rectStart = max(leftTop.x, leftBottom.x) - var - pen = sliverStart - prevPen = pen - penY = if inverted: y.float32 else: (y + 1).float32 - prevPenY = penY - for x in sliverStart.int ..< rectStart.ceil.int: - prevPen = pen - pen = (x + 1).float32 - var rightRectArea = 0.float32 - if pen > rectStart: - rightRectArea = pen - rectStart - pen = rectStart - prevPenY = penY - penY = left.solveY(pen) - if x < 0 or x >= image.width: - continue - let - run = pen - prevPen - triangleArea = 0.5.float32 * run * abs(penY - prevPenY) - rectArea = - if inverted: - (prevPenY - y.float32) * run - else: - ((y + 1).float32 - prevPenY) * run - area = triangleArea + rectArea + rightRectArea - dataIndex = image.dataIndex(x, y) - backdrop = image.data[dataIndex] - source = - when allowSimd and defined(amd64): - applyOpacity(vecRgbx, area) - else: - rgbx * area - image.data[dataIndex] = blender(backdrop, source) - - block: # Right-side partial coverage - let - inverted = rightTop.x > rightBottom.x - rectEnd = min(rightTop.x, rightBottom.x) - sliverEnd = max(rightTop.x, rightBottom.x) - var - pen = rectEnd - prevPen = pen - penY = if inverted: (y + 1).float32 else: y.float32 - prevPenY = penY - for x in rectEnd.int ..< sliverEnd.ceil.int: - prevPen = pen - pen = (x + 1).float32 - let leftRectArea = prevPen.fractional - if pen > sliverEnd: - pen = sliverEnd - prevPenY = penY - penY = right.solveY(pen) - if x < 0 or x >= image.width: - continue - let - run = pen - prevPen - triangleArea = 0.5.float32 * run * abs(penY - prevPenY) - rectArea = - if inverted: - (penY - y.float32) * run - else: - ((y + 1).float32 - penY) * run - area = leftRectArea + triangleArea + rectArea - dataIndex = image.dataIndex(x, y) - backdrop = image.data[dataIndex] - source = - when allowSimd and defined(amd64): - applyOpacity(vecRgbx, area) - else: - rgbx * area - image.data[dataIndex] = blender(backdrop, source) + let blender = blendMode.blender() + block: # Left-side partial coverage let - fillBegin = leftCoverEnd.clamp(0, image.width) - fillEnd = rightCoverBegin.clamp(0, image.width) - if fillEnd - fillBegin > 0: - hits[0] = (fixed32(fillBegin.float32), 1.int16) - hits[1] = (fixed32(fillEnd.float32), -1.int16) - image.fillHits(rgbx, 0, y, hits, 2, NonZero, blendMode) + inverted = leftTop.x < leftBottom.x + sliverStart = min(leftTop.x, leftBottom.x) + rectStart = max(leftTop.x, leftBottom.x) + var + pen = sliverStart + prevPen = pen + penY = if inverted: y.float32 else: (y + 1).float32 + prevPenY = penY + for x in sliverStart.int ..< rectStart.ceil.int: + prevPen = pen + pen = (x + 1).float32 + var rightRectArea = 0.float32 + if pen > rectStart: + rightRectArea = pen - rectStart + pen = rectStart + prevPenY = penY + penY = left.solveY(pen) + if x < 0 or x >= image.width: + continue + let + run = pen - prevPen + triangleArea = 0.5.float32 * run * abs(penY - prevPenY) + rectArea = + if inverted: + (prevPenY - y.float32) * run + else: + ((y + 1).float32 - prevPenY) * run + area = triangleArea + rectArea + rightRectArea + dataIndex = image.dataIndex(x, y) + backdrop = image.data[dataIndex] + source = + when allowSimd and defined(amd64): + applyOpacity(vecRgbx, area) + else: + rgbx * area + image.data[dataIndex] = blender(backdrop, source) - inc y - continue + block: # Right-side partial coverage + let + inverted = rightTop.x > rightBottom.x + rectEnd = min(rightTop.x, rightBottom.x) + sliverEnd = max(rightTop.x, rightBottom.x) + var + pen = rectEnd + prevPen = pen + penY = if inverted: (y + 1).float32 else: y.float32 + prevPenY = penY + for x in rectEnd.int ..< sliverEnd.ceil.int: + prevPen = pen + pen = (x + 1).float32 + let leftRectArea = prevPen.fractional + if pen > sliverEnd: + pen = sliverEnd + prevPenY = penY + penY = right.solveY(pen) + if x < 0 or x >= image.width: + continue + let + run = pen - prevPen + triangleArea = 0.5.float32 * run * abs(penY - prevPenY) + rectArea = + if inverted: + (penY - y.float32) * run + else: + ((y + 1).float32 - penY) * run + area = leftRectArea + triangleArea + rectArea + dataIndex = image.dataIndex(x, y) + backdrop = image.data[dataIndex] + source = + when allowSimd and defined(amd64): + applyOpacity(vecRgbx, area) + else: + rgbx * area + image.data[dataIndex] = blender(backdrop, source) - else: let - minX = left.segment.at.x.int.clamp(0, image.width) - maxX = right.segment.at.x.int.clamp(0, image.width) - hits[0] = (cast[Fixed32](minX * 256), 1.int16) - hits[1] = (cast[Fixed32](maxX * 256), -1.int16) - image.fillHits(rgbx, 0, y, hits, 2, NonZero, blendMode) + fillBegin = leftCoverEnd.clamp(0, image.width) + fillEnd = rightCoverBegin.clamp(0, image.width) + if fillEnd - fillBegin > 0: + hits[0] = (fixed32(fillBegin.float32), 1.int16) + hits[1] = (fixed32(fillEnd.float32), -1.int16) + image.fillHits(rgbx, 0, y, hits, 2, NonZero, blendMode) inc y continue