Browse Source

Rasterize shape by shape into atlas

master
Macoy Madson 5 months ago
parent
commit
075d5177be
  1. 2
      Dependencies/nanosvg
  2. 84
      src/NanoSVG.cake
  3. 92
      src/VectorPuppetShow.cake

2
Dependencies/nanosvg

@ -1 +1 @@
Subproject commit ae22b51cde85a401813c3104b94c6bcd69e580e7
Subproject commit 7229f59adc4f52180ff4afbd63204a9f726af94b

84
src/NanoSVG.cake

@ -27,7 +27,8 @@
(token-splice-rest body tokens)))
(return true))
(forward-declare (struct NSVGshape) (struct NSVGimage))
(forward-declare (struct NSVGshape) (struct NSVGimage)
(struct NSVGrasterizer))
;; This isn't 100% accurate I think, but does seem to make containment likely
(defun nanosvg-get-shape-bounds-with-stroke-int (shape (addr NSVGshape) bounds-out (addr int))
@ -67,7 +68,7 @@
(export-and-evaluate
(add-c-search-directory-module "Dependencies/stb")
(c-import "stb_rect_pack.h"))
(c-import "<stdlib.h>")
(c-import "<stdlib.h>" "<stdio.h>")
(forward-declare (struct stbrp_rect))
(defun nanosvg-pack-into-atlas (image (addr NSVGimage)
@ -95,14 +96,89 @@
(each-shape-in-svg-image image shape
(unless (bit-and (path shape > flags) NSVG_FLAGS_VISIBLE)
(continue))
(var sized-bounds (array 4 float) (array 0))
(nanosvg-get-shape-bounds-with-stroke-float shape sized-bounds)
(var rectangle (addr stbrp_rect) (addr (at shape-index rectangles)))
(set-fields (deref rectangle)
id shape-index
w (- (at 2 (path shape > bounds)) (at 0 (path shape > bounds)))
h (- (at 3 (path shape > bounds)) (at 1 (path shape > bounds))))
w (- (at 2 sized-bounds) (at 0 sized-bounds))
h (- (at 3 sized-bounds) (at 1 sized-bounds)))
(incr shape-index))
(unless (stbrp_pack_rects (addr context) rectangles num-shapes)
(return false))
(set (deref rectangles-out) rectangles)
(return true))
;; Caller needs to free rectangles-out and atlas-out once they are finished with the atlas
;; Note: temporarily modifies image during rasterization
(defun nanosvg-rasterize-atlas (rasterizer (addr NSVGrasterizer)
image (addr NSVGimage)
max-texture-size int
rectangles-out (addr (addr stbrp_rect))
atlas-out (addr (addr (unsigned char)))
width-out (addr int) height-out (addr int)
&return bool)
(var attempt-pack-width int 256)
(var attempt-pack-height int 256)
(var packed-successfully bool true)
(var packed-rectangles (addr stbrp_rect) null)
(while (= 0 (nanosvg-pack-into-atlas
image
attempt-pack-width
attempt-pack-height
(addr packed-rectangles)))
;; TODO: We could reuse these each attempt
(free packed-rectangles)
(set attempt-pack-width (* 2 attempt-pack-width))
(set attempt-pack-height (* 2 attempt-pack-height))
(when (> attempt-pack-width max-texture-size)
(fprintf stderr "Failed to pack SVG shapes into %dx%d texture\n"
attempt-pack-width
attempt-pack-height)
(set packed-successfully false)
(break)))
(unless packed-successfully
(free packed-rectangles)
(return false))
;; Let's rasterize the SVG now
(var atlas-buffer-size size_t (* attempt-pack-width attempt-pack-height 4))
(var-cast-to atlas-buffer (addr (unsigned char))
(malloc atlas-buffer-size))
(memset atlas-buffer 0 atlas-buffer-size)
(var current-rect (addr stbrp_rect) packed-rectangles)
(each-shape-in-svg-image image shape
(unless (bit-and (path shape > flags) NSVG_FLAGS_VISIBLE)
(continue))
(var sized-bounds (array 4 float) (array 0))
(nanosvg-get-shape-bounds-with-stroke-float shape sized-bounds)
(nsvgRasterizeEx
rasterizer image shape
;; Need to translate negatively
(- 0 (at 0 sized-bounds))
(- 0 (at 1 sized-bounds))
1.f
;; atlas-buffer
(+ atlas-buffer
(* 4
(+ (path current-rect > x)
(* (path current-rect > y)
attempt-pack-width))))
(type-cast
(- (at 2 sized-bounds)
(at 0 sized-bounds))
int)
(type-cast
(- (at 3 sized-bounds)
(at 1 sized-bounds))
int)
;; Stride becomes the whole atlas size
(* attempt-pack-width 4))
(incr current-rect))
(set (deref rectangles-out) packed-rectangles)
(set (deref atlas-out) atlas-buffer)
(set (deref width-out) attempt-pack-width)
(set (deref height-out) attempt-pack-height)
(return true))

92
src/VectorPuppetShow.cake

@ -154,6 +154,10 @@
(SDL_DestroyTexture svg-image-texture)))
(var packed-rectangles (addr stbrp_rect) null)
(defer (when packed-rectangles (free packed-rectangles)))
(var puppet-atlas-texture (addr SDL_Texture) null)
(defer (when puppet-atlas-texture (SDL_DestroyTexture puppet-atlas-texture)))
(var puppet-atlas-width int 0)
(var puppet-atlas-height int 0)
(scope
(var filename (addr (const char)) "data/TestPuppet.svg")
(set puppet-image (nsvgParseFromFile filename "px" 96.0f))
@ -179,27 +183,39 @@
(at 2 (path shape > bounds))
(at 3 (path shape > bounds))))
(var puppet-atlas-buffer (addr (unsigned char)) null)
(defer (when puppet-atlas-buffer (free puppet-atlas-buffer)))
(var max-texture-size int 4096)
(var attempt-pack-width int 256)
(var attempt-pack-height int 256)
(var packed-successfully bool true)
(while (= 0 (nanosvg-pack-into-atlas
puppet-image
attempt-pack-width
attempt-pack-height
(addr packed-rectangles)))
(free packed-rectangles)
(set attempt-pack-width (* 2 attempt-pack-width))
(set attempt-pack-height (* 2 attempt-pack-height))
(when (> attempt-pack-width max-texture-size)
(vpslog "Failed to pack image into atlas\n")
(set packed-successfully false)
(break)))
(var packed-successfully bool
(nanosvg-rasterize-atlas rasterizer
puppet-image max-texture-size
(addr packed-rectangles)
(addr puppet-atlas-buffer)
(addr puppet-atlas-width)
(addr puppet-atlas-height)))
(when packed-successfully
(vpslog "Packed SVG shapes into %dx%d texture\n" attempt-pack-width
attempt-pack-height))
(nsvgRasterize rasterizer puppet-image 0 0 1 image-buffer
(vpslog "Packed SVG shapes into %dx%d texture\n" puppet-atlas-width
puppet-atlas-height)
(var image-surface (addr SDL_Surface)
(SDL_CreateRGBSurfaceWithFormatFrom
puppet-atlas-buffer
puppet-atlas-width
puppet-atlas-height
32 ;; bit depth of each pixel (RGBA)
(* puppet-atlas-width 4) ;; pitch (width of a row in bytes)
;; Because of how SDL reads pixels in, this does seem to be endian-dependent
;; I don't fully understand why
SDL_PIXELFORMAT_RGBA32))
(unless image-surface
(sdl-print-error)
(return 1))
(defer (SDL_FreeSurface image-surface)) ;; We don't need this after making the texture
(set puppet-atlas-texture (SDL_CreateTextureFromSurface renderer image-surface))
(unless puppet-atlas-texture
(sdl-print-error)
(return 1)))
(nsvgRasterize rasterizer puppet-image 0.f 0.f 1.f image-buffer
(path puppet-image > width) (path puppet-image > height)
(* 4 (path puppet-image > width)))
(var image-surface (addr SDL_Surface)
@ -274,25 +290,35 @@
(SDL_SetRenderDrawColor renderer 11 11 11 255)
(SDL_RenderClear renderer)
(scope
(var source-rectangle SDL_Rect
(array 0
0
svg-image-width
svg-image-height))
(var destination-rectangle SDL_Rect
(array
0
0
svg-image-width
svg-image-height))
(SDL_RenderCopy renderer svg-image-texture
(addr source-rectangle) (addr destination-rectangle)))
;; (scope ;; Draw entire SVG
;; (var source-rectangle SDL_Rect
;; (array 0
;; 0
;; svg-image-width
;; svg-image-height))
;; (var destination-rectangle SDL_Rect
;; (array
;; 0
;; 0
;; svg-image-width
;; svg-image-height))
;; (SDL_RenderCopy renderer svg-image-texture
;; (addr source-rectangle) (addr destination-rectangle)))
(var mouse-x int 0)
(var mouse-y int 0)
(SDL_GetMouseState (addr mouse-x) (addr mouse-y))
(when puppet-atlas-texture
(var atlas-rectangle SDL_Rect
(array 0
0
puppet-atlas-width
puppet-atlas-height))
(unless (= 0 (SDL_RenderCopy renderer puppet-atlas-texture (addr atlas-rectangle) (addr atlas-rectangle)))
(sdl-print-error)
(set exit-reason "Failed to render atlas texture")))
(scope ;; Shape selection and debug viewing
(var hovered-shape-id-buffer (array 1024 char) (array 0))
(var hovered-write-head (addr char) hovered-shape-id-buffer)

Loading…
Cancel
Save