Search Docs
Loading...
Skip to content

Stroke Effect

Draw a brush stroke along the masks of a block with the stroke effect.

Two yellow rings and a half ring along an ellipse mask: over the image, on transparency and along half of the path

5 mins
estimated time
GitHub

The stroke effect uses the same effect API as all other effects. You create it with createEffect(_:), set its properties and add it to a block with appendEffect(_:effectID:). The stroke follows the path of a mask, so the block needs a mask first. Scenes that use this effect do not open in earlier versions of CE.SDK. For the general effect API, see Apply a Filter or Effect. For masks, see Masks and Mattes.

The example adds three copies of one image to the page and draws a stroke on each.

Adding a Mask#

A mask normally cuts the block to its shape. The "None" mode keeps the whole block and only gives the stroke its path. We add an ellipse mask in this mode to each block.

// An ellipse mask in the "None" mode gives the stroke a path without cutting the block
func addEllipseMask(to block: DesignBlockID) throws {
let mask = try engine.block.createMask()
try engine.block.setShape(mask, shape: engine.block.createShape(.ellipse))
try engine.block.setEnum(mask, property: "mask/mode", value: "None")
try engine.block.appendMask(block, maskID: mask)
}
try addEllipseMask(to: imageBlock)
try addEllipseMask(to: transparentBlock)
try addEllipseMask(to: halfBlock)

A mask in the default "Add" mode cuts the block, and it also cuts the outer half of the stroke.

Drawing the Stroke#

brushSize is the brush radius in design units, so the line is twice as wide. hardness sets how sharp the edge is, from 0 to 1. Below 1 the edge fades. color sets the color of the stroke, here yellow on all three blocks.

// Draw a hard yellow brush stroke along the first mask, over the image
let stroke = try engine.block.createEffect(.stroke)
try engine.block.setInt(stroke, property: "effect/stroke/maskIndex", value: 0)
try engine.block.setFloat(stroke, property: "effect/stroke/brushSize", value: 6)
try engine.block.setFloat(stroke, property: "effect/stroke/hardness", value: 1)
try engine.block.setColor(stroke, property: "effect/stroke/color", color: .rgba(r: 1, g: 0.8, b: 0.1, a: 1))
try engine.block.appendEffect(imageBlock, effectID: stroke)

Choosing the Mask and the Paint Style#

maskIndex picks the mask to stroke in the mask list of the block. An index outside the list strokes nothing. Set allMasks to true to stroke every mask instead.

paintStyle is "OnOriginalImage" to draw over the fill, or "OnTransparent" to draw only the stroke. With "OnTransparent", the fill of the block becomes transparent.

// Stroke every mask and draw only the stroke, without the image
let ringOnly = try engine.block.createEffect(.stroke)
try engine.block.setBool(ringOnly, property: "effect/stroke/allMasks", value: true)
try engine.block.setFloat(ringOnly, property: "effect/stroke/brushSize", value: 6)
try engine.block.setFloat(ringOnly, property: "effect/stroke/hardness", value: 1)
try engine.block.setEnum(ringOnly, property: "effect/stroke/paintStyle", value: "OnTransparent")
try engine.block.setColor(ringOnly, property: "effect/stroke/color", color: .rgba(r: 1, g: 0.8, b: 0.1, a: 1))
try engine.block.appendEffect(transparentBlock, effectID: ringOnly)

Trimming the Stroke#

start and end draw a part of each mask path, as a fraction of its length from 0 to 1. A start after end swaps the two. Here the stroke draws the first half of the ellipse.

// Draw only the first half of the mask path
let halfStroke = try engine.block.createEffect(.stroke)
try engine.block.setFloat(halfStroke, property: "effect/stroke/brushSize", value: 6)
try engine.block.setFloat(halfStroke, property: "effect/stroke/hardness", value: 1)
try engine.block.setFloat(halfStroke, property: "effect/stroke/start", value: 0)
try engine.block.setFloat(halfStroke, property: "effect/stroke/end", value: 0.5)
try engine.block.setColor(halfStroke, property: "effect/stroke/color", color: .rgba(r: 1, g: 0.8, b: 0.1, a: 1))
try engine.block.appendEffect(halfBlock, effectID: halfStroke)

Troubleshooting#

  • The stroke does not show: add a mask to the block, and check that maskIndex points to it.
  • The block shows only the inside of the mask: set mask/mode to "None".
  • The outer half of the stroke is missing: the mask is in the "Add" mode and cuts the stroke. Set mask/mode to "None".

API Reference#

Method Description
engine.block.createMask() Create a mask
engine.block.createShape(_:) Create the ellipse shape of the mask
engine.block.setShape(_:shape:) Give the mask its shape
engine.block.appendMask(_:maskID:) Attach a mask to a block
engine.block.createEffect(_:) Create the .stroke effect
engine.block.appendEffect(_:effectID:) Add the effect to the end of the effect list of a block
engine.block.setInt(_:property:value:) Set maskIndex
engine.block.setBool(_:property:value:) Set allMasks
engine.block.setFloat(_:property:value:) Set the brush and trim properties
engine.block.setColor(_:property:color:) Set the stroke color
engine.block.setEnum(_:property:value:) Set paintStyle and the mask mode

Next Steps#