2017-08-29 60 views
1

我使用[email protected][email protected]。我想画一个SVG图像,但SDL2图像不支持此格式(见https://www.libsdl.org/projects/SDL_image/):如何将SVG栅格化为Haskell中的SDL.Surface?

SDL_image是图像文件加载库。
它加载图像作为SDL表面和纹理,并支持以下格式:BMP,GIF,JPEG,LBM,PCX,PNG,PNM,TGA,TIFF,WEBP,XCF,XPM,XV

如何我加载一个SVG,缩放/调整它的大小并将其光栅化为SDL.SurfaceSDL.Texture

编辑:

在C++中,你可以使用nanosvg库:

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) 
         ); 

您必须对您传递给SDL_CreateRGBSurfaceFrom图像数据做了static_cast<void *>。 Haskell的SDL.createRGBSurfaceFrom(sdl2版本2.2.0)预计Data.Vector.Storable.Mutable.VectorIO Word8作为图像数据。

我找到了一个有希望的库rasterific-svg

该软件包可以使SVG图像或将其转换为PDF。

您可以使用drawingOfSvgDocumentrenderSvgDocument进行渲染。但是,我不知道如何将结果转换为Data.Vector.Storable.Mutable.VectorIO Word8

编辑2:

我已经运行example现在。但是,渲染结果还不正确。

SVG

SVG image

呈现为

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 
+0

你有什么试过? “sdl2-image不支持这种格式”是什么意思?如果你认为应该可以使用这些库,我猜你可以编写相应的C程序 - 如果有的话,可以随意包含该程序。 – user2407038

+0

我已经添加了一个使用nanosvg的C++解决方案,并引用了SDL2 Image支持的图像格式。 – maiermic

+0

因为这是一个图书馆推荐问题而关闭的原因,这是SO的主题。只需谷歌“Haskell SVG渲染器”,你应该找到一些选择。 – Cubic

回答

1

我还没有真正的代码测试这一点,但希望这是在正确的轨道上:

renderSvgDocument为您提供IO (Image PixelRGBA8),其中结果类型来自JuicyPixels图像库。

imageData让你我的猜测是追逐所有类型后Vector Word8一(StorableVector (PixelBaseComponent PixelRGBA8)

thawunsafeThaw(请阅读重要安全信息的文档)为您提供不可变的Vector的可变副本(或查看不安全变体)。

+0

我已经根据您的建议在我的问题中添加了一个运行示例。但是,它没有呈现正确的结果。我想我必须以某种方式转换像素数据。 – maiermic

+1

@maiermic我认为你可能只需要将宽度设置为宽度的4倍,就像在你的C++示例中一样 – Claude

+0

你是对的。我忽略了这一点。我也忽略了使用'ABGR8888'而不是'RGBA8888' – maiermic