{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE DisambiguateRecordFields #-}
module Graphics.Vty.Image
(
Image
, imageWidth
, imageHeight
, emptyImage
, char
, string
, iso10646String
, utf8String
, text
, text'
, backgroundFill
, utf8Bytestring
, utf8Bytestring'
, charFill
, horizJoin
, (<|>)
, vertJoin
, (<->)
, horizCat
, vertCat
, crop
, cropRight
, cropLeft
, cropBottom
, cropTop
, pad
, resize
, resizeWidth
, resizeHeight
, translate
, translateX
, translateY
, safeWcwidth
, safeWcswidth
, safeWctwidth
, safeWctlwidth
, wcwidth
, wcswidth
, wctwidth
, wctlwidth
, DisplayText
, DisplayRegion
, regionWidth
, regionHeight
)
where
import Graphics.Vty.Attributes
import Graphics.Vty.Image.Internal
import Graphics.Text.Width
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import Data.Word
type DisplayRegion = (Int,Int)
regionWidth :: DisplayRegion -> Int
regionWidth :: DisplayRegion -> Int
regionWidth = DisplayRegion -> Int
forall a b. (a, b) -> a
fst
regionHeight :: DisplayRegion -> Int
regionHeight :: DisplayRegion -> Int
regionHeight = DisplayRegion -> Int
forall a b. (a, b) -> b
snd
infixr 5 <|>
infixr 4 <->
backgroundFill :: Int
-> Int
-> Image
backgroundFill :: Int -> Int -> Image
backgroundFill Int
w Int
h
| Int
w Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = Image
EmptyImage
| Int
h Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = Image
EmptyImage
| Bool
otherwise = Int -> Int -> Image
BGFill Int
w Int
h
(<|>) :: Image -> Image -> Image
<|> :: Image -> Image -> Image
(<|>) = Image -> Image -> Image
horizJoin
(<->) :: Image -> Image -> Image
<-> :: Image -> Image -> Image
(<->) = Image -> Image -> Image
vertJoin
horizCat :: [Image] -> Image
horizCat :: [Image] -> Image
horizCat = (Image -> Image -> Image) -> Image -> [Image] -> Image
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Image -> Image -> Image
horizJoin Image
EmptyImage
vertCat :: [Image] -> Image
vertCat :: [Image] -> Image
vertCat = (Image -> Image -> Image) -> Image -> [Image] -> Image
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Image -> Image -> Image
vertJoin Image
EmptyImage
text :: Attr -> TL.Text -> Image
text :: Attr -> Text -> Image
text Attr
a Text
txt = let displayWidth :: Int
displayWidth = Text -> Int
safeWctlwidth Text
txt
in Attr -> Text -> Int -> Int -> Image
HorizText Attr
a Text
txt Int
displayWidth (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int64 -> Int) -> Int64 -> Int
forall a b. (a -> b) -> a -> b
$! Text -> Int64
TL.length Text
txt)
text' :: Attr -> T.Text -> Image
text' :: Attr -> Text -> Image
text' Attr
a Text
txt = let displayWidth :: Int
displayWidth = Text -> Int
safeWctwidth Text
txt
in Attr -> Text -> Int -> Int -> Image
HorizText Attr
a (Text -> Text
TL.fromStrict Text
txt) Int
displayWidth (Text -> Int
T.length Text
txt)
char :: Attr -> Char -> Image
char :: Attr -> Char -> Image
char Attr
a Char
c =
let displayWidth :: Int
displayWidth = Char -> Int
safeWcwidth Char
c
in Attr -> Text -> Int -> Int -> Image
HorizText Attr
a (Char -> Text
TL.singleton Char
c) Int
displayWidth Int
1
iso10646String :: Attr -> String -> Image
iso10646String :: Attr -> String -> Image
iso10646String Attr
a String
str =
let displayWidth :: Int
displayWidth = String -> Int
safeWcswidth String
str
in Attr -> Text -> Int -> Int -> Image
HorizText Attr
a (String -> Text
TL.pack String
str) Int
displayWidth (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
str)
string :: Attr -> String -> Image
string :: Attr -> String -> Image
string = Attr -> String -> Image
iso10646String
utf8String :: Attr -> [Word8] -> Image
utf8String :: Attr -> [Word8] -> Image
utf8String Attr
a [Word8]
bytes = Attr -> ByteString -> Image
utf8Bytestring Attr
a ([Word8] -> ByteString
BL.pack [Word8]
bytes)
utf8Bytestring :: Attr -> BL.ByteString -> Image
utf8Bytestring :: Attr -> ByteString -> Image
utf8Bytestring Attr
a ByteString
bs = Attr -> Text -> Image
text Attr
a (ByteString -> Text
TL.decodeUtf8 ByteString
bs)
utf8Bytestring' :: Attr -> B.ByteString -> Image
utf8Bytestring' :: Attr -> ByteString -> Image
utf8Bytestring' Attr
a ByteString
bs = Attr -> Text -> Image
text' Attr
a (ByteString -> Text
T.decodeUtf8 ByteString
bs)
charFill :: Integral d
=> Attr
-> Char
-> d
-> d
-> Image
charFill :: Attr -> Char -> d -> d -> Image
charFill Attr
a Char
c d
w d
h
| d
w d -> d -> Bool
forall a. Ord a => a -> a -> Bool
<= d
0 Bool -> Bool -> Bool
|| d
h d -> d -> Bool
forall a. Ord a => a -> a -> Bool
<= d
0 = Image
EmptyImage
| Bool
otherwise = [Image] -> Image
vertCat
([Image] -> Image) -> [Image] -> Image
forall a b. (a -> b) -> a -> b
$ Int -> Image -> [Image]
forall a. Int -> a -> [a]
replicate (d -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral d
h)
(Image -> [Image]) -> Image -> [Image]
forall a b. (a -> b) -> a -> b
$ Attr -> Text -> Int -> Int -> Image
HorizText Attr
a Text
txt Int
displayWidth Int
forall a. Num a => a
charWidth
where
txt :: Text
txt = Int64 -> Text -> Text
TL.replicate Int64
forall a. Num a => a
charWidth (Char -> Text
TL.singleton Char
c)
displayWidth :: Int
displayWidth = Char -> Int
safeWcwidth Char
c Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
forall a. Num a => a
charWidth
charWidth :: Num a => a
charWidth :: a
charWidth = d -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral d
w
emptyImage :: Image
emptyImage :: Image
emptyImage = Image
EmptyImage
pad :: Int
-> Int
-> Int
-> Int
-> Image
-> Image
pad :: Int -> Int -> Int -> Int -> Image -> Image
pad Int
0 Int
0 Int
0 Int
0 Image
i = Image
i
pad Int
inL Int
inT Int
inR Int
inB Image
inImage
| Int
inL Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
|| Int
inT Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
|| Int
inR Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
|| Int
inB Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = String -> Image
forall a. HasCallStack => String -> a
error String
"cannot pad by negative amount"
| Bool
otherwise = Int -> Int -> Int -> Int -> Image -> Image
go Int
inL Int
inT Int
inR Int
inB Image
inImage
where
go :: Int -> Int -> Int -> Int -> Image -> Image
go Int
0 Int
0 Int
0 Int
0 Image
i = Image
i
go Int
0 Int
0 Int
0 Int
b Image
i = Image -> Image -> Int -> Int -> Image
VertJoin Image
i (Int -> Int -> Image
BGFill Int
w Int
b) Int
w Int
h
where w :: Int
w = Image -> Int
imageWidth Image
i
h :: Int
h = Image -> Int
imageHeight Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
b
go Int
0 Int
0 Int
r Int
b Image
i = Int -> Int -> Int -> Int -> Image -> Image
go Int
0 Int
0 Int
0 Int
b (Image -> Image) -> Image -> Image
forall a b. (a -> b) -> a -> b
$ Image -> Image -> Int -> Int -> Image
HorizJoin Image
i (Int -> Int -> Image
BGFill Int
r Int
h) Int
w Int
h
where w :: Int
w = Image -> Int
imageWidth Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
r
h :: Int
h = Image -> Int
imageHeight Image
i
go Int
0 Int
t Int
r Int
b Image
i = Int -> Int -> Int -> Int -> Image -> Image
go Int
0 Int
0 Int
r Int
b (Image -> Image) -> Image -> Image
forall a b. (a -> b) -> a -> b
$ Image -> Image -> Int -> Int -> Image
VertJoin (Int -> Int -> Image
BGFill Int
w Int
t) Image
i Int
w Int
h
where w :: Int
w = Image -> Int
imageWidth Image
i
h :: Int
h = Image -> Int
imageHeight Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
t
go Int
l Int
t Int
r Int
b Image
i = Int -> Int -> Int -> Int -> Image -> Image
go Int
0 Int
t Int
r Int
b (Image -> Image) -> Image -> Image
forall a b. (a -> b) -> a -> b
$ Image -> Image -> Int -> Int -> Image
HorizJoin (Int -> Int -> Image
BGFill Int
l Int
h) Image
i Int
w Int
h
where w :: Int
w = Image -> Int
imageWidth Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
l
h :: Int
h = Image -> Int
imageHeight Image
i
translate :: Int
-> Int
-> Image
-> Image
translate :: Int -> Int -> Image -> Image
translate Int
x Int
y Image
i = Int -> Image -> Image
translateX Int
x (Int -> Image -> Image
translateY Int
y Image
i)
translateX :: Int -> Image -> Image
translateX :: Int -> Image -> Image
translateX Int
x Image
i
| Int
x Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
&& (Int -> Int
forall a. Num a => a -> a
abs Int
x Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Image -> Int
imageWidth Image
i) = Image
emptyImage
| Int
x Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = let s :: Int
s = Int -> Int
forall a. Num a => a -> a
abs Int
x in Image -> Int -> Int -> Int -> Image
CropLeft Image
i Int
s (Image -> Int
imageWidth Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
s) (Image -> Int
imageHeight Image
i)
| Int
x Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = Image
i
| Bool
otherwise = let h :: Int
h = Image -> Int
imageHeight Image
i in Image -> Image -> Int -> Int -> Image
HorizJoin (Int -> Int -> Image
BGFill Int
x Int
h) Image
i (Image -> Int
imageWidth Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
x) Int
h
translateY :: Int -> Image -> Image
translateY :: Int -> Image -> Image
translateY Int
y Image
i
| Int
y Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
&& (Int -> Int
forall a. Num a => a -> a
abs Int
y Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Image -> Int
imageHeight Image
i) = Image
emptyImage
| Int
y Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = let s :: Int
s = Int -> Int
forall a. Num a => a -> a
abs Int
y in Image -> Int -> Int -> Int -> Image
CropTop Image
i Int
s (Image -> Int
imageWidth Image
i) (Image -> Int
imageHeight Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
s)
| Int
y Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 = Image
i
| Bool
otherwise = let w :: Int
w = Image -> Int
imageWidth Image
i in Image -> Image -> Int -> Int -> Image
VertJoin (Int -> Int -> Image
BGFill Int
w Int
y) Image
i Int
w (Image -> Int
imageHeight Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
y)
crop :: Int
-> Int
-> Image
-> Image
crop :: Int -> Int -> Image -> Image
crop Int
0 Int
_ Image
_ = Image
EmptyImage
crop Int
_ Int
0 Image
_ = Image
EmptyImage
crop Int
w Int
h Image
i = Int -> Image -> Image
cropBottom Int
h (Int -> Image -> Image
cropRight Int
w Image
i)
cropBottom :: Int -> Image -> Image
cropBottom :: Int -> Image -> Image
cropBottom Int
0 Image
_ = Image
EmptyImage
cropBottom Int
h Image
inI
| Int
h Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = String -> Image
forall a. HasCallStack => String -> a
error String
"cannot crop height to less than zero"
| Bool
otherwise = Image -> Image
go Image
inI
where
go :: Image -> Image
go Image
EmptyImage = Image
EmptyImage
go i :: Image
i@(CropBottom {Image
croppedImage :: Image -> Image
croppedImage :: Image
croppedImage, Int
outputWidth :: Image -> Int
outputWidth :: Int
outputWidth, Int
outputHeight :: Image -> Int
outputHeight :: Int
outputHeight})
| Int
outputHeight Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
h = Image
i
| Bool
otherwise = Image -> Int -> Int -> Image
CropBottom Image
croppedImage Int
outputWidth Int
h
go Image
i
| Int
h Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Image -> Int
imageHeight Image
i = Image
i
| Bool
otherwise = Image -> Int -> Int -> Image
CropBottom Image
i (Image -> Int
imageWidth Image
i) Int
h
cropRight :: Int -> Image -> Image
cropRight :: Int -> Image -> Image
cropRight Int
0 Image
_ = Image
EmptyImage
cropRight Int
w Image
inI
| Int
w Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = String -> Image
forall a. HasCallStack => String -> a
error String
"cannot crop width to less than zero"
| Bool
otherwise = Image -> Image
go Image
inI
where
go :: Image -> Image
go Image
EmptyImage = Image
EmptyImage
go i :: Image
i@(CropRight {Image
croppedImage :: Image
croppedImage :: Image -> Image
croppedImage, Int
outputWidth :: Int
outputWidth :: Image -> Int
outputWidth, Int
outputHeight :: Int
outputHeight :: Image -> Int
outputHeight})
| Int
outputWidth Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
w = Image
i
| Bool
otherwise = Image -> Int -> Int -> Image
CropRight Image
croppedImage Int
w Int
outputHeight
go Image
i
| Int
w Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Image -> Int
imageWidth Image
i = Image
i
| Bool
otherwise = Image -> Int -> Int -> Image
CropRight Image
i Int
w (Image -> Int
imageHeight Image
i)
cropLeft :: Int -> Image -> Image
cropLeft :: Int -> Image -> Image
cropLeft Int
0 Image
_ = Image
EmptyImage
cropLeft Int
w Image
inI
| Int
w Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = String -> Image
forall a. HasCallStack => String -> a
error String
"cannot crop the width to less than zero"
| Bool
otherwise = Image -> Image
go Image
inI
where
go :: Image -> Image
go Image
EmptyImage = Image
EmptyImage
go i :: Image
i@(CropLeft {Image
croppedImage :: Image
croppedImage :: Image -> Image
croppedImage, Int
leftSkip :: Image -> Int
leftSkip :: Int
leftSkip, Int
outputWidth :: Int
outputWidth :: Image -> Int
outputWidth, Int
outputHeight :: Int
outputHeight :: Image -> Int
outputHeight})
| Int
outputWidth Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
w = Image
i
| Bool
otherwise =
let leftSkip' :: Int
leftSkip' = Int
leftSkip Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
outputWidth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
w
in Image -> Int -> Int -> Int -> Image
CropLeft Image
croppedImage Int
leftSkip' Int
w Int
outputHeight
go Image
i
| Image -> Int
imageWidth Image
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
w = Image
i
| Bool
otherwise = Image -> Int -> Int -> Int -> Image
CropLeft Image
i (Image -> Int
imageWidth Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
w) Int
w (Image -> Int
imageHeight Image
i)
cropTop :: Int -> Image -> Image
cropTop :: Int -> Image -> Image
cropTop Int
0 Image
_ = Image
EmptyImage
cropTop Int
h Image
inI
| Int
h Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 = String -> Image
forall a. HasCallStack => String -> a
error String
"cannot crop the height to less than zero"
| Bool
otherwise = Image -> Image
go Image
inI
where
go :: Image -> Image
go Image
EmptyImage = Image
EmptyImage
go i :: Image
i@(CropTop {Image
croppedImage :: Image
croppedImage :: Image -> Image
croppedImage, Int
topSkip :: Image -> Int
topSkip :: Int
topSkip, Int
outputWidth :: Int
outputWidth :: Image -> Int
outputWidth, Int
outputHeight :: Int
outputHeight :: Image -> Int
outputHeight})
| Int
outputHeight Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
h = Image
i
| Bool
otherwise =
let topSkip' :: Int
topSkip' = Int
topSkip Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
outputHeight Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
h
in Image -> Int -> Int -> Int -> Image
CropTop Image
croppedImage Int
topSkip' Int
outputWidth Int
h
go Image
i
| Image -> Int
imageHeight Image
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
h = Image
i
| Bool
otherwise = Image -> Int -> Int -> Int -> Image
CropTop Image
i (Image -> Int
imageHeight Image
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
h) (Image -> Int
imageWidth Image
i) Int
h
resize :: Int -> Int -> Image -> Image
resize :: Int -> Int -> Image -> Image
resize Int
w Int
h Image
i = Int -> Image -> Image
resizeHeight Int
h (Int -> Image -> Image
resizeWidth Int
w Image
i)
resizeWidth :: Int -> Image -> Image
resizeWidth :: Int -> Image -> Image
resizeWidth Int
w Image
i = case Int
w Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` Image -> Int
imageWidth Image
i of
Ordering
LT -> Int -> Image -> Image
cropRight Int
w Image
i
Ordering
EQ -> Image
i
Ordering
GT -> Image
i Image -> Image -> Image
<|> Int -> Int -> Image
BGFill (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Image -> Int
imageWidth Image
i) (Image -> Int
imageHeight Image
i)
resizeHeight :: Int -> Image -> Image
resizeHeight :: Int -> Image -> Image
resizeHeight Int
h Image
i = case Int
h Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` Image -> Int
imageHeight Image
i of
Ordering
LT -> Int -> Image -> Image
cropBottom Int
h Image
i
Ordering
EQ -> Image
i
Ordering
GT -> Image
i Image -> Image -> Image
<-> Int -> Int -> Image
BGFill (Image -> Int
imageWidth Image
i) (Int
h Int -> Int -> Int
forall a. Num a => a -> a -> a
- Image -> Int
imageHeight Image
i)