How to rasterize SVG to SDL.Surface in Haskell?

1

I'm using sdl2@2.2.0 and sdl2-image@2.0.0. I'd like to draw a SVG image, but sdl2-image doesn't support this format (see https://www.libsdl.org/projects/SDL_image/):

SDL_image is an image file loading library.
It loads images as SDL surfaces and textures, and supports the following formats: BMP, GIF, JPEG, LBM, PCX, PNG, PNM, TGA, TIFF, WEBP, XCF, XPM, XV

How can I load a SVG, scale/resize it and rasterize it to a SDL.Surface or SDL.Texture?

Edit:

In C++ you can use nanosvg library:

NSVGimage * svg_image = nsvgParseFromFile(filepath.c_str(), "px", 96.0);
std::vector<Uint8> img_data;
img_data.resize(width * height * 4);

NSVGrasterizer * rasterizer = nsvgCreateRasterizer()
nsvgRasterize(rasterizer,
              svg_image, 0,0,1,
              img_data.data(),
              width, height,
              width * 4);

SDL_Surface * surface = SDL_CreateRGBSurfaceFrom(
                          static_cast<void *>(img_data.data()),
                          width, height,
                          32,         // depth
                          4 * width,  // pitch
                          0x000000FF, // red mask
                          0x0000FF00, // green mask
                          0x00FF0000, // blue mask
                          0xFF000000  // alpha mask (alpha in this format)
                        );

You have to do a static_cast<void *> on the image data you pass to SDL_CreateRGBSurfaceFrom. Haskell's SDL.createRGBSurfaceFrom (sdl2 version 2.2.0) expects Data.Vector.Storable.Mutable.VectorIO Word8 as image data.

I found a promising library rasterific-svg

This package can render SVG to an image or transform it to a PDF.

You can render using drawingOfSvgDocument or renderSvgDocument. However, I don't know how to convert either of the results to Data.Vector.Storable.Mutable.VectorIO Word8.

Edit 2:

I've a running example now. However, the rendering result is not correct yet.

This SVG

SVG image

is rendered as

rendering result

{-# LANGUAGE OverloadedStrings #-}

module Main where

import Codec.Picture
import Codec.Picture.Types
import Data.Vector.Storable.Mutable (IOVector)
import Data.Vector.Generic (thaw)
import Graphics.Rasterific.Svg
       (loadCreateFontCache, renderSvgDocument)
import Graphics.Svg (loadSvgFile)

import Data.Function (fix)
import Data.Word (Word8)

import Control.Monad as CM
import qualified Data.Vector.Generic.Mutable as GM
import Data.Vector.Storable (Vector)
import qualified Data.Vector.Storable as V

import Foreign.C.Types (CInt)
import qualified SDL
import SDL.Vect (Point(P), V2(V2), V4(V4))

import Control.Concurrent (threadDelay)
import SDL (($=))

import Paths_render_svg_in_sdl2_haskell (getDataFileName)

screenWidth, screenHeight :: CInt
(screenWidth, screenHeight) = (1280, 720)

-- SDL dependency: sudo apt-get install libsdl2-dev
main :: IO ()
main = do
  SDL.initialize [SDL.InitVideo, SDL.InitTimer, SDL.InitEvents]
    -- ensure render quality
  SDL.HintRenderScaleQuality $= SDL.ScaleLinear
  do renderQuality <- SDL.get SDL.HintRenderScaleQuality
     CM.when (renderQuality /= SDL.ScaleLinear) $
       putStrLn "Warning: Linear texture filtering not enabled!"
  window <-
    SDL.createWindow
      "Load and render SVG"
      SDL.defaultWindow
      { SDL.windowPosition = SDL.Centered
      , SDL.windowInitialSize = V2 screenWidth screenHeight
      }
  SDL.showWindow window
  renderer <-
    SDL.createRenderer
      window
      (-1)
      SDL.RendererConfig
      { SDL.rendererType = SDL.AcceleratedVSyncRenderer
      , SDL.rendererTargetTexture = True
      }
  SDL.rendererDrawColor renderer $= V4 maxBound maxBound maxBound maxBound
  SDL.clear renderer
  renderSvgExample renderer
  SDL.present renderer
  threadDelay 2000000
  SDL.destroyRenderer renderer
  SDL.destroyWindow window
  SDL.quit

renderSvgExample :: SDL.Renderer -> IO ()
renderSvgExample renderer = do
  mimage <- getDataFileName "thumbs-up.svg" >>= loadSVGImage
  case mimage of
    Nothing -> putStrLn "Image convertion failed."
    (Just image) -> do
      let surfaceSize :: V2 CInt
          surfaceSize = V2 screenWidth screenHeight
      surface <- createSurfaceFromSVG image surfaceSize
      texture <- SDL.createTextureFromSurface renderer surface
      SDL.freeSurface surface
      let source = SDL.Rectangle (P $ V2 0 0) surfaceSize
          dest = SDL.Rectangle (P $ V2 0 0) surfaceSize
          angle = 0.0
          center = Nothing
          flipNone = V2 False False
      SDL.copyEx
        renderer
        texture
        (Just source)
        (Just dest)
        angle
        center
        flipNone
      SDL.destroyTexture texture

createSurfaceFromSVG :: Image PixelRGBA8 -> V2 CInt -> IO SDL.Surface
createSurfaceFromSVG image surfaceSize = do
  let rawImageData :: Vector Word8
      rawImageData = imageData image
      imWidth :: Int
      imWidth = imageWidth image
      pitch :: CInt
      pitch = fromIntegral imWidth
  mutableVector <- convertToMutableVector rawImageData
  SDL.createRGBSurfaceFrom mutableVector surfaceSize pitch SDL.RGBA8888

convertToMutableVector :: Vector Word8 -> IO (IOVector Word8)
convertToMutableVector= thaw

loadSVGImage :: FilePath -> IO (Maybe (Image PixelRGBA8))
loadSVGImage filepath = do
  mdoc <- loadSvgFile filepath
  case mdoc of
    Nothing -> return Nothing
    Just doc -> do
      cache <- loadCreateFontCache "fonty-texture-cache"
      (finalImage, _) <- renderSvgDocument cache Nothing 96 doc
      return $ Just finalImage
haskell
svg
sdl-2
haskell-stack
haskell-sdl
asked on Stack Overflow Aug 29, 2017 by maiermic • edited Aug 30, 2017 by maiermic

1 Answer

1

I haven't tested this with real code, but hopefully it's on the right track:

renderSvgDocument gets you an IO (Image PixelRGBA8) where the result type is from the JuicyPixels image library.

imageData gets you a (Storable) Vector (PixelBaseComponent PixelRGBA8) which I guess is a Vector Word8 after chasing all the types.

thaw or unsafeThaw (read the docs for important safety information) get you a mutable copy (or view, for the unsafe variant) of an immutable Vector.

answered on Stack Overflow Aug 30, 2017 by Claude

User contributions licensed under CC BY-SA 3.0