import Control.Monad( forM_ ) import Control.Monad.ST( runST ) import Codec.Picture import Codec.Picture.Types( promoteImage ) import Graphics.Text.TrueType( loadFontFile ) import Graphics.Rasterific import Graphics.Rasterific.Outline import Graphics.Rasterific.Texture import Graphics.Rasterific.Transformations import Graphics.Rasterific.MeshPatch import Graphics.Rasterific.Immediate import System.Directory( createDirectoryIfMissing ) import System.FilePath( () ) import qualified Data.ByteString.Lazy as LB import qualified Data.Vector as V import Graphics.Rasterific.Linear( (^+^) ) logo :: Int -> Bool -> Vector -> [Primitive] logo size inv offset = toPrimitives . bezierFromPath . way $ map (^+^ offset) [ (V2 0 is) , (V2 0 0) , (V2 is 0) , (V2 is2 0) , (V2 is2 is) , (V2 is2 is2) , (V2 is is2) , (V2 0 is2) , (V2 0 is) ] where is = fromIntegral size is2 = is + is way | inv = reverse | otherwise = id backgroundColor :: PixelRGBA8 backgroundColor = PixelRGBA8 255 255 255 255 frontColor, accentColor, accent2Color :: PixelRGBA8 frontColor = PixelRGBA8 0 0x86 0xc1 255 accentColor = PixelRGBA8 0xff 0xf4 0xc1 255 accent2Color = PixelRGBA8 0xFF 0x53 0x73 255 frontTexture, accentTexture, accent2Texture :: Texture PixelRGBA8 frontTexture = uniformTexture frontColor accentTexture = uniformTexture accentColor accent2Texture = uniformTexture accent2Color produceDocImageAtSize :: Int -> Int -> FilePath -> Drawing PixelRGBA8 () -> IO () produceDocImageAtSize width height filename drawing = do putStrLn $ "Producing " <> filename writePng filename img writePdf $ filename <> ".draw.pdf" writeOrderPdf $ filename <> ".order.pdf" where img = renderDrawing width height backgroundColor $ withTexture frontTexture drawing writeOrderPdf fname = LB.writeFile fname . renderOrdersAtDpiToPdf width height 92 $ drawOrdersOfDrawing width height 92 (PixelRGBA8 0 0 0 0) $ withTexture frontTexture drawing writePdf fname = LB.writeFile fname . renderDrawingAtDpiToPDF width height 92 $ withTexture frontTexture drawing produceDocImage :: FilePath -> Drawing PixelRGBA8 () -> IO () produceDocImage = produceDocImageAtSize 200 200 capTester :: (FilePath, Cap) -> IO () capTester (filename, cap) = produceDocImage filename $ do stroke 30 JoinRound (cap, cap) base_stroke withTexture accentTexture $ stroke 2 JoinRound (cap, cap) base_stroke where base_stroke = line (V2 0 200) (V2 100 100) joinTester :: (FilePath, Join) -> IO () joinTester (filename, join) = produceDocImage filename $ do stroke 30 join (CapRound, CapRound) base_stroke withTexture accentTexture $ stroke 2 join (CapRound, CapRound) base_stroke where base_stroke = [ Line (V2 0 200) (V2 100 100) , Line (V2 100 100) (V2 200 200) ] samplerTester :: (FilePath, SamplerRepeat) -> IO () samplerTester (filename, sampler) = produceDocImage filename $ (let gradDef = [(0, PixelRGBA8 0 0x86 0xc1 255) ,(0.5, PixelRGBA8 0xff 0xf4 0xc1 255) ,(1, PixelRGBA8 0xFF 0x53 0x73 255)] in withTexture (withSampler sampler $ linearGradientTexture gradDef (V2 80 100) (V2 120 110)) $ fill $ rectangle (V2 10 10) 180 180) outFolder :: FilePath outFolder = "docimages" moduleExample :: IO () moduleExample = do let drawColor = PixelRGBA8 0 0x86 0xc1 255 recColor = PixelRGBA8 0xFF 0x53 0x73 255 img = withTexture (uniformTexture drawColor) $ do fill $ circle (V2 0 0) 30 stroke 4 JoinRound (CapRound, CapRound) $ circle (V2 400 200) 40 withTexture (uniformTexture recColor) . fill $ rectangle (V2 100 100) 200 100 produceDocImageAtSize 400 200 (outFolder "module_example.png") img sansSerifFont :: FilePath sansSerifFont = "test_fonts/DejaVuSans.ttf" monospaceFont :: FilePath monospaceFont = "test_fonts/DejaVuSansMono.ttf" textOnPathExample :: IO () textOnPathExample = do fontErr <- loadFontFile sansSerifFont case fontErr of Left err -> putStrLn err Right font -> let path = Path (V2 100 180) False [PathCubicBezierCurveTo (V2 20 20) (V2 170 20) (V2 300 200)] in produceDocImage (outFolder "text_on_path.png") $ do stroke 3 JoinRound (CapStraight 0, CapStraight 0) path withTexture (uniformTexture $ PixelRGBA8 0 0 0 255) $ do withPathOrientation path 0 $ printTextAt font (PointSize 24) (V2 0 0) "Text on path" geometryOnPath :: IO () geometryOnPath = do fontErr <- loadFontFile sansSerifFont case fontErr of Left err -> putStrLn err Right font -> produceDocImage (outFolder "geometry_on_path.png") $ do let path = Path (V2 100 180) False [PathCubicBezierCurveTo (V2 20 20) (V2 170 20) (V2 300 200)] withTexture (uniformTexture $ PixelRGBA8 0 0 0 255) $ stroke 3 JoinRound (CapStraight 0, CapStraight 0) path withPathOrientation path 0 $ do printTextAt font (PointSize 24) (V2 0 0) "TX" fill $ rectangle (V2 (-10) (-10)) 30 20 fill $ rectangle (V2 45 0) 10 20 fill $ rectangle (V2 60 (-10)) 20 20 fill $ rectangle (V2 100 (-15)) 20 50 textExample :: IO () textExample = do fontErr <- loadFontFile sansSerifFont case fontErr of Left err -> putStrLn err Right font -> produceDocImageAtSize 300 70 (outFolder "text_example.png") . withTexture (uniformTexture $ PixelRGBA8 0 0 0 255) $ printTextAt font (PointSize 12) (V2 20 40) "A simple text test!" textMultipleExample :: IO () textMultipleExample = do eitherFont1 <- loadFontFile sansSerifFont eitherFont2 <- loadFontFile monospaceFont case (,) <$> eitherFont1 <*> eitherFont2 of Left err -> putStrLn err Right (font1, font2) -> produceDocImageAtSize 300 70 (outFolder "text_complex_example.png") $ let blackTexture = Just . uniformTexture $ PixelRGBA8 0 0 0 255 redTexture = Just . uniformTexture $ PixelRGBA8 255 0 0 255 in printTextRanges (V2 20 40) [ TextRange font1 (PointSize 12) "A complex " blackTexture , TextRange font2 (PointSize 8) "text test" redTexture] coordinateSystem :: IO () coordinateSystem = do fontErr <- loadFontFile sansSerifFont case fontErr of Left err -> putStrLn err Right font -> produceDocImage (outFolder "coordinate.png") $ create font where black = PixelRGBA8 0 0 0 255 stroker = stroke 4 JoinRound (CapStraight 0, CapStraight 0) create font = withTexture (uniformTexture black) $ do stroker $ line (V2 10 40) (V2 190 40) stroker $ line (V2 40 10) (V2 40 190) printTextAt font (PointSize 12) (V2 4 37) "(0,0)" printTextAt font (PointSize 12) (V2 100 37) "(width, 0)" printTextAt font (PointSize 12) (V2 57 190) "(0, height)" fill $ Path (V2 170 30) True [PathLineTo (V2 195 40), PathLineTo (V2 170 50)] fill $ Path (V2 30 170) True [PathLineTo (V2 40 195), PathLineTo (V2 50 170)] fillingSample :: FillMethod -> Drawing px () fillingSample fillMethod = fillWithMethod fillMethod geometry where geometry = transform (applyTransformation $ scale 0.35 0.4 <> translate (V2 (-80) (-180))) [ Path (V2 484 499) True [ PathCubicBezierCurveTo (V2 681 452) (V2 639 312) (V2 541 314) , PathCubicBezierCurveTo (V2 327 337) (V2 224 562) (V2 484 499) ] , Path (V2 136 377) True [ PathCubicBezierCurveTo (V2 244 253) (V2 424 420) (V2 357 489) , PathCubicBezierCurveTo (V2 302 582) (V2 47 481) (V2 136 377) ] , Path (V2 340 265) True [ PathCubicBezierCurveTo (V2 64 371) (V2 128 748) (V2 343 536) , PathCubicBezierCurveTo (V2 668 216) (V2 17 273) (V2 367 575) , PathCubicBezierCurveTo (V2 589 727) (V2 615 159) (V2 340 265) ] ] immediateDrawExample :: Image PixelRGBA8 immediateDrawExample = runST $ runDrawContext 200 200 (PixelRGBA8 0 0 0 255) $ fillWithTexture FillWinding texture geometry where circlePrimitives = circle (V2 100 100) 50 geometry = strokize 4 JoinRound (CapRound, CapRound) circlePrimitives texture = uniformTexture (PixelRGBA8 255 255 255 255) immediateDrawMaskExample :: Image PixelRGBA8 immediateDrawMaskExample = runST $ runDrawContext 200 200 (PixelRGBA8 0 0 0 255) $ forM_ [1 .. 10] $ \ix -> fillWithTextureAndMask FillWinding texture mask $ rectangle (V2 10 (ix * 18 - 5)) 180 13 where texture = uniformTexture $ PixelRGBA8 0 0x86 0xc1 255 mask = sampledImageTexture $ runST $ runDrawContext 200 200 0 $ fillWithTexture FillWinding (uniformTexture 255) maskGeometry maskGeometry = strokize 15 JoinRound (CapRound, CapRound) $ circle (V2 100 100) 80 main :: IO () main = do let addFolder (p, v) = (outFolder p, v) createDirectoryIfMissing True outFolder moduleExample mapM_ (capTester . addFolder) [ ("cap_straight.png", CapStraight 0) , ("cap_straight_1.png", CapStraight 1) , ("cap_round.png", CapRound) ] mapM_ (joinTester . addFolder) [ ("join_round.png", JoinRound) , ("join_miter.png", JoinMiter 0) , ("join_miter_5.png", JoinMiter 5) ] mapM_ (samplerTester . addFolder) [ ("sampler_pad.png", SamplerPad) , ("sampler_repeat.png", SamplerRepeat) , ("sampler_reflect.png", SamplerReflect) ] writePng (outFolder "immediate_fill.png") immediateDrawExample writePng (outFolder "immediate_mask.png") immediateDrawMaskExample produceDocImage (outFolder "fill_circle.png") $ fill $ circle (V2 100 100) 75 produceDocImage (outFolder "fill_ellipse.png") $ fill $ ellipse (V2 100 100) 75 30 produceDocImage (outFolder "stroke_circle.png") $ stroke 5 JoinRound (CapRound, CapRound) $ circle (V2 100 100) 75 produceDocImage (outFolder "dashed_stroke.png") $ dashedStroke [5, 10, 5] 3 JoinRound (CapRound, CapStraight 0) $ line (V2 0 100) (V2 200 100) produceDocImage (outFolder "dashed_stroke_with_offset.png") $ dashedStrokeWithOffset 3 [5, 10, 5] 3 JoinRound (CapRound, CapStraight 0) $ line (V2 0 100) (V2 200 100) produceDocImage (outFolder "fill_rect.png") $ fill $ rectangle (V2 30 30) 150 100 produceDocImage (outFolder "with_texture.png") $ withTexture frontTexture $ do fill $ circle (V2 50 50) 20 fill $ circle (V2 100 100) 20 withTexture accent2Texture $ fill $ circle (V2 150 150) 20 produceDocImage (outFolder "strokize_path.png") $ stroke 3 (JoinMiter 0) (CapStraight 0, CapStraight 0) $ strokize 40 JoinRound (CapRound, CapRound) $ CubicBezier (V2 40 160) (V2 40 40) (V2 160 40) (V2 160 160) produceDocImage (outFolder "strokize_dashed_path.png") $ mapM_ (stroke 3 (JoinMiter 0) (CapStraight 0, CapStraight 0)) $ dashedStrokize 0 [10, 5] 40 JoinRound (CapStraight 0, CapStraight 0) [CubicBezier (V2 40 160) (V2 40 40) (V2 160 40) (V2 160 160)] produceDocImage (outFolder "with_clipping.png") $ withClipping (fill $ circle (V2 100 100) 75) $ mapM_ (stroke 7 JoinRound (CapRound, CapRound)) [line (V2 0 yf) (V2 200 (yf + 10)) | y <- [5 :: Int, 17 .. 200] , let yf = fromIntegral y ] {-produceDocImage (outFolder "clip_compose.png") $-} produceDocImage (outFolder "stroke_line.png") $ stroke 17 JoinRound (CapRound, CapRound) $ line (V2 10 10) (V2 180 170) produceDocImage (outFolder "linear_gradient.png") $ (let gradDef = [(0, PixelRGBA8 0 0x86 0xc1 255) ,(0.5, PixelRGBA8 0xff 0xf4 0xc1 255) ,(1, PixelRGBA8 0xFF 0x53 0x73 255)] in withTexture (linearGradientTexture gradDef (V2 40 40) (V2 130 130)) $ fill $ circle (V2 100 100) 100) produceDocImage (outFolder "radial_gradient.png") $ (let gradDef = [(0, PixelRGBA8 0 0x86 0xc1 255) ,(0.5, PixelRGBA8 0xff 0xf4 0xc1 255) ,(1, PixelRGBA8 0xFF 0x53 0x73 255)] in withTexture (radialGradientTexture gradDef (V2 100 100) 75) $ fill $ circle (V2 100 100) 100) produceDocImage (outFolder "radial_gradient_focus.png") $ (let gradDef = [(0, PixelRGBA8 0 0x86 0xc1 255) ,(0.5, PixelRGBA8 0xff 0xf4 0xc1 255) ,(1, PixelRGBA8 0xFF 0x53 0x73 255)] in withTexture (radialGradientWithFocusTexture gradDef (V2 100 100) 75 (V2 70 70)) $ fill $ circle (V2 100 100) 100) produceDocImage (outFolder "sampler_pad.png") $ (let gradDef = [(0, PixelRGBA8 0 0x86 0xc1 255) ,(0.5, PixelRGBA8 0xff 0xf4 0xc1 255) ,(1, PixelRGBA8 0xFF 0x53 0x73 255)] in withTexture (linearGradientTexture gradDef (V2 80 100) (V2 120 110)) $ fill $ rectangle (V2 10 10) 180 180) produceDocImage (outFolder "logo.png") $ do let colorCycle = cycle [ PixelRGBA8 0 0x86 0xc1 255 , PixelRGBA8 0xff 0xf4 0xc1 255 , PixelRGBA8 0 0x86 0xc1 255 , PixelRGBA8 0xDf 0xD4 0xc1 255 , PixelRGBA8 0 0x86 0xc1 255 , PixelRGBA8 0 0x86 0xc1 255] colors = V.fromListN (4 * 4) colorCycle mesh = generateLinearGrid 3 3 (V2 10 10) (V2 60 60) colors withTexture (transformTexture (scale 0.7 0.7 <> rotateCenter (-0.4) (V2 100 100)) $ meshPatchTexture PatchBicubic mesh) $ fill $ logo 80 False (V2 20 20) ++ logo 40 True (V2 40 40) produceDocImage (outFolder "cubic_bezier.png") $ stroke 5 JoinRound (CapRound, CapRound) $ CubicBezier (V2 0 10) (V2 205 250) (V2 (-10) 250) (V2 160 35) produceDocImage (outFolder "quadratic_bezier.png") $ fill [ Bezier (V2 10 10) (V2 200 50) (V2 200 100) , Bezier (V2 200 100) (V2 150 200) (V2 120 175) , Bezier (V2 120 175) (V2 30 100) (V2 10 10)] produceDocImage (outFolder "simple_line.png") $ fill [ Line (V2 10 10) (V2 190 10) , Line (V2 190 10) (V2 95 170) , Line (V2 95 170) (V2 10 10) ] produceDocImage (outFolder "primitive_mixed.png") $ fill [ toPrim $ CubicBezier (V2 50 20) (V2 90 60) (V2 5 100) (V2 50 140) , toPrim $ Line (V2 50 140) (V2 120 80) , toPrim $ Line (V2 120 80) (V2 50 20) ] produceDocImage (outFolder "path_example.png") $ fill $ Path (V2 50 20) True [ PathCubicBezierCurveTo (V2 90 60) (V2 5 100) (V2 50 140) , PathLineTo (V2 120 80) ] produceDocImage (outFolder "stroke_polyline.png") $ stroke 4 JoinRound (CapRound, CapRound) $ polyline [V2 10 10, V2 100 70, V2 190 190] produceDocImage (outFolder "fill_polygon.png") $ fill $ polygon [V2 30 30, V2 100 70, V2 80 170] produceDocImage (outFolder "fill_roundedRectangle.png") $ fill $ roundedRectangle (V2 10 10) 150 150 20 10 produceDocImage (outFolder "stroke_roundedRectangle.png") $ stroke 4 JoinRound (CapRound, CapRound) $ roundedRectangle (V2 10 10) 150 150 20 10 produceDocImage (outFolder "fill_evenodd.png") $ fillingSample FillEvenOdd produceDocImage (outFolder "fill_winding.png") $ fillingSample FillWinding produceDocImage (outFolder "transform_rotate.png") $ fill . transform (applyTransformation $ rotate 0.2) $ rectangle (V2 40 40) 120 120 produceDocImage (outFolder "transform_rotate_center.png") $ fill . transform (applyTransformation $ rotateCenter 0.2 (V2 200 200)) $ rectangle (V2 40 40) 120 120 produceDocImage (outFolder "transform_translate.png") $ fill . transform (applyTransformation $ translate (V2 100 100)) $ rectangle (V2 40 40) 40 40 produceDocImage (outFolder "transform_scale.png") $ fill . transform (applyTransformation $ scale 2 2) $ rectangle (V2 40 40) 40 40 produceDocImage (outFolder "transform_skewx.png") $ fill . transform (applyTransformation $ skewX 0.3) $ rectangle (V2 50 50) 80 80 produceDocImage (outFolder "transform_skewy.png") $ fill . transform (applyTransformation $ skewY 0.3) $ rectangle (V2 50 50) 80 80 Right (ImageRGB8 img) <- readImage "avatar.png" let textureImage = promoteImage img produceDocImage (outFolder "sampled_texture_repeat.png") $ withTexture (withSampler SamplerRepeat $ sampledImageTexture textureImage) $ fill $ rectangle (V2 0 0) 200 200 produceDocImage (outFolder "image_simple.png") $ drawImage textureImage 0 (V2 30 30) produceDocImage (outFolder "image_resize.png") $ drawImageAtSize textureImage 2 (V2 30 30) 128 128 produceDocImage (outFolder "sampled_texture_reflect.png") $ withTexture (withSampler SamplerReflect $ sampledImageTexture textureImage) $ fill $ rectangle (V2 0 0) 200 200 produceDocImage (outFolder "sampled_texture_pad.png") $ withTexture (sampledImageTexture textureImage) $ fill $ rectangle (V2 0 0) 200 200 produceDocImage (outFolder "sampled_texture_rotate.png") $ withTexture (withSampler SamplerRepeat $ transformTexture (rotateCenter 1 (V2 0 0)) $ sampledImageTexture textureImage) $ fill $ rectangle (V2 0 0) 200 200 produceDocImage (outFolder "sampled_texture_scaled.png") $ withTexture (withSampler SamplerRepeat $ transformTexture (rotateCenter 1 (V2 0 0) <> scale 0.5 0.25) $ sampledImageTexture textureImage) $ fill $ rectangle (V2 0 0) 200 200 produceDocImage (outFolder "group_opacity.png") $ do withTexture accent2Texture $ stroke 3 JoinRound (CapRound, CapRound) $ line (V2 0 100) (V2 200 100) withGroupOpacity 128 $ do withTexture frontTexture . fill $ circle (V2 70 100) 60 withTexture accentTexture . fill $ circle (V2 120 100) 60 produceDocImage (outFolder "pattern_texture.png") $ let pattern = patternTexture 40 40 96 accent2Color . withTexture frontTexture $ fill $ circle (V2 20 20) 13 in withTexture pattern $ fill $ roundedRectangle (V2 20 20) 160 160 20 20 produceDocImage (outFolder "item_opacity.png") $ do withTexture accent2Texture $ stroke 3 JoinRound (CapRound, CapRound) $ line (V2 0 100) (V2 200 100) withTexture (uniformTexture $ PixelRGBA8 0 0x86 0xc1 128) . fill $ circle (V2 70 100) 60 withTexture (uniformTexture $ PixelRGBA8 0xff 0xf4 0xc1 128) . fill $ circle (V2 120 100) 60 produceDocImage (outFolder "mesh_patch_interp_bilinear.png") $ do let colorCycle = cycle [ PixelRGBA8 0 0x86 0xc1 255 , PixelRGBA8 0xff 0xf4 0xc1 255 , PixelRGBA8 0xFF 0x53 0x73 255 , PixelRGBA8 0xff 0xf4 0xc1 255 , PixelRGBA8 0 0x86 0xc1 255] colors = V.fromListN (4 * 4) colorCycle withTransformation (rotate 0.2) $ renderMeshPatch PatchBilinear $ generateLinearGrid 3 3 (V2 10 10) (V2 60 60) colors produceDocImage (outFolder "mesh_patch_interp_clip.png") $ do let colorCycle = cycle [ PixelRGBA8 0 0x86 0xc1 255 , PixelRGBA8 0xff 0xf4 0xc1 255 , PixelRGBA8 0xFF 0x53 0x73 255 , PixelRGBA8 0xff 0xf4 0xc1 255 , PixelRGBA8 0 0x86 0xc1 255] colors = V.fromListN (4 * 4) colorCycle withClipping (fill $ circle (V2 100 100) 75) $ renderMeshPatch PatchBilinear $ generateLinearGrid 3 3 (V2 10 10) (V2 60 60) colors produceDocImage (outFolder "mesh_patch_interp_bicubic.png") $ do let colorCycle = cycle [ PixelRGBA8 0 0x86 0xc1 255 , PixelRGBA8 0xff 0xf4 0xc1 255 , PixelRGBA8 0xFF 0x53 0x73 255 , PixelRGBA8 0xff 0xf4 0xc1 255 , PixelRGBA8 0 0x86 0xc1 255] colors = V.fromListN (4 * 4) colorCycle renderMeshPatch PatchBicubic $ generateLinearGrid 3 3 (V2 10 10) (V2 60 60) colors produceDocImage (outFolder "mesh_patch_transp.png") $ do let colorCycle = cycle [ PixelRGBA8 0 0x86 0xc1 255 , PixelRGBA8 0xff 0xf4 0xc1 255 , PixelRGBA8 0xFF 0x53 0x73 127 , PixelRGBA8 0xff 0xf4 0xc1 127 ] colors = V.fromListN (1 * 4) colorCycle withTexture (uniformTexture $ PixelRGBA8 0 0 0 255) $ fill $ rectangle (V2 0 70) 200 60 renderMeshPatch PatchBicubic $ generateLinearGrid 1 1 (V2 10 10) (V2 180 180) colors textExample textMultipleExample coordinateSystem textOnPathExample geometryOnPath