Add bmIntersectMask and bmExcludeMask.
This commit is contained in:
parent
779f813cca
commit
3764063605
3 changed files with 80 additions and 49 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in a new issue