Skip to content

Instantly share code, notes, and snippets.

@kaoskorobase
Last active March 12, 2017 07:59
Show Gist options
  • Save kaoskorobase/ac90e14d0c9e1ca7f999 to your computer and use it in GitHub Desktop.
Save kaoskorobase/ac90e14d0c9e1ca7f999 to your computer and use it in GitHub Desktop.
Generating mobile application icons from SVG with Shake

Shake is quite amazing: powered by Haskell, it's really easy to quickly throw together build rules for little problems that appear in everyday programming tasks.

Here's a small snippet of Shake rules that convert an SVG to an iOS application icon in the various required sizes. As a bonus point, it generates icons for different build configurations by showing and hiding layers using inkscape-export-layers. See this article for how to use the different icons sets.

-- Generate iOS icons from SVG
iosIcons :: String -> Rules ()
iosIcons target = do
  let resolutions = [(29,[1,2,3]), (40,[1,2,3]), (50,[1,2]), (57,[1,2]), (60,[2,3]), (72,[1,2]), (76,[1,2])]
      configurations = [Beta, Debug, Release]
      icon size mul config = buildDir </> "ios/icons"
                                      </> "LocosonicPlayer" ++ show config
                                      </> "AppIcon" ++ show size ++ "x" ++ show size ++ (if mul /= 1 then "@" ++ show mul else "") ++ ".png"
  forM_ resolutions $ \(size,muls) -> do
    forM_ configurations $ \config -> do
      forM_ muls $ \mul -> do
        icon size mul config %> \png -> do
          pwd <- liftIO Dir.getCurrentDirectory
          let input = "assets/LocosonicPlayer/locosonic_app_icon.svg"
          need [input]
          withTempFile $ \svg -> do
            let badge config = show config ++ " badge"
                toShow = [badge config, "Icon", "Square background"]
                toHide = map badge (delete config configurations) ++ ["Rounded background"]
            () <- cmd "./tools/inkscape-export-layers/exportlayers.py" $
                 concatMap (\layer -> ["--show", layer]) toShow
              ++ concatMap (\layer -> ["--hide", layer]) toHide
              ++ [input, svg]
            let width = size * mul
                height = width
            cmd "inkscape" [
              "--export-png", pwd </> png,
              "--export-area-page",
              "--export-width", show width,
              "--export-height", show height,
              pwd </> svg ]

  phony target $ need $ concat [ [ icon s m c | m <- ms ] | (s,ms) <- resolutions, c <- configurations ]

And just for fun, the same for Android:

-- Generate Android icons from SVG
androidIcons :: String -> Rules ()
androidIcons target = do
  let resolutions = ["hdpi", "mdpi", "xhdpi", "xxhdpi", "xxxhdpi"]
      configurations = [Beta, Debug]
      icon res config = buildDir </> "android/icons"
                                 </> "LocosonicPlayer" ++ show config
                                 </> "res" </> ("drawable-" ++ res)
                                 </> "ic_launcher.png"
  forM_ resolutions $ \res -> do
    forM_ configurations $ \config -> do
      icon res config %> \png -> do
        pwd <- liftIO Dir.getCurrentDirectory
        let input = "assets/LocosonicPlayer/locosonic_app_icon.svg"
        need [input]
        withTempFile $ \svg -> do
          let badge config = show config ++ " badge"
              toShow = [badge config, "Icon", "Rounded background"]
              toHide = map badge (delete config configurations) ++ ["Square background"]
          () <- cmd "./tools/inkscape-export-layers/exportlayers.py" $
               concatMap (\layer -> ["--show", layer]) toShow
            ++ concatMap (\layer -> ["--hide", layer]) toHide
            ++ [input, svg]
          let (width,height) = iconSize res
          cmd "inkscape" [
            "--export-png", pwd </> png,
            "--export-area-drawing",
            "--export-width", show width,
            "--export-height", show height,
            pwd </> svg ]

  phony target $ need $ [ icon r c | r <- resolutions, c <- configurations ]
  where
    iconSize res = case res of
                      "mdpi" -> (48,48)
                      "hdpi" -> (72,72)
                      "xhdpi" -> (96,96)
                      "xxhdpi" -> (144,144)
                      "xxxhdpi" -> (192,192)
                      _ -> error $ "Unknown resolution " ++ res

Comments welcome! @kaoskorobase

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment