1-- Copyright 2009 Corey O'Connor
2{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE NamedFieldPuns #-}
4{-# LANGUAGE DisambiguateRecordFields #-}
5{-# OPTIONS_GHC -funbox-strict-fields #-}
6module Graphics.Vty.Image ( Image(..)
24 -- | The possible display attributes used in constructing an `Image`.
25 , module Graphics.Vty.Attributes
29import Graphics.Vty.Attributes
31import Codec.Binary.UTF8.Width
33import Codec.Binary.UTF8.String ( decode )
35import Data.AffineSpace
36import qualified Data.ByteString as BS
38import qualified Data.Sequence as Seq
39import qualified Data.String.UTF8 as UTF8
45type StringSeq = Seq.Seq Char
47-- | An image in VTY defines:
49-- * properties required to display the image. These are properties that effect the output image
50-- but are independent of position
52-- * A set of position-dependent text and attribute regions. The possible regions are:
56-- * a horizontal line of characters with a single attribute. (string, utf8_string,
59-- * a fill of a single character. (char_fill)
61-- * a fill of the picture's background. (background_fill)
63-- todo: increase the number of encoded bytestring formats supported.
65 -- A horizontal text span is always >= 1 column and has a row height of 1.
68 -- All character data is stored as Char sequences with the ISO-10646 encoding.
71 , output_width :: !Word -- >= 0
72 , char_width :: !Word -- >= 1
74 -- A horizontal join can be constructed between any two images. However a HorizJoin instance is
75 -- required to be between two images of equal height. The horiz_join constructor adds blanks to
76 -- the provided images that assure this is true for the HorizJoin value produced.
80 , output_width :: !Word -- >= 1
81 , output_height :: !Word -- >= 1
83 -- A veritical join can be constructed between any two images. However a VertJoin instance is
84 -- required to be between two images of equal width. The horiz_join constructor adds blanks to
85 -- the provides images that assure this is true for the HorizJoin value produced.
88 , part_bottom :: Image
89 , output_width :: !Word -- >= 1
90 , output_height :: !Word -- >= 1
92 -- A background fill will be filled with the background pattern. The background pattern is
93 -- defined as a property of the Picture this Image is used to form.
95 { output_width :: !Word -- >= 1
96 , output_height :: !Word -- >= 1
98 -- The combining operators identity constant.
99 -- EmptyImage <|> a = a
100 -- EmptyImage <-> a = a
102 -- Any image of zero size equals the empty image.
104 | Translation (Int, Int) Image
107instance Show Image where
108 show ( HorizText { output_width = ow, text = txt } )
109 = "HorizText [" ++ show ow ++ "] (" ++ show txt ++ ")"
110 show ( BGFill { output_width = c, output_height = r } )
111 = "BGFill (" ++ show c ++ "," ++ show r ++ ")"
112 show ( HorizJoin { part_left = l, part_right = r, output_width = c } )
113 = "HorizJoin " ++ show c ++ " ( " ++ show l ++ " <|> " ++ show r ++ " )"
114 show ( VertJoin { part_top = t, part_bottom = b, output_width = c, output_height = r } )
115 = "VertJoin (" ++ show c ++ ", " ++ show r ++ ") ( " ++ show t ++ " ) <-> ( " ++ show b ++ " )"
116 show ( EmptyImage ) = "EmptyImage"
118-- | Currently append in the Monoid instance is equivalent to <->. Future versions will just stack
120instance Monoid Image where
124-- A horizontal text image of 0 characters in width simplifies to the EmptyImage
125horiz_text :: Attr -> StringSeq -> Word -> Image
127 | ow == 0 = EmptyImage
128 | otherwise = HorizText a txt (toEnum $ Seq.length txt) ow
130horiz_join :: Image -> Image -> Word -> Word -> Image
131horiz_join i_0 i_1 w h
132 -- A horiz join of two 0 width images simplifies to the EmptyImage
133 | w == 0 = EmptyImage
134 -- A horizontal join where either part is 0 columns in width simplifies to the other part.
135 -- This covers the case where one part is the EmptyImage.
136 | image_width i_0 == 0 = i_1
137 | image_width i_1 == 0 = i_0
138 -- If the images are of the same height then no BG padding is required
139 | image_height i_0 == image_height i_1 = HorizJoin i_0 i_1 w h
140 -- otherwise one of the imagess needs to be padded to the right size.
141 | image_height i_0 < image_height i_1 -- Pad i_0
142 = let pad_amount = image_height i_1 - image_height i_0
143 in horiz_join ( vert_join i_0
144 ( BGFill ( image_width i_0 ) pad_amount )
150 | image_height i_0 > image_height i_1 -- Pad i_1
151 = let pad_amount = image_height i_0 - image_height i_1
154 ( BGFill ( image_width i_1 ) pad_amount )
159horiz_join _ _ _ _ = error "horiz_join applied to undefined values."
161vert_join :: Image -> Image -> Word -> Word -> Image
163 -- A vertical join of two 0 height images simplifies to the EmptyImage
164 | h == 0 = EmptyImage
165 -- A vertical join where either part is 0 rows in height simplifies to the other part.
166 -- This covers the case where one part is the EmptyImage
167 | image_height i_0 == 0 = i_1
168 | image_height i_1 == 0 = i_0
169 -- If the images are of the same height then no background padding is required
170 | image_width i_0 == image_width i_1 = VertJoin i_0 i_1 w h
171 -- Otherwise one of the images needs to be padded to the size of the other image.
172 | image_width i_0 < image_width i_1
173 = let pad_amount = image_width i_1 - image_width i_0
174 in vert_join ( horiz_join i_0
175 ( BGFill pad_amount ( image_height i_0 ) )
181 | image_width i_0 > image_width i_1
182 = let pad_amount = image_width i_0 - image_width i_1
185 ( BGFill pad_amount ( image_height i_1 ) )
190vert_join _ _ _ _ = error "vert_join applied to undefined values."
192-- | An area of the picture's bacground (See Background) of w columns and h rows.
193background_fill :: Word -> Word -> Image
195 | w == 0 = EmptyImage
196 | h == 0 = EmptyImage
197 | otherwise = BGFill w h
199-- | The width of an Image. This is the number display columns the image will occupy.
200image_width :: Image -> Word
201image_width HorizText { output_width = w } = w
202image_width HorizJoin { output_width = w } = w
203image_width VertJoin { output_width = w } = w
204image_width BGFill { output_width = w } = w
205image_width EmptyImage = 0
206image_width ( Translation _v i ) = image_width i
208-- | The height of an Image. This is the number of display rows the image will occupy.
209image_height :: Image -> Word
210image_height HorizText {} = 1
211image_height HorizJoin { output_height = r } = r
212image_height VertJoin { output_height = r } = r
213image_height BGFill { output_height = r } = r
214image_height EmptyImage = 0
215image_height ( Translation _v i ) = image_width i
217-- | Combines two images side by side.
219-- The result image will have a width equal to the sum of the two images width. And the height will
220-- equal the largest height of the two images. The area not defined in one image due to a height
221-- missmatch will be filled with the background pattern.
222(<|>) :: Image -> Image -> Image
224-- Two horizontal text spans with the same attributes can be merged.
225h0@(HorizText attr_0 text_0 ow_0 _) <|> h1@(HorizText attr_1 text_1 ow_1 _)
226 | attr_0 == attr_1 = horiz_text attr_0 (text_0 Seq.>< text_1) (ow_0 + ow_1)
227 | otherwise = horiz_join h0 h1 (ow_0 + ow_1) 1
229-- Anything placed to the right of a join wil be joined to the right sub image.
230-- The total columns for the join is the sum of the two arguments columns
231h0@( HorizJoin {} ) <|> h1
232 = horiz_join ( part_left h0 )
233 ( part_right h0 <|> h1 )
234 ( image_width h0 + image_width h1 )
235 ( max (image_height h0) (image_height h1) )
237-- Anything but a join placed to the left of a join wil be joined to the left sub image.
238-- The total columns for the join is the sum of the two arguments columns
239h0 <|> h1@( HorizJoin {} )
240 = horiz_join ( h0 <|> part_left h1 )
242 ( image_width h0 + image_width h1 )
243 ( max (image_height h0) (image_height h1) )
248 ( image_width h0 + image_width h1 )
249 ( max (image_height h0) (image_height h1) )
251-- | Combines two images vertically.
252-- The result image will have a height equal to the sum of the heights of both images.
253-- The width will equal the largest width of the two images.
254-- The area not defined in one image due to a width missmatch will be filled with the background
256(<->) :: Image -> Image -> Image
260 ( max (image_width im_t) (image_width im_b) )
261 ( image_height im_t + image_height im_b )
263-- | Compose any number of images horizontally.
264horiz_cat :: [Image] -> Image
265horiz_cat = foldr (<|>) EmptyImage
267horzcat :: [Image] -> Image
270-- | Compose any number of images vertically.
271vert_cat :: [Image] -> Image
272vert_cat = foldr (<->) EmptyImage
274vertcat :: [Image] -> Image
277-- | an image of a single character. This is a standard Haskell 31-bit character assumed to be in
278-- the ISO-10646 encoding.
279char :: Attr -> Char -> Image
280char !a !c = HorizText a (Seq.singleton c) (safe_wcwidth c) 1
282-- | A string of characters layed out on a single row with the same display attribute. The string is
283-- assumed to be a sequence of ISO-10646 characters.
285-- Note: depending on how the Haskell compiler represents string literals a string literal in a
286-- UTF-8 encoded source file, for example, may be represented as a ISO-10646 string.
287-- That is, I think, the case with GHC 6.10. This means, for the most part, you don't need to worry
288-- about the encoding format when outputting string literals. Just provide the string literal
289-- directly to iso_10646_string or string.
291iso_10646_string :: Attr -> String -> Image
292iso_10646_string !a !str = horiz_text a (Seq.fromList str) (safe_wcswidth str)
294-- | Alias for iso_10646_string. Since the usual case is that a literal string like "foo" is
295-- represented internally as a list of ISO 10646 31 bit characters.
297-- Note: Keep in mind that GHC will compile source encoded as UTF-8 but the literal strings, while
298-- UTF-8 encoded in the source, will be transcoded to a ISO 10646 31 bit characters runtime
300string :: Attr -> String -> Image
301string = iso_10646_string
303-- | A string of characters layed out on a single row. The string is assumed to be a sequence of
305utf8_string :: Attr -> [Word8] -> Image
306utf8_string !a !str = string a ( decode str )
308safe_wcwidth :: Char -> Word
309safe_wcwidth c = case wcwidth c of
310 i | i < 0 -> error "negative wcwidth"
311 | otherwise -> toEnum i
313safe_wcswidth :: String -> Word
314safe_wcswidth str = case wcswidth str of
315 i | i < 0 -> error "negative wcswidth"
316 | otherwise -> toEnum i
318-- | Renders a UTF-8 encoded bytestring.
319utf8_bytestring :: Attr -> BS.ByteString -> Image
320utf8_bytestring !a !bs = string a (UTF8.toString $ UTF8.fromRep bs)
322-- | creates a fill of the specified character. The dimensions are in number of characters wide and
323-- number of rows high.
325-- Unlike the Background fill character this character can have double column display width.
326char_fill :: Enum d => Attr -> Char -> d -> d -> Image
328 vert_cat $ replicate (fromEnum h) $ horiz_cat $ replicate (fromEnum w) $ char a c
330-- | The empty image. Useful for fold combinators. These occupy no space nor define any display
333empty_image = EmptyImage
335translate :: (Int, Int) -> Image -> Image
336translate v i = Translation v i
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list