124 lines
3.4 KiB
Nim
124 lines
3.4 KiB
Nim
|
import benchy, chroma, pixie, pixie/internal, strformat
|
||
|
import benchy, chroma, pixie
|
||
|
|
||
|
proc newRoundedRectImage1(w, h, r: int, color: Color): Image =
|
||
|
result = newImage(w, h)
|
||
|
let ctx = newContext(result)
|
||
|
ctx.fillStyle = color(0, 1, 0, 1)
|
||
|
let
|
||
|
pos = vec2(0, 0)
|
||
|
wh = vec2(w.float32, h.float32)
|
||
|
r = r.float32
|
||
|
ctx.fillRoundedRect(rect(pos, wh), r)
|
||
|
|
||
|
proc newRoundedRectImage15(w, h, r: int, color: Color): Image =
|
||
|
let path = newPath()
|
||
|
let
|
||
|
pos = vec2(0, 0)
|
||
|
wh = vec2(w.float32, h.float32)
|
||
|
r = r.float32
|
||
|
path.roundedRect(rect(pos, wh), r, r, r, r)
|
||
|
result = path.fillImage(w, h, color(0, 1, 0, 1))
|
||
|
|
||
|
proc newRoundedRectImage2(w, h, r: int, color: Color): Image =
|
||
|
result = newImage(w, h)
|
||
|
result.fill(color)
|
||
|
|
||
|
let
|
||
|
w1 = w - 1
|
||
|
h1 = h - 1
|
||
|
for y in 0 ..< r:
|
||
|
for x in 0 ..< r:
|
||
|
var a: float32 = 0
|
||
|
for s in 0 ..< 5:
|
||
|
let
|
||
|
yc = y.float32 + s.float32 / 5 + (1 / 5 / 2)
|
||
|
xc = r.float32 - sqrt(r.float32*r.float32 - (yc - r.float32) ^ 2)
|
||
|
let mid = (x.float32 - xc + 1).clamp(0, 1)
|
||
|
a += 1/5 * mid
|
||
|
|
||
|
if a < 1:
|
||
|
var c = color
|
||
|
c.a = a
|
||
|
let cx = c.rgbx
|
||
|
result.setRgbaUnsafe(x, y, cx)
|
||
|
result.setRgbaUnsafe(w1 - x, y, cx)
|
||
|
result.setRgbaUnsafe(w1 - x, h1 - y, cx)
|
||
|
result.setRgbaUnsafe(x, h1 - y, cx)
|
||
|
|
||
|
proc newRoundedRectImage3(w, h, r: int, color: Color): Image =
|
||
|
result = newImage(w, h)
|
||
|
result.fill(color)
|
||
|
|
||
|
if r == 0:
|
||
|
return
|
||
|
|
||
|
const
|
||
|
q = 5
|
||
|
qf = q.float32
|
||
|
qoffset: float32 = (1 / qf / 2)
|
||
|
|
||
|
let
|
||
|
r = r.clamp(0, min(w, h) div 2)
|
||
|
rf = r.float32
|
||
|
w1 = w - 1
|
||
|
h1 = h - 1
|
||
|
rgbx = color.rgbx
|
||
|
channels = [rgbx.r.uint32, rgbx.g.uint32, rgbx.b.uint32, rgbx.a.uint32]
|
||
|
|
||
|
var coverage = newSeq[uint8](r)
|
||
|
|
||
|
for y in 0 ..< r:
|
||
|
zeroMem(coverage[0].addr, coverage.len)
|
||
|
var yf: float32 = y.float32 + qoffset
|
||
|
for m in 0 ..< q:
|
||
|
let hit = sqrt(rf^2 - yf^2)
|
||
|
coverage[hit.int] += max((1 - (hit - hit.trunc)) * 255 / qf, 0).uint8
|
||
|
for x in hit.int + 1 ..< r:
|
||
|
coverage[x] += (255 div q).uint8
|
||
|
yf += 1 / qf
|
||
|
|
||
|
for x in 0 ..< r:
|
||
|
let coverage = 255 - coverage[x]
|
||
|
if coverage != 255:
|
||
|
var cx: ColorRGBX
|
||
|
cx.r = ((channels[0] * coverage) div 255).uint8
|
||
|
cx.g = ((channels[1] * coverage) div 255).uint8
|
||
|
cx.b = ((channels[2] * coverage) div 255).uint8
|
||
|
cx.a = ((channels[3] * coverage) div 255).uint8
|
||
|
|
||
|
let
|
||
|
xn = r - x - 1
|
||
|
yn = r - y - 1
|
||
|
result.setRgbaUnsafe(xn, yn, cx)
|
||
|
result.setRgbaUnsafe(w1 - xn, yn, cx)
|
||
|
result.setRgbaUnsafe(w1 - xn, h1 - yn, cx)
|
||
|
result.setRgbaUnsafe(xn, h1 - yn, cx)
|
||
|
|
||
|
const r = 16
|
||
|
|
||
|
let img1 = newRoundedRectImage1(200, 200, r, color(0, 1, 0, 1))
|
||
|
img1.writeFile("rrect_current.png")
|
||
|
let img2 = newRoundedRectImage3(200, 200, r, color(0, 1, 0, 1))
|
||
|
img2.writeFile("rrect_new.png")
|
||
|
|
||
|
let (diffScore, diffImage) = diff(img1, img2)
|
||
|
echo &"score: {diffScore}"
|
||
|
diffImage.writeFile("rrect_diff.png")
|
||
|
|
||
|
timeIt "fill rounded rect via path 1":
|
||
|
for i in 0 ..< 10:
|
||
|
discard newRoundedRectImage1(200, 200, r, color(0, 1, 0, 1))
|
||
|
|
||
|
timeIt "fill rounded rect via path 1.5":
|
||
|
for i in 0 ..< 10:
|
||
|
discard newRoundedRectImage15(200, 200, r, color(0, 1, 0, 1))
|
||
|
|
||
|
timeIt "fill rounded rect via math 2":
|
||
|
for i in 0 ..< 10:
|
||
|
discard newRoundedRectImage2(200, 200, 50, color(0, 1, 0, 1))
|
||
|
|
||
|
timeIt "fill rounded rect via math 3":
|
||
|
for i in 0 ..< 10:
|
||
|
discard newRoundedRectImage3(200, 200, r, color(0, 1, 0, 1))
|