commit
f2330897eb
7 changed files with 88 additions and 34 deletions
|
@ -292,18 +292,31 @@ proc getRgbaSmooth*(image: Image, x, y: float32): ColorRGBA =
|
||||||
lerp(bottomMix, topMix, diffY)
|
lerp(bottomMix, topMix, diffY)
|
||||||
|
|
||||||
proc drawCorrect(
|
proc drawCorrect(
|
||||||
a: Image, b: Image | Mask, mat = mat3(), blendMode = bmNormal
|
a: Image | Mask, b: Image | Mask, mat = mat3(), blendMode = bmNormal
|
||||||
) =
|
) =
|
||||||
## Draws one image onto another using matrix with color blending.
|
## Draws one image onto another using matrix with color blending.
|
||||||
when type(b) is Image:
|
|
||||||
let blender = blendMode.blender()
|
proc validateMaskBlendMode() =
|
||||||
else:
|
|
||||||
if blendMode notin {bmMask}:
|
if blendMode notin {bmMask}:
|
||||||
raise newException(
|
raise newException(
|
||||||
PixieError,
|
PixieError,
|
||||||
"Blend mode " & $blendMode & " not supported for masks"
|
"Blend mode " & $blendMode & " not supported for masks"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
when type(a) is Image:
|
||||||
|
when type(b) is Image:
|
||||||
|
let blender = blendMode.blenderPremultiplied()
|
||||||
|
else: # b is a Mask
|
||||||
|
validateMaskBlendMode()
|
||||||
|
else: # a is a Mask
|
||||||
|
when type(b) is Image:
|
||||||
|
raise newException(
|
||||||
|
PixieError,
|
||||||
|
"Drawing an image onto a mask is not supported yet"
|
||||||
|
)
|
||||||
|
else: # b is a Mask
|
||||||
|
validateMaskBlendMode()
|
||||||
|
|
||||||
var
|
var
|
||||||
matInv = mat.inverse()
|
matInv = mat.inverse()
|
||||||
b = b
|
b = b
|
||||||
|
@ -325,28 +338,39 @@ proc drawCorrect(
|
||||||
samplePos = matInv * vec2(x.float32 + h, y.float32 + h)
|
samplePos = matInv * vec2(x.float32 + h, y.float32 + h)
|
||||||
xFloat = samplePos.x - h
|
xFloat = samplePos.x - h
|
||||||
yFloat = samplePos.y - h
|
yFloat = samplePos.y - h
|
||||||
rgba = a.getRgbaUnsafe(x, y)
|
|
||||||
|
|
||||||
var blended: ColorRGBA
|
when type(a) is Image:
|
||||||
when type(b) is Image:
|
let rgba = a.getRgbaUnsafe(x, y)
|
||||||
let sample = b.getRgbaSmooth(xFloat, yFloat)
|
var blended: ColorRGBA
|
||||||
blended = blender(rgba, sample)
|
when type(b) is Image:
|
||||||
else:
|
let sample = b.getRgbaSmooth(xFloat, yFloat)
|
||||||
let sample = b.getValueSmooth(xFloat, yFloat).uint32
|
blended = blender(rgba, sample)
|
||||||
blended = rgba(
|
else: # b is a Mask
|
||||||
((rgba.r * sample) div 255).uint8,
|
let sample = b.getValueSmooth(xFloat, yFloat).uint32
|
||||||
((rgba.g * sample) div 255).uint8,
|
blended = rgba(
|
||||||
((rgba.b * sample) div 255).uint8,
|
((rgba.r * sample) div 255).uint8,
|
||||||
((rgba.a * sample) div 255).uint8
|
((rgba.g * sample) div 255).uint8,
|
||||||
)
|
((rgba.b * sample) div 255).uint8,
|
||||||
|
((rgba.a * sample) div 255).uint8
|
||||||
|
)
|
||||||
|
a.setRgbaUnsafe(x, y, blended)
|
||||||
|
else: # a is a Mask, b must be a mask
|
||||||
|
let
|
||||||
|
value = a.getValueUnsafe(x, y)
|
||||||
|
sample = b.getValueSmooth(xFloat, yFloat).uint32
|
||||||
|
a.setValueUnsafe(x, y, ((value * sample) div 255).uint8)
|
||||||
|
|
||||||
a.setRgbaUnsafe(x, y, blended)
|
proc draw*(image: Image, mask: Mask, mat: Mat3, blendMode = bmMask) =
|
||||||
|
image.drawCorrect(mask, mat, blendMode)
|
||||||
|
|
||||||
proc draw*(
|
proc draw*(
|
||||||
image: Image, mask: Mask, pos = vec2(0, 0), blendMode = bmMask
|
image: Image, mask: Mask, pos = vec2(0, 0), blendMode = bmMask
|
||||||
) {.inline.} =
|
) {.inline.} =
|
||||||
image.drawCorrect(mask, translate(pos), blendMode)
|
image.drawCorrect(mask, translate(pos), blendMode)
|
||||||
|
|
||||||
|
proc draw*(a, b: Mask, mat = mat3(), blendMode = bmMask) =
|
||||||
|
a.drawCorrect(b, mat, blendMode)
|
||||||
|
|
||||||
when defined(release):
|
when defined(release):
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import common, vmath, system/memory
|
import blends, common, vmath, system/memory
|
||||||
|
|
||||||
type
|
type
|
||||||
Mask* = ref object
|
Mask* = ref object
|
||||||
|
|
|
@ -363,6 +363,23 @@ proc rect*(path: var Path, x, y, w, h: float32) =
|
||||||
proc rect*(path: var Path, pos: Vec2, wh: Vec2) {.inline.} =
|
proc rect*(path: var Path, pos: Vec2, wh: Vec2) {.inline.} =
|
||||||
path.rect(pos.x, pos.y, wh.x, wh.y)
|
path.rect(pos.x, pos.y, wh.x, wh.y)
|
||||||
|
|
||||||
|
proc roundedRect*(
|
||||||
|
path: var Path, pos, wh: Vec2, nw, ne, se, sw: float32, clockwise = true
|
||||||
|
) =
|
||||||
|
let
|
||||||
|
maxRadius = min(wh.x / 2, wh.y / 2)
|
||||||
|
nw = min(nw, maxRadius)
|
||||||
|
ne = min(ne, maxRadius)
|
||||||
|
se = min(se, maxRadius)
|
||||||
|
sw = min(sw, maxRadius)
|
||||||
|
|
||||||
|
path.moveTo(pos.x + nw, pos.y)
|
||||||
|
path.arcTo(pos.x + wh.x, pos.y, pos.x + wh.x, pos.y + wh.y, ne)
|
||||||
|
path.arcTo(pos.x + wh.x, pos.y + wh.y, pos.x, pos.y + wh.y, se)
|
||||||
|
path.arcTo(pos.x, pos.y + wh.y, pos.x, pos.y, sw)
|
||||||
|
path.arcTo(pos.x, pos.y, pos.x + wh.x, pos.y, nw)
|
||||||
|
path.closePath()
|
||||||
|
|
||||||
proc ellipse*(path: var Path, cx, cy, rx, ry: float32) =
|
proc ellipse*(path: var Path, cx, cy, rx, ry: float32) =
|
||||||
let
|
let
|
||||||
magicX = (4.0 * (-1.0 + sqrt(2.0)) / 3) * rx
|
magicX = (4.0 * (-1.0 + sqrt(2.0)) / 3) * rx
|
||||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
tests/images/masks/maskedMask.png
Normal file
BIN
tests/images/masks/maskedMask.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 885 B |
|
@ -91,17 +91,3 @@ block:
|
||||||
a = readImage("tests/images/flipped1.png")
|
a = readImage("tests/images/flipped1.png")
|
||||||
b = a.minifyBy2(2)
|
b = a.minifyBy2(2)
|
||||||
b.writeFile("tests/images/minifiedBy4.png")
|
b.writeFile("tests/images/minifiedBy4.png")
|
||||||
|
|
||||||
block:
|
|
||||||
let image = newImage(100, 100)
|
|
||||||
image.fill(rgba(255, 100, 100, 255))
|
|
||||||
|
|
||||||
var path: Path
|
|
||||||
path.ellipse(image.width / 2, image.height / 2, 25, 25)
|
|
||||||
|
|
||||||
let mask = newMask(image.width, image.height)
|
|
||||||
mask.fillPath(path)
|
|
||||||
|
|
||||||
image.draw(mask)
|
|
||||||
image.toStraightAlpha()
|
|
||||||
image.writeFile("tests/images/circleMask.png")
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import pixie, pixie/fileformats/png
|
import chroma, pixie, pixie/fileformats/png
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let
|
let
|
||||||
|
@ -21,3 +21,30 @@ block:
|
||||||
doAssert minified.width == 50 and minified.height == 50
|
doAssert minified.width == 50 and minified.height == 50
|
||||||
|
|
||||||
writeFile("tests/images/masks/maskMinified.png", minified.encodePng())
|
writeFile("tests/images/masks/maskMinified.png", minified.encodePng())
|
||||||
|
|
||||||
|
block:
|
||||||
|
let image = newImage(100, 100)
|
||||||
|
image.fill(rgba(255, 100, 100, 255))
|
||||||
|
|
||||||
|
var path: Path
|
||||||
|
path.ellipse(image.width / 2, image.height / 2, 25, 25)
|
||||||
|
|
||||||
|
let mask = newMask(image.width, image.height)
|
||||||
|
mask.fillPath(path)
|
||||||
|
|
||||||
|
image.draw(mask)
|
||||||
|
image.toStraightAlpha()
|
||||||
|
image.writeFile("tests/images/masks/circleMask.png")
|
||||||
|
|
||||||
|
block:
|
||||||
|
let a = newMask(100, 100)
|
||||||
|
a.fill(255)
|
||||||
|
|
||||||
|
var path: Path
|
||||||
|
path.ellipse(a.width / 2, a.height / 2, 25, 25)
|
||||||
|
|
||||||
|
let b = newMask(a.width, a.height)
|
||||||
|
b.fillPath(path)
|
||||||
|
|
||||||
|
a.draw(b)
|
||||||
|
writeFile("tests/images/masks/maskedMask.png", a.encodePng())
|
||||||
|
|
Loading…
Reference in a new issue