Add procs that fidget/mirror and typography require.

This commit is contained in:
treeform 2020-11-21 19:43:47 -08:00
parent eb6d47c560
commit f7ab97ae87

View file

@ -143,6 +143,7 @@ proc hasEffect*(blendMode: BlendMode, rgba: ColorRGBA): bool =
else: else:
rgba.a > 0 rgba.a > 0
# TODO: Move this to vmath.
func translate*(v: Vec2): Mat3 = func translate*(v: Vec2): Mat3 =
result[0, 0] = 1 result[0, 0] = 1
result[1, 1] = 1 result[1, 1] = 1
@ -150,6 +151,12 @@ func translate*(v: Vec2): Mat3 =
result[2, 1] = v.y result[2, 1] = v.y
result[2, 2] = 1 result[2, 2] = 1
# TODO: Move this to vmath.
func scale*(v: Vec2): Mat3 =
result[0, 0] = v.x
result[1, 1] = v.y
result[2, 2] = 1
proc fraction(v: float32): float32 = proc fraction(v: float32): float32 =
result = abs(v) result = abs(v)
result = result - floor(result) result = result - floor(result)
@ -158,8 +165,8 @@ proc drawFast1*(a: Image, b: Image, mat: Mat3): Image =
## Draws one image onto another using integer x,y offset with COPY. ## Draws one image onto another using integer x,y offset with COPY.
result = newImage(a.width, a.height) result = newImage(a.width, a.height)
var matInv = mat.inverse() var matInv = mat.inverse()
for y in 0 ..< a.width: for y in 0 ..< a.height:
for x in 0 ..< a.height: for x in 0 ..< a.width:
var rgba = a.getRgbaUnsafe(x, y) 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): if b.inside(srcPos.x.floor.int, srcPos.y.floor.int):
@ -170,8 +177,9 @@ proc drawFast2*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
## Draws one image onto another using matrix with color blending. ## Draws one image onto another using matrix with color blending.
result = newImage(a.width, a.height) result = newImage(a.width, a.height)
var matInv = mat.inverse() var matInv = mat.inverse()
for y in 0 ..< a.width: for y in 0 ..< a.height:
for x in 0 ..< a.height: for x in 0 ..< a.width:
#echo x, ", ", y
var rgba = a.getRgbaUnsafe(x, y) 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): if b.inside(srcPos.x.floor.int, srcPos.y.floor.int):
@ -184,8 +192,8 @@ proc drawFast3*(a: Image, b: Image, mat: Mat3, blendMode: BlendMode): Image =
## Draws one image onto another using matrix with color blending. ## Draws one image onto another using matrix with color blending.
result = newImage(a.width, a.height) result = newImage(a.width, a.height)
var matInv = mat.inverse() var matInv = mat.inverse()
for y in 0 ..< a.width: for y in 0 ..< a.height:
for x in 0 ..< a.height: for x in 0 ..< a.width:
var rgba = a.getRgbaUnsafe(x, y) 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): if b.inside1px(srcPos.x, srcPos.y):
@ -212,3 +220,175 @@ proc draw*(a: Image, b: Image, mat: Mat3, blendMode = bmNormal): Image =
proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = bmNormal): Image = proc draw*(a: Image, b: Image, pos = vec2(0, 0), blendMode = bmNormal): Image =
a.draw(b, translate(pos), blendMode) a.draw(b, translate(pos), blendMode)
# TODO: Make methods bellow not be in place.
proc blur*(image: Image, radius: float32): Image =
## Applies Gaussian blur to the image given a radius.
let radius = (radius).int
if radius == 0:
return image.copy()
# Compute lookup table for 1d Gaussian kernel.
var lookup = newSeq[float](radius*2+1)
var total = 0.0
for xb in -radius .. radius:
let s = radius.float32 / 2.2 # 2.2 matches Figma.
let x = xb.float32
let a = 1/sqrt(2*PI*s^2) * exp(-1*x^2/(2*s^2))
lookup[xb + radius] = a
total += a
for xb in -radius .. radius:
lookup[xb + radius] /= total
# Blur in the X direction.
var blurX = newImage(image.width, image.height)
for y in 0 ..< image.height:
for x in 0 ..< image.width:
var c: Color
var totalA = 0.0
for xb in -radius .. radius:
let c2 = image[x + xb, y].color
let a = lookup[xb + radius]
let aa = c2.a * a
totalA += aa
c.r += c2.r * aa
c.g += c2.g * aa
c.b += c2.b * aa
c.a += c2.a * a
c.r = c.r / totalA
c.g = c.g / totalA
c.b = c.b / totalA
blurX.setRgbaUnsafe(x, y, c.rgba )
# Blur in the Y direction.
var blurY = newImage(image.width, image.height)
for y in 0 ..< image.height:
for x in 0 ..< image.width:
var c: Color
var totalA = 0.0
for yb in -radius .. radius:
let c2 = blurX[x, y + yb].color
let a = lookup[yb + radius]
let aa = c2.a * a
totalA += aa
c.r += c2.r * aa
c.g += c2.g * aa
c.b += c2.b * aa
c.a += c2.a * a
c.r = c.r / totalA
c.g = c.g / totalA
c.b = c.b / totalA
blurY.setRgbaUnsafe(x, y, c.rgba)
return blurY
proc resize*(srcImage: Image, width, height: int): Image =
result = newImage(width, height)
return result.draw(
srcImage,
scale(vec2(
(width + 1).float / srcImage.width.float,
(height + 1).float / srcImage.height.float
))
)
proc shift(image: Image, offset: Vec2): Image =
## Shifts the image by offset.
result = newImage(image.width, image.height)
return result.draw(image, offset)
proc spread(image: Image, spread: float32): Image =
## Grows the image as a mask by spread.
result = newImage(image.width, image.height)
assert spread > 0
for y in 0 ..< result.height:
for x in 0 ..< result.width:
var maxAlpha = 0.uint8
for bx in -spread.int .. spread.int:
for by in -spread.int .. spread.int:
#if vec2(bx.float32, by.float32).length < spread:
let alpha = image[x + bx, y + by].a
if alpha > maxAlpha:
maxAlpha = alpha
if maxAlpha == 255:
break
if maxAlpha == 255:
break
result[x, y] = rgba(0, 0, 0, maxAlpha)
proc shadow*(
mask: Image,
offset: Vec2,
spread: float,
blur: float32,
color: Color
): Image =
## Create a shadow of the image with the offset, spread and blur.
var shadow = mask
if offset != vec2(0, 0):
shadow = shadow.shift(offset)
if spread > 0:
shadow = shadow.spread(spread)
if blur > 0:
shadow = shadow.blur(blur)
result = newImage(mask.width, mask.height)
result.fill(color.rgba)
return result.draw(shadow, blendMode = bmMask)
proc invertColor*(image: Image) =
## Flips the image around the Y axis.
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
image.setRgbaUnsafe(x, y, rgba)
proc applyOpacity*(image: Image, opacity: float32) =
## Multiplies alpha of the image by opacity.
let op = (255 * opacity).uint8
for y in 0 ..< image.height:
for x in 0 ..< image.width:
var rgba = image.getRgbaUnsafe(x, y)
rgba.a = ((rgba.a.uint32 * op.uint32) div 255).clamp(0, 255).uint8
image.setRgbaUnsafe(x, y, rgba)
# TODO: Make this method use path's AA lines.
proc line*(image: Image, at, to: Vec2, rgba: ColorRGBA) =
## Draws a line from one at vec to to vec.
let
dx = to.x - at.x
dy = to.y - at.y
var x = at.x
while true:
if dx == 0:
break
let y = at.y + dy * (x - at.x) / dx
image[int x, int y] = rgba
if at.x < to.x:
x += 1
if x > to.x:
break
else:
x -= 1
if x < to.x:
break
var y = at.y
while true:
if dy == 0:
break
let x = at.x + dx * (y - at.y) / dy
image[int x, int y] = rgba
if at.y < to.y:
y += 1
if y > to.y:
break
else:
y -= 1
if y < to.y:
break