commit
504429c752
4 changed files with 98 additions and 142 deletions
35
experiments/benchmark_cairo.nim
Normal file
35
experiments/benchmark_cairo.nim
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import cairo, math, benchy, pixie, chroma
|
||||||
|
|
||||||
|
var
|
||||||
|
surface = imageSurfaceCreate(FORMAT_ARGB32, 1000, 1000)
|
||||||
|
ctx = surface.create()
|
||||||
|
|
||||||
|
ctx.setSourceRgba(0, 0, 0, 1)
|
||||||
|
ctx.fill()
|
||||||
|
ctx.setSourceRgba(0, 0, 1, 1)
|
||||||
|
|
||||||
|
timeIt "cairo":
|
||||||
|
ctx.newPath()
|
||||||
|
ctx.moveTo(0, 0)
|
||||||
|
ctx.lineTo(500, 0)
|
||||||
|
ctx.lineTo(500, 500)
|
||||||
|
ctx.lineTo(0, 500)
|
||||||
|
ctx.closePath()
|
||||||
|
ctx.fill()
|
||||||
|
surface.flush()
|
||||||
|
|
||||||
|
discard surface.writeToPng("cairo.png")
|
||||||
|
|
||||||
|
var a = newImage(1000, 1000)
|
||||||
|
a.fill(rgba(0, 0, 0, 255))
|
||||||
|
|
||||||
|
timeIt "pixie":
|
||||||
|
let p = newPath()
|
||||||
|
p.moveTo(0, 0)
|
||||||
|
p.lineTo(500, 0)
|
||||||
|
p.lineTo(500, 500)
|
||||||
|
p.lineTo(0, 500)
|
||||||
|
p.closePath()
|
||||||
|
a.fillPath(p, rgba(0, 0, 255, 255))
|
||||||
|
|
||||||
|
discard surface.writeToPng("pixie.png")
|
|
@ -1,28 +0,0 @@
|
||||||
import cairo, math, times
|
|
||||||
|
|
||||||
var
|
|
||||||
surface = imageSurfaceCreate(FORMAT_ARGB32, 256, 256)
|
|
||||||
ctx = surface.create()
|
|
||||||
|
|
||||||
let start = epochTime()
|
|
||||||
|
|
||||||
ctx.setSourceRGB(0, 0, 1)
|
|
||||||
ctx.newPath() # current path is not consumed by ctx.clip()
|
|
||||||
ctx.rectangle(96, 96, 128, 128)
|
|
||||||
ctx.fill()
|
|
||||||
|
|
||||||
ctx.setSourceRGB(0, 1, 0)
|
|
||||||
ctx.newPath() # current path is not consumed by ctx.clip()
|
|
||||||
ctx.rectangle(64, 64, 128, 128)
|
|
||||||
ctx.fill()
|
|
||||||
|
|
||||||
for i in 0 .. 10000:
|
|
||||||
|
|
||||||
ctx.setSourceRGB(1, 0, 0)
|
|
||||||
ctx.newPath() # current path is not consumed by ctx.clip()
|
|
||||||
ctx.rectangle(32, 32, 128, 128)
|
|
||||||
ctx.fill()
|
|
||||||
|
|
||||||
echo epochTime() - start
|
|
||||||
|
|
||||||
discard surface.writeToPng("cairotest.png")
|
|
|
@ -1,57 +0,0 @@
|
||||||
import staticglfw, opengl, pixie, times
|
|
||||||
|
|
||||||
if init() == 0:
|
|
||||||
raise newException(Exception, "Failed to Initialize GLFW")
|
|
||||||
windowHint(VISIBLE, false.cint)
|
|
||||||
var window = createWindow(512, 512, "GLFW3 WINDOW", nil, nil)
|
|
||||||
window.makeContextCurrent()
|
|
||||||
# This must be called to make any GL function work
|
|
||||||
loadExtensions()
|
|
||||||
|
|
||||||
|
|
||||||
let start = epochTime()
|
|
||||||
|
|
||||||
# Draw red color screen.
|
|
||||||
glClearColor(1, 1, 1, 1)
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT)
|
|
||||||
|
|
||||||
glLoadIdentity()
|
|
||||||
glTranslatef(-0.25, -0.25, 0)
|
|
||||||
glBegin(GL_QUADS)
|
|
||||||
glColor3f(1.0, 0.0, 0.0)
|
|
||||||
glVertex2f(0.0, 0.0)
|
|
||||||
glVertex2f(1.0, 0.0)
|
|
||||||
glVertex2f(1.0, 1.0)
|
|
||||||
glVertex2f(0.0, 1.0)
|
|
||||||
glEnd()
|
|
||||||
|
|
||||||
glTranslatef(-0.25, -0.25, 0)
|
|
||||||
glBegin(GL_QUADS)
|
|
||||||
glColor3f(0.0, 0.0, 1.0)
|
|
||||||
glVertex2f(0.0, 0.0)
|
|
||||||
glVertex2f(1.0, 0.0)
|
|
||||||
glVertex2f(1.0, 1.0)
|
|
||||||
glVertex2f(0.0, 1.0)
|
|
||||||
glEnd()
|
|
||||||
|
|
||||||
glTranslatef(-0.25, -0.25, 0)
|
|
||||||
glBegin(GL_QUADS)
|
|
||||||
glColor3f(0.0, 1.0, 0.0)
|
|
||||||
glVertex2f(0.0, 0.0)
|
|
||||||
glVertex2f(1.0, 0.0)
|
|
||||||
glVertex2f(1.0, 1.0)
|
|
||||||
glVertex2f(0.0, 1.0)
|
|
||||||
glEnd()
|
|
||||||
|
|
||||||
var screen = newImage(512, 512)
|
|
||||||
glReadPixels(
|
|
||||||
0, 0,
|
|
||||||
512, 512,
|
|
||||||
GL_RGBA, GL_UNSIGNED_BYTE,
|
|
||||||
screen.data[0].addr
|
|
||||||
)
|
|
||||||
|
|
||||||
echo epochTime() - start
|
|
||||||
|
|
||||||
|
|
||||||
screen.writeFile("screen.png")
|
|
|
@ -5,72 +5,78 @@ import chroma, pixie/images, pixie/common, pixie/paths, vmath, xmlparser, xmltre
|
||||||
|
|
||||||
const svgSignature* = "<?xml"
|
const svgSignature* = "<?xml"
|
||||||
|
|
||||||
|
template failInvalid() =
|
||||||
|
raise newException(PixieError, "Invalid SVG data")
|
||||||
|
|
||||||
proc draw(img: Image, matStack: var seq[Mat3], xml: XmlNode) =
|
proc draw(img: Image, matStack: var seq[Mat3], xml: XmlNode) =
|
||||||
case xml.tag:
|
if xml.tag != "g":
|
||||||
of "g":
|
raise newException(PixieError, "Unsupported SVG tag: " & xml.tag & ".")
|
||||||
let
|
|
||||||
fill = xml.attr("fill")
|
|
||||||
stroke = xml.attr("stroke")
|
|
||||||
strokeWidth = xml.attr("stroke-width")
|
|
||||||
transform = xml.attr("transform")
|
|
||||||
|
|
||||||
if transform != "":
|
let
|
||||||
if transform.startsWith("matrix("):
|
fill = xml.attr("fill")
|
||||||
let arr = transform[7..^2].split(",")
|
stroke = xml.attr("stroke")
|
||||||
assert arr.len == 6
|
strokeWidth = xml.attr("stroke-width")
|
||||||
var m = mat3()
|
transform = xml.attr("transform")
|
||||||
m[0] = parseFloat(arr[0])
|
|
||||||
m[1] = parseFloat(arr[1])
|
|
||||||
m[3] = parseFloat(arr[2])
|
|
||||||
m[4] = parseFloat(arr[3])
|
|
||||||
m[6] = parseFloat(arr[4])
|
|
||||||
m[7] = parseFloat(arr[5])
|
|
||||||
matStack.add(matStack[^1] * m)
|
|
||||||
else:
|
|
||||||
raise newException(
|
|
||||||
PixieError, "Unsupported SVG transform: " & transform & ".")
|
|
||||||
|
|
||||||
for child in xml:
|
|
||||||
if child.tag == "path":
|
|
||||||
let d = child.attr("d")
|
|
||||||
|
|
||||||
if fill != "none" and fill != "":
|
|
||||||
let fillColor = parseHtmlColor(fill).rgba
|
|
||||||
let (bounds, fillImg) =
|
|
||||||
fillPathBounds(d, fillColor, mat = matStack[^1])
|
|
||||||
img.draw(fillImg, bounds.xy)
|
|
||||||
|
|
||||||
if stroke != "none" and stroke != "":
|
|
||||||
let strokeColor = parseHtmlColor(stroke).rgba
|
|
||||||
let strokeWidth =
|
|
||||||
if strokeWidth == "": 1.0 # Default stroke width is 1px
|
|
||||||
else: parseFloat(strokeWidth)
|
|
||||||
let (bounds, strokeImg) =
|
|
||||||
strokePathBounds(d, strokeColor, strokeWidth, mat = matStack[^1])
|
|
||||||
img.draw(strokeImg, bounds.xy)
|
|
||||||
|
|
||||||
else:
|
|
||||||
img.draw(matStack, child)
|
|
||||||
|
|
||||||
if transform != "":
|
|
||||||
discard matStack.pop()
|
|
||||||
|
|
||||||
|
if transform != "":
|
||||||
|
if transform.startsWith("matrix("):
|
||||||
|
let arr = transform[7..^2].split(",")
|
||||||
|
if arr.len != 6:
|
||||||
|
failInvalid()
|
||||||
|
var m = mat3()
|
||||||
|
m[0] = parseFloat(arr[0])
|
||||||
|
m[1] = parseFloat(arr[1])
|
||||||
|
m[3] = parseFloat(arr[2])
|
||||||
|
m[4] = parseFloat(arr[3])
|
||||||
|
m[6] = parseFloat(arr[4])
|
||||||
|
m[7] = parseFloat(arr[5])
|
||||||
|
matStack.add(matStack[^1] * m)
|
||||||
else:
|
else:
|
||||||
raise newException(PixieError, "Unsupported SVG tag: " & xml.tag & ".")
|
raise newException(
|
||||||
|
PixieError, "Unsupported SVG transform: " & transform & ".")
|
||||||
|
|
||||||
|
for child in xml:
|
||||||
|
if child.tag == "path":
|
||||||
|
let d = child.attr("d")
|
||||||
|
|
||||||
|
if fill != "none" and fill != "":
|
||||||
|
let
|
||||||
|
fillColor = parseHtmlColor(fill).rgba
|
||||||
|
(bounds, fillImg) = fillPathBounds(d, fillColor, matStack[^1])
|
||||||
|
img.draw(fillImg, bounds.xy)
|
||||||
|
|
||||||
|
if stroke != "none" and stroke != "":
|
||||||
|
let
|
||||||
|
strokeColor = parseHtmlColor(stroke).rgba
|
||||||
|
strokeWidth =
|
||||||
|
if strokeWidth == "": 1.0 # Default stroke width is 1px
|
||||||
|
else: parseFloat(strokeWidth)
|
||||||
|
(bounds, strokeImg) =
|
||||||
|
strokePathBounds(d, strokeColor, strokeWidth, matStack[^1])
|
||||||
|
img.draw(strokeImg, bounds.xy)
|
||||||
|
else:
|
||||||
|
img.draw(matStack, child)
|
||||||
|
|
||||||
|
if transform != "":
|
||||||
|
discard matStack.pop()
|
||||||
|
|
||||||
proc decodeSvg*(data: string): Image =
|
proc decodeSvg*(data: string): Image =
|
||||||
## Render SVG file and return the image.
|
## Render SVG file and return the image.
|
||||||
try:
|
try:
|
||||||
var xml = parseXml(data)
|
let xml = parseXml(data)
|
||||||
assert xml.tag == "svg"
|
if xml.tag != "svg":
|
||||||
var viewBox = xml.attr "viewBox"
|
failInvalid()
|
||||||
let box = viewBox.split(" ")
|
|
||||||
assert parseInt(box[0]) == 0
|
|
||||||
assert parseInt(box[1]) == 0
|
|
||||||
let w = parseInt(box[2])
|
|
||||||
let h = parseInt(box[3])
|
|
||||||
result = newImage(w, h)
|
|
||||||
|
|
||||||
|
let
|
||||||
|
viewBox = xml.attr("viewBox")
|
||||||
|
box = viewBox.split(" ")
|
||||||
|
if parseInt(box[0]) != 0 or parseInt(box[1]) != 0:
|
||||||
|
failInvalid()
|
||||||
|
|
||||||
|
let
|
||||||
|
width = parseInt(box[2])
|
||||||
|
height = parseInt(box[3])
|
||||||
|
result = newImage(width, height)
|
||||||
var matStack = @[mat3()]
|
var matStack = @[mat3()]
|
||||||
for n in xml:
|
for n in xml:
|
||||||
result.draw(matStack, n)
|
result.draw(matStack, n)
|
||||||
|
|
Loading…
Reference in a new issue