Skip to content

Instantly share code, notes, and snippets.

@axman6
Created March 8, 2018 06:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save axman6/47cb868f541097e175c9c7c1fdbbc497 to your computer and use it in GitHub Desktop.
Save axman6/47cb868f541097e175c9c7c1fdbbc497 to your computer and use it in GitHub Desktop.
Fast CSS hex scaling
-- Scales colours found in a given file by a given factor, manages ~150MB/s
{-# LANGUAGE OverloadedStrings #-}
import Data.Attoparsec.ByteString (Parser, satisfy)
import Data.Attoparsec.ByteString.Lazy as A
import Data.ByteString.Lazy as BSL hiding (concatMap, map)
import Data.Monoid ((<>))
import Data.Word (Word8)
import Prelude hiding (readFile, writeFile)
import System.Environment (getArgs)
type Colour = (Word8,Word8,Word8)
main :: IO ()
main = do
args <- getArgs
case args of
(inFile:scaleS:outfile:_) ->
writeFile outfile . BSL.intercalate "#" . map (scaleHex (read scaleS)) . BSL.split 35 =<< readFile inFile
_ -> error "Usage: <infile> <scale> <outfile>"
where
scaleHex :: Double -> ByteString -> ByteString
scaleHex d bs = either id (\(c,r) -> renderColour (scaleColour d c) <> r) . colour $ bs
scaleColour :: Double -> Colour -> Colour
scaleColour d (r,g,b) = (scale r, scale g, scale b) where
scale :: Word8 -> Word8
scale = floor . (*d) . fromIntegral
parseColour :: Parser Colour
parseColour = (,,) <$> hexPair <*> hexPair <*> hexPair
where
hexPair = (\a b -> a*16 + b) <$> hex <*> hex
hex = fromHex <$> satisfy isHexDigit
isHexDigit w = (w >= 48 && w <= 57) || (w >= 97 && w <= 102) || (w >= 65 && w <= 70)
fromHex w | w >= 48 && w <= 57 = w - 48
| w >= 97 = w - 87
| otherwise = w - 55
-- | Either parse a colour from the front of the input or return the input unchanged
colour :: ByteString -> Either ByteString (Colour,ByteString)
colour bs = case A.parse parseColour bs of
Done rest clr -> Right (clr,rest)
_ -> Left bs
renderColour :: Colour -> ByteString
renderColour (r,g,b) = BSL.pack . concatMap renderByte $ [r,g,b] where
renderByte w = case quotRem w 16 of (x,y) -> map nibble [x,y]
nibble w | w < 10 = w + 48
| otherwise = w + 87 -- b + 97-10, aka 'a'-10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment