{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Codec.QRCode.JuicyPixels ( -- * Image toImage -- * URL , toPngDataUrlBS , toPngDataUrlS , toPngDataUrlT ) where import Codec.Picture (Image (..), Pixel8, encodePng) import Data.Bool (bool) import qualified Data.ByteString.Base64.Lazy as B64L import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BLC8 import qualified Data.Text.Lazy as TL import qualified Data.Vector.Storable as SV import qualified Data.Vector.Unboxed as UV import Data.Word (Word8) import Codec.QRCode (QRImage (..)) -- | Convert the QR code into an image. -- -- If this is not the required image format use `Codec.Picture.Types.promoteImage` and/or `Codec.Picture.Types.convertImage`. toImage :: Int -- ^ Border to add around the QR code, recommended is 4 (<0 is treated as 0) -> Int -- ^ Factor to scale the image (<1 is treated as 1) -> QRImage -- ^ The QRImage -> Image Pixel8 toImage border scale QRImage{..} | border <= 0 && scale <= 1 = Image qrImageSize qrImageSize (SV.fromList $ map (bool 0xff 0x00) (UV.toList qrImageData)) toImage border' scale' QRImage{..} = let border = border' `max` 0 scale = scale' `max` 1 size = (qrImageSize + 2 * border) * scale in Image size size (SV.fromList $ concat $ doScale scale $ addBorder border $ toMatrix qrImageData) where toMatrix :: UV.Vector Bool -> [[Word8]] toMatrix img | UV.null img = [] | otherwise = let (h, t) = UV.splitAt qrImageSize img in map (bool 0xff 0x00) (UV.toList h) : toMatrix t addBorder :: Int -> [[Word8]] -> [[Word8]] addBorder 0 img = img addBorder n img = topBottom ++ addLeftRight img ++ topBottom where topBottom = [replicate ((qrImageSize + 2 * n) * n) 0xff] leftRight = replicate n 0xff addLeftRight = map (\ x -> leftRight ++ x ++ leftRight) doScale :: Int -> [[Word8]] -> [[Word8]] doScale 1 img = img doScale n img = scaleV img where scaleV :: [[Word8]] -> [[Word8]] scaleV = concatMap (replicate n . scaleH) scaleH :: [Word8] -> [Word8] scaleH = concatMap (replicate n) -- | Convert an QR code into a Uri. -- Has the same arguments as `toImage`. -- -- This can be used to display a image in HTML without creating a temporary file. toPngDataUrlBS :: Int -> Int -> QRImage -> BL.ByteString toPngDataUrlBS border scale img = "data:image/png;base64," `BL.append` B64L.encode (encodePng $ toImage border scale img) -- | Convert an QR code into a Uri. -- Has the same arguments as `toImage`. -- -- Like `toPngDataUrlBS` but with a to String conversion afterwards. toPngDataUrlS :: Int -> Int -> QRImage -> String {-# INLINE toPngDataUrlS #-} toPngDataUrlS border scale = BLC8.unpack . toPngDataUrlBS border scale -- | Convert an QR code into a Uri. -- Has the same arguments as `toImage`. -- -- Like `toPngDataUrlS` but with a to Text conversion afterwards. toPngDataUrlT :: Int -> Int -> QRImage -> TL.Text {-# INLINE toPngDataUrlT #-} toPngDataUrlT border scale = TL.pack . toPngDataUrlS border scale