Render SVGs into textures
This commit is contained in:
parent
cfdaa6dd27
commit
cf762d96c7
162
src/zua.nim
162
src/zua.nim
|
@ -14,16 +14,28 @@ import math, sdl2, sdl2/gfx
|
|||
|
||||
import ./zua/protocol
|
||||
|
||||
type SdlError = object of CatchableError
|
||||
|
||||
template check(res: cint) =
|
||||
if res != 0:
|
||||
let msg = $sdl2.getError()
|
||||
raise newException(SdlError, msg)
|
||||
|
||||
template check(res: SDL_Return) =
|
||||
if res == SdlError:
|
||||
let msg = $sdl2.getError()
|
||||
raise newException(SdlError, msg)
|
||||
|
||||
const
|
||||
amask = uint32 0xff000000
|
||||
rmask = uint32 0x000000ff
|
||||
gmask = uint32 0x0000ff00
|
||||
bmask = uint32 0x00ff0000
|
||||
amask = uint32 0xff000000
|
||||
|
||||
type
|
||||
Pane = ref object
|
||||
svg: string
|
||||
image: Image
|
||||
texture: TexturePtr
|
||||
|
||||
type
|
||||
App = ref object
|
||||
|
@ -31,73 +43,89 @@ type
|
|||
# ctx: Context
|
||||
window: WindowPtr
|
||||
renderer: RendererPtr
|
||||
surface: SurfacePtr
|
||||
texture: TexturePtr
|
||||
# surface: SurfacePtr
|
||||
# texture: TexturePtr
|
||||
panes: Table[Hash, Pane]
|
||||
viewPort: sdl2.Rect
|
||||
zoomFactor: float
|
||||
|
||||
proc newPane(svg: string): Pane =
|
||||
Pane(svg: svg)
|
||||
proc newPane(app: App; svg: string): Pane =
|
||||
result = Pane(svg: svg)
|
||||
try:
|
||||
let (w, h) = app.window.getSize
|
||||
var
|
||||
image = decodeSvg(svg, w, h)
|
||||
dataPtr = image.data[0].addr
|
||||
surface = createRGBSurfaceFrom(
|
||||
dataPtr, cint w, h, cint 32, cint 4*w,
|
||||
rmask, gmask, bmask, amask)
|
||||
result.texture = createTextureFromSurface(app.renderer, surface)
|
||||
destroy surface
|
||||
#[
|
||||
result.texture = createTexture(
|
||||
app.renderer,
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
w div 2, h div 2)
|
||||
updateTexture(result.texture, nil, image.data[0].addr, cint 32)
|
||||
]#
|
||||
except Exception as e:
|
||||
destroyTexture(result.texture)
|
||||
stderr.writeLine "Failed to render SVG, ", e.msg
|
||||
|
||||
proc newApp(width, height: int): App =
|
||||
new result
|
||||
result = App(zoomFactor: 1.0)
|
||||
discard createWindowAndRenderer(
|
||||
cint width, cint height,
|
||||
SDL_WINDOW_RESIZABLE,
|
||||
result.window, result.renderer)
|
||||
var info: RendererInfo
|
||||
check getRendererInfo(result.renderer, addr info)
|
||||
echo "SDL Renderer: ", info.name
|
||||
|
||||
proc equalDimensions(a, b: Image): bool =
|
||||
a.width == b.width and a.height == b.height
|
||||
|
||||
proc update(app: App; pane: Pane) =
|
||||
try:
|
||||
assert(not app.screen.isNil)
|
||||
pane.image = decodeSvg(pane.svg) # , app.screen.width, app.screen.height)
|
||||
# TODO: determine the width and height from zoom level
|
||||
draw(app.screen, pane.image)
|
||||
except Exception as e:
|
||||
stderr.writeLine "Failed to render SVG, ", e.msg
|
||||
proc redraw(app: App) =
|
||||
var srcRect, dstRect: sdl2.Rect
|
||||
(srcRect.w, srcRect.h) = app.window.getSize
|
||||
dstRect.w = cint(srcRect.w.float * app.zoomFactor)
|
||||
dstRect.h = cint(srcRect.h.float * app.zoomFactor)
|
||||
app.renderer.setDrawColor(128, 128, 128)
|
||||
app.renderer.clear()
|
||||
app.renderer.setViewport(addr app.viewPort)
|
||||
for pane in app.panes.values:
|
||||
app.renderer.copy(pane.texture, nil, addr dstRect)
|
||||
app.renderer.present()
|
||||
|
||||
proc update(app: App) =
|
||||
let (w, h) = app.window.getSize
|
||||
if app.screen.isNil or app.screen.width != w or app.screen.height != h:
|
||||
app.screen = newImage(w, h)
|
||||
# app.screen = newImage(w, h)
|
||||
check app.renderer.setLogicalSize(w, h)
|
||||
app.viewPort = app.renderer.getViewport
|
||||
|
||||
#[
|
||||
app.screen.fill(rgba(255, 255, 255, 255))
|
||||
for pane in app.panes.values:
|
||||
app.update(pane)
|
||||
|
||||
var dataPtr = app.screen.data[0].addr
|
||||
var mainSurface = createRGBSurfaceFrom(
|
||||
dataPtr, cint w, cint h, cint 32, cint 4*w,
|
||||
dataPtr, cint app.screen.width, cint app.screen.height, cint 32, cint 4*w,
|
||||
rmask, gmask, bmask, amask)
|
||||
var mainTexture = app.renderer.createTextureFromSurface(mainSurface)
|
||||
destroy(mainSurface)
|
||||
destroy app.texture
|
||||
app.texture = app.renderer.createTextureFromSurface(mainSurface)
|
||||
]#
|
||||
app.redraw()
|
||||
|
||||
app.renderer.clear()
|
||||
app.renderer.copy(mainTexture, nil, nil)
|
||||
destroy(mainTexture)
|
||||
proc zoom(app: App; change: float) =
|
||||
# TODO: how does renderer scaling work?
|
||||
app.zoomFactor = app.zoomFactor * (1.0 - (0.1 * change))
|
||||
app.redraw()
|
||||
|
||||
app.renderer.present()
|
||||
|
||||
discard sdl2.init(INIT_TIMER or INIT_VIDEO or INIT_EVENTS)
|
||||
|
||||
const svgTextSimple = """
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="10cm" height="3cm" viewBox="0 0 1000 300"
|
||||
xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
<desc>Example text01 - 'Hello, out there' in blue</desc>
|
||||
<text x="250" y="150"
|
||||
font-family="Verdana" font-size="55" fill="blue" >
|
||||
Hello, out there
|
||||
</text>
|
||||
<!-- Show outline of canvas using 'rect' element -->
|
||||
<rect x="1" y="1" width="998" height="298"
|
||||
fill="none" stroke="blue" stroke-width="2" />
|
||||
</svg>
|
||||
"""
|
||||
proc pan(app: App; x, y: int) =
|
||||
app.viewPort.x.inc x
|
||||
app.viewPort.y.inc y
|
||||
app.redraw()
|
||||
|
||||
proc main() =
|
||||
discard sdl2.init(INIT_TIMER or INIT_VIDEO or INIT_EVENTS)
|
||||
let app = newApp(1024, 768)
|
||||
app.update()
|
||||
|
||||
|
@ -109,20 +137,17 @@ proc main() =
|
|||
let ds = unembed a
|
||||
|
||||
onPublish(turn, ds, protocol.Pane ? {0: `?*`()}) do (svg: string):
|
||||
echo "an SVG was published"
|
||||
onRetract:
|
||||
echo "an SVG was retracted"
|
||||
app.panes.del(hash svg)
|
||||
app.update()
|
||||
# TODO: keep dead panes around until they are cleaned up.
|
||||
# If something crashes then the last state should be visable.
|
||||
var pane = app.panes.getOrDefault(hash svg)
|
||||
if pane.isNil:
|
||||
pane = newPane(svg)
|
||||
pane = newPane(app, svg)
|
||||
app.panes[hash svg] = pane
|
||||
else:
|
||||
pane.svg = svg
|
||||
app.update(pane)
|
||||
app.update() # TODO: wait til end of turn
|
||||
|
||||
var
|
||||
|
@ -133,19 +158,40 @@ proc main() =
|
|||
sdlTimeout = 500
|
||||
asyncPollTimeout = 500
|
||||
|
||||
var evt = sdl2.defaultEvent
|
||||
asyncdispatch.poll(0)
|
||||
var
|
||||
evt = sdl2.defaultEvent
|
||||
mousePanning: bool
|
||||
while true:
|
||||
# TODO asyncdispatch.poll(0) or make an SDL event thread
|
||||
if not waitEventTimeout(evt, sdlTimeout):
|
||||
asyncdispatch.poll(0)
|
||||
else:
|
||||
if waitEventTimeout(evt, sdlTimeout):
|
||||
case evt.kind
|
||||
of QuitEvent:
|
||||
quit(0)
|
||||
of MouseWheel:
|
||||
app.zoom(evt.wheel.y.float)
|
||||
of WindowEvent:
|
||||
if evt.window.event == WindowEvent_Resized:
|
||||
app.update()
|
||||
of MouseMotion:
|
||||
if mousePanning:
|
||||
if (getModState() and (KMOD_LCTRL or KMOD_RCTRL)) == KMOD_NONE:
|
||||
app.pan(-2 * evt.motion.xrel, -2 * evt.motion.yrel)
|
||||
# TODO: panning accelation curve
|
||||
# ../../../genode/repos/os/src/server/event_filter/accelerate_source.h
|
||||
else:
|
||||
app.pan(evt.motion.xrel, evt.motion.yrel)
|
||||
of MouseButtonDown:
|
||||
case evt.button.button
|
||||
of BUTTON_MIDDLE:
|
||||
mousePanning = true
|
||||
else: discard
|
||||
of MouseButtonUp:
|
||||
case evt.button.button
|
||||
of BUTTON_MIDDLE:
|
||||
mousePanning = false
|
||||
else: discard
|
||||
of KeyUp: discard
|
||||
of KeyDown: discard
|
||||
of QuitEvent: quit(0)
|
||||
else:
|
||||
echo evt.kind
|
||||
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue