diff --git a/experiments/rounded_rect.nim b/experiments/rounded_rect.nim new file mode 100644 index 0000000..8313e3a --- /dev/null +++ b/experiments/rounded_rect.nim @@ -0,0 +1,123 @@ +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))