Add bmIntersectMask and bmExcludeMask.

This commit is contained in:
treeform 2020-11-23 08:56:11 -08:00
parent 779f813cca
commit 3764063605
3 changed files with 80 additions and 49 deletions

View file

@ -24,6 +24,8 @@ type BlendMode* = enum
bmMask ## Special blend mode that is used for masking
bmOverwrite ## Special that does not blend but copies the pixels from target.
bmSubtractMask ## Inverse mask
bmIntersectMask
bmExcludeMask
proc parseBlendMode*(s: string): BlendMode =
case s:
@ -97,6 +99,18 @@ proc mix*(blendMode: BlendMode, target, blend: Color): Color =
result.b = target.b
result.a = target.a * (1 - blend.a)
return
elif blendMode == bmIntersectMask:
result.r = target.r
result.g = target.g
result.b = target.b
result.a = target.a * blend.a
return
elif blendMode == bmExcludeMask:
result.r = target.r
result.g = target.g
result.b = target.b
result.a = abs(target.a - blend.a)
return
elif blendMode == bmOverwrite:
result = blend
return

View file

@ -82,18 +82,31 @@ proc `[]=`*(image: Image, x, y: int, rgba: ColorRGBA) {.inline.} =
if image.inside(x, y):
image.setRgbaUnsafe(x, y, rgba)
proc fill*(image: Image, rgba: ColorRgba) =
proc newImageFill*(width, height: int, rgba: ColorRgba): Image =
## Fills the image with a solid color.
for i in 0 ..< image.data.len:
image.data[i] = rgba
result = newImageNoInit(width, height)
for y in 0 ..< result.height:
for x in 0 ..< result.width:
result.setRgbaUnsafe(x, y, rgba)
proc invert*(image: Image) =
proc fill*(image: Image, rgba: ColorRgba): Image =
## Fills the image with a solid color.
result = newImageNoInit(image.width, image.height)
for y in 0 ..< result.height:
for x in 0 ..< result.width:
result.setRgbaUnsafe(x, y, rgba)
proc invert*(image: Image): Image =
## Inverts all of the colors and alpha.
for rgba in image.data.mitems:
rgba.r = 255 - rgba.r
rgba.g = 255 - rgba.g
rgba.b = 255 - rgba.b
rgba.a = 255 - rgba.a
result = newImageNoInit(image.width, image.height)
for y in 0 ..< image.height:
for x in 0 ..< image.width:
var rgba = image.getRgbaUnsafe(x, y)
rgba.r = 255 - rgba.r
rgba.g = 255 - rgba.g
rgba.b = 255 - rgba.b
rgba.a = 255 - rgba.a
result.setRgbaUnsafe(x, y, rgba)
proc subImage*(image: Image, x, y, w, h: int): Image =
## Gets a sub image of the main image.
@ -183,9 +196,19 @@ proc hasEffect*(blendMode: BlendMode, rgba: ColorRGBA): bool =
rgba.a != 255
of bmOverwrite:
true
of bmIntersectMask:
true
else:
rgba.a > 0
proc allowCopy*(blendMode: BlendMode): bool =
## Returns true if applying rgba with current blend mode has effect.
case blendMode
of bmIntersectMask:
false
else:
true
proc drawOverwrite*(a: Image, b: Image, mat: Mat3): Image =
## Draws one image onto another using integer x,y offset with COPY.
result = newImageNoInit(a.width, a.height)
@ -195,7 +218,6 @@ proc drawOverwrite*(a: Image, b: Image, mat: Mat3): Image =
for x in 0 ..< a.width:
var rgba = a.getRgbaUnsafe(x, y)
let srcPos = matInv * vec2(x.float32, y.float32)
if b.inside(srcPos.x.floor.int, srcPos.y.floor.int):
rgba = b.getRgbaUnsafe(srcPos.x.floor.int, srcPos.y.floor.int)
result.setRgbaUnsafe(x, y, rgba)
@ -206,14 +228,19 @@ proc drawBlend*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
var matInv = mat.inverse()
for y in 0 ..< a.height:
for x in 0 ..< a.width:
var rgba = a.getRgbaUnsafe(x, y)
let srcPos = matInv * vec2(x.float32, y.float32)
let srcPos = matInv * vec2(x.float32, y.float32)
if b.inside(srcPos.x.floor.int, srcPos.y.floor.int):
var rgba = a.getRgbaUnsafe(x, y)
let rgba2 = b.getRgbaUnsafe(srcPos.x.floor.int, srcPos.y.floor.int)
if blendMode.hasEffect(rgba2):
rgba = blendMode.mix(rgba, rgba2)
result.setRgbaUnsafe(x, y, rgba)
result.setRgbaUnsafe(x, y, rgba)
else:
if blendMode.allowCopy():
var rgba = a.getRgbaUnsafe(x, y)
result.setRgbaUnsafe(x, y, rgba)
proc drawBlendSmooth*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
## Draws one image onto another using matrix with color blending.
@ -224,14 +251,19 @@ proc drawBlendSmooth*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Imag
var matInv = mat.inverse()
for y in 0 ..< a.height:
for x in 0 ..< a.width:
var rgba = a.getRgbaUnsafe(x, y)
let srcPos = matInv * vec2(x.float32, y.float32)
let srcPos = matInv * vec2(x.float32, y.float32)
if b.inside1px(srcPos.x, srcPos.y):
var rgba = a.getRgbaUnsafe(x, y)
let rgba2 = b.getRgbaSmooth(srcPos.x, srcPos.y)
if blendMode.hasEffect(rgba2):
rgba = blendMode.mix(rgba, rgba2)
result.setRgbaUnsafe(x, y, rgba)
result.setRgbaUnsafe(x, y, rgba)
else:
if blendMode.allowCopy():
var rgba = a.getRgbaUnsafe(x, y)
result.setRgbaUnsafe(x, y, rgba)
proc draw*(a: Image, b: Image, mat: Mat3, blendMode = bmNormal): Image =
## Draws one image onto another using matrix with color blending.
@ -359,22 +391,9 @@ proc shadow*(
shadow = shadow.spread(spread)
if blur > 0:
shadow = shadow.blur(blur)
result = newImage(mask.width, mask.height)
result.fill(color.rgba)
result = newImageFill(mask.width, mask.height, color.rgba)
return result.draw(shadow, blendMode = bmMask)
proc invertColor*(image: Image): Image =
## Flips the image around the Y axis.
result = newImageNoInit(image.width, image.height)
for y in 0 ..< image.height:
for x in 0 ..< image.width:
var rgba = image.getRgbaUnsafe(x, y)
rgba.r = 255 - rgba.r
rgba.g = 255 - rgba.g
rgba.b = 255 - rgba.b
rgba.a = 255 - rgba.a
result.setRgbaUnsafe(x, y, rgba)
proc applyOpacity*(image: Image, opacity: float32): Image =
## Multiplies alpha of the image by opacity.
result = newImageNoInit(image.width, image.height)

View file

@ -587,11 +587,10 @@ proc fillPolygons*(
polys: seq[seq[Vec2]],
color: ColorRGBA,
quality = 4,
) =
): Image =
const ep = 0.0001 * PI
if polys.len == 0:
image.fill(rgba(0, 0, 0, 0))
result = newImage(image.width, image.height)
proc scanLineHits(
polys: seq[seq[Vec2]],
@ -616,9 +615,9 @@ proc fillPolygons*(
var hits: seq[(float32, bool)]
var alphas = newSeq[float32](image.width)
for y in 0 ..< image.height:
for x in 0 ..< image.width:
var alphas = newSeq[float32](result.width)
for y in 0 ..< result.height:
for x in 0 ..< result.width:
alphas[x] = 0
for m in 0 ..< quality:
polys.scanLineHits(hits, y, float32(m)/float32(quality))
@ -627,7 +626,7 @@ proc fillPolygons*(
var
penFill = 0.0
curHit = 0
for x in 0 ..< image.width:
for x in 0 ..< result.width:
var penEdge = penFill
while true:
if curHit >= hits.len:
@ -644,11 +643,11 @@ proc fillPolygons*(
penEdge -= 1.0 - cover
inc curHit
alphas[x] += penEdge
for x in 0 ..< image.width:
for x in 0 ..< result.width:
var a = clamp(abs(alphas[x]) / float32(quality), 0.0, 1.0)
var colorWithAlpha = color
colorWithAlpha.a = uint8(clamp(a, 0, 1) * 255.0)
image[x, y] = colorWithAlpha
result[x, y] = colorWithAlpha
{.pop.}
@ -656,7 +655,7 @@ proc fillPath*(
image: Image,
path: Path,
color: ColorRGBA
) =
): Image =
let polys = commandsToPolygons(path.commands)
image.fillPolygons(polys, color)
@ -664,7 +663,7 @@ proc fillPath*(
image: Image,
path: string,
color: ColorRGBA
) =
): Image =
image.fillPath(parsePath(path), color)
proc fillPath*(
@ -672,7 +671,7 @@ proc fillPath*(
path: string,
color: ColorRGBA,
pos: Vec2
) =
): Image =
var polys = commandsToPolygons(parsePath(path).commands)
for poly in polys.mitems:
for i, p in poly.mpairs:
@ -684,7 +683,7 @@ proc fillPath*(
path: Path,
color: ColorRGBA,
mat: Mat3
) =
): Image =
var polys = commandsToPolygons(path.commands)
for poly in polys.mitems:
for i, p in poly.mpairs:
@ -696,7 +695,7 @@ proc fillPath*(
path: string,
color: ColorRGBA,
mat: Mat3
) =
): Image =
image.fillPath(parsePath(path), color, mat)
proc strokePath*(
@ -707,7 +706,7 @@ proc strokePath*(
# strokeLocation: StrokeLocation,
# strokeCap: StorkeCap,
# strokeJoin: StorkeJoin
) =
): Image =
let polys = commandsToPolygons(path.commands)
let (strokeL, strokeR) = (strokeWidth/2, strokeWidth/2)
let polys2 = strokePolygons(polys, strokeL, strokeR)
@ -718,7 +717,7 @@ proc strokePath*(
path: string,
color: ColorRGBA,
strokeWidth: float32
) =
): Image =
image.strokePath(parsePath(path), color, strokeWidth)
proc strokePath*(
@ -727,7 +726,7 @@ proc strokePath*(
color: ColorRGBA,
strokeWidth: float32,
pos: Vec2
) =
): Image =
var polys = commandsToPolygons(parsePath(path).commands)
let (strokeL, strokeR) = (strokeWidth/2, strokeWidth/2)
var polys2 = strokePolygons(polys, strokeL, strokeR)
@ -742,7 +741,7 @@ proc strokePath*(
color: ColorRGBA,
strokeWidth: float32,
mat: Mat3
) =
): Image =
var polys = commandsToPolygons(parsePath(path).commands)
let (strokeL, strokeR) = (strokeWidth/2, strokeWidth/2)
var polys2 = strokePolygons(polys, strokeL, strokeR)
@ -859,7 +858,6 @@ proc arcTo*(path: Path, x1, y1, x2, y2, r: float32) =
]
))
proc ellipse*(path: Path) =
## Adds an elliptical arc to the path which is centered at (x, y) position with the radii radiusX and radiusY starting at startAngle and ending at endAngle going in the given direction by anticlockwise (defaulting to clockwise).
raise newException(ValueError, "not implemented")