on master help sign in
Merge remote branch 'mathstuf/dev/integration'
88308c8Corey O'Connor
src/Graphics/Vty/Image.hs+29−3
@@ -23,6 +23,8 @@ module Graphics.Vty.Image ( Image(..)
23 , safe_wcswidth
23 , safe_wcswidth
24 , wcwidth
24 , wcwidth
25 , wcswidth
25 , wcswidth
26+ , crop
27+ , pad
26 -- | The possible display attributes used in constructing an `Image`.
28 -- | The possible display attributes used in constructing an `Image`.
27 , module Graphics.Vty.Attributes
29 , module Graphics.Vty.Attributes
28 )
30 )
@@ -103,6 +105,10 @@ data Image =
103 -- Any image of zero size equals the empty image.
105 -- Any image of zero size equals the empty image.
104 | EmptyImage
106 | EmptyImage
105 | Translation (Int, Int) Image
107 | Translation (Int, Int) Image
108+ -- Crop an image to a size
109+ | ImageCrop (Word, Word) Image
110+ -- Pad an image up to a size
111+ | ImagePad (Word, Word) Image
106 deriving Eq
112 deriving Eq
107
113
108instance Show Image where
114instance Show Image where
@@ -115,7 +121,11 @@ instance Show Image where
115 show ( VertJoin { part_top = t, part_bottom = b, output_width = c, output_height = r } )
121 show ( VertJoin { part_top = t, part_bottom = b, output_width = c, output_height = r } )
116 = "VertJoin (" ++ show c ++ ", " ++ show r ++ ") ( " ++ show t ++ " ) <-> ( " ++ show b ++ " )"
122 = "VertJoin (" ++ show c ++ ", " ++ show r ++ ") ( " ++ show t ++ " ) <-> ( " ++ show b ++ " )"
117 show ( Translation offset i )
123 show ( Translation offset i )
118− = "Translation ( " ++ show offset ++ ", " ++ show i ++ " )"
124+ = "Translation " ++ show offset ++ " ( " ++ show i ++ " )"
125+ show ( ImageCrop size i )
126+ = "ImageCrop " ++ show size ++ " ( " ++ show i ++ " )"
127+ show ( ImagePad size i )
128+ = "ImagePad " ++ show size ++ " ( " ++ show i ++ " )"
119 show ( EmptyImage ) = "EmptyImage"
129 show ( EmptyImage ) = "EmptyImage"
120
130
121-- | Currently append in the Monoid instance is equivalent to <->. Future versions will just stack
131-- | Currently append in the Monoid instance is equivalent to <->. Future versions will just stack
@@ -206,7 +216,9 @@ image_width HorizJoin { output_width = w } = w
206image_width VertJoin { output_width = w } = w
216image_width VertJoin { output_width = w } = w
207image_width BGFill { output_width = w } = w
217image_width BGFill { output_width = w } = w
208image_width EmptyImage = 0
218image_width EmptyImage = 0
209−image_width ( Translation _v i ) = image_width i
219+image_width ( Translation _v i ) = toEnum $ max 0 $ (fst _v +) $ fromEnum $ image_width i
220+image_width ( ImageCrop _v i ) = min (image_width i) $ fst _v
221+image_width ( ImagePad _v i ) = max (image_width i) $ fst _v
210
222
211-- | The height of an Image. This is the number of display rows the image will occupy.
223-- | The height of an Image. This is the number of display rows the image will occupy.
212image_height :: Image -> Word
224image_height :: Image -> Word
@@ -215,7 +227,9 @@ image_height HorizJoin { output_height = r } = r
215image_height VertJoin { output_height = r } = r
227image_height VertJoin { output_height = r } = r
216image_height BGFill { output_height = r } = r
228image_height BGFill { output_height = r } = r
217image_height EmptyImage = 0
229image_height EmptyImage = 0
218−image_height ( Translation _v i ) = image_height i
230+image_height ( Translation _v i ) = toEnum $ max 0 $ (snd _v +) $ fromEnum $ image_height i
231+image_height ( ImageCrop _v i ) = min (image_height i) $ snd _v
232+image_height ( ImagePad _v i ) = max (image_height i) $ snd _v
219
233
220-- | Combines two images side by side.
234-- | Combines two images side by side.
221--
235--
@@ -332,3 +346,15 @@ empty_image = EmptyImage
332translate :: (Int, Int) -> Image -> Image
346translate :: (Int, Int) -> Image -> Image
333translate v i = Translation v i
347translate v i = Translation v i
334
348
349+crop :: (Word, Word) -> Image -> Image
350+crop (0,_) _ = EmptyImage
351+crop (_,0) _ = EmptyImage
352+crop v (ImageCrop _size i) = ImagePad (min (fst v) (fst _size), min (snd v) (snd _size)) i
353+crop v (ImagePad _size i) = ImagePad (min (fst v) (fst _size), min (snd v) (snd _size)) i
354+crop v i = ImagePad v i
355+
356+pad :: (Word, Word) -> Image -> Image
357+pad (0,_) _ = EmptyImage
358+pad (_,0) _ = EmptyImage
359+pad v (ImagePad _size i) = ImagePad (max (fst v) (fst _size), max (snd v) (snd _size)) i
360+pad v i = ImagePad v i
src/Graphics/Vty/Picture.hs+2
@@ -16,6 +16,8 @@ module Graphics.Vty.Picture ( module Graphics.Vty.Picture
16 , char_fill
16 , char_fill
17 , empty_image
17 , empty_image
18 , translate
18 , translate
19+ , crop
20+ , pad
19 -- | The possible display attributes used in constructing an `Image`.
21 -- | The possible display attributes used in constructing an `Image`.
20 , module Graphics.Vty.Attributes
22 , module Graphics.Vty.Attributes
21 )
23 )
src/Graphics/Vty/Span.hs+95−41
@@ -24,6 +24,7 @@ import Data.Array.ST
24import qualified Data.ByteString as B
24import qualified Data.ByteString as B
25import qualified Data.ByteString.Internal as BInt
25import qualified Data.ByteString.Internal as BInt
26import qualified Data.Foldable as Foldable
26import qualified Data.Foldable as Foldable
27+import Data.List
27import Data.Word
28import Data.Word
28import qualified Data.ByteString.UTF8 as BSUTF8
29import qualified Data.ByteString.UTF8 as BSUTF8
29import qualified Data.String.UTF8 as UTF8
30import qualified Data.String.UTF8 as UTF8
@@ -110,7 +111,7 @@ build_spans pic region = do
110 -- The ops builder recursively descends the image and outputs span ops that would
111 -- The ops builder recursively descends the image and outputs span ops that would
111 -- display that image. The number of columns remaining in this row before exceeding the
112 -- display that image. The number of columns remaining in this row before exceeding the
112 -- bounds is also provided. This is used to clip the span ops produced.
113 -- bounds is also provided. This is used to clip the span ops produced.
113− ops_for_row mrow_ops (pic_background pic) region (pic_image pic) 0 (region_width region)
114+ _ <- ops_for_row mrow_ops (pic_background pic) region (pic_image pic) (0,0) 0 (region_width region)
114 -- Fill in any unspecified columns with the background pattern.
115 -- Fill in any unspecified columns with the background pattern.
115 forM_ [0 .. region_height region - 1] $ \row -> do
116 forM_ [0 .. region_height region - 1] $ \row -> do
116 end_x <- readSTArray mrow_ops row >>= return . span_ops_effected_columns
117 end_x <- readSTArray mrow_ops row >>= return . span_ops_effected_columns
@@ -122,61 +123,105 @@ build_spans pic region = do
122
123
123type MRowOps s = STArray s Word SpanOps
124type MRowOps s = STArray s Word SpanOps
124
125
125−ops_for_row :: MRowOps s -> Background -> DisplayRegion -> Image -> Word -> Word -> ST s ()
126+ops_for_row :: MRowOps s -> Background -> DisplayRegion -> Image -> (Word, Word) -> Word -> Word -> ST s (Word, Word)
126−ops_for_row mrow_ops bg region image y remaining_columns
127+ops_for_row mrow_ops bg region image skip_dim@(skip_row,skip_col) y remaining_columns
127− | remaining_columns == 0 = return ()
128+ | remaining_columns == 0 = return skip_dim
128− | y >= region_height region = return ()
129+ | y >= region_height region = return skip_dim
129 | otherwise = case image of
130 | otherwise = case image of
130− EmptyImage -> return ()
131+ EmptyImage -> return skip_dim
131 -- The width provided is the number of columns this text span will occupy when displayed.
132 -- The width provided is the number of columns this text span will occupy when displayed.
132 -- if this is greater than the number of remaining columsn the output has to be produced a
133 -- if this is greater than the number of remaining columsn the output has to be produced a
133 -- character at a time.
134 -- character at a time.
134− HorizText a text_str ow cw -> do
135+ HorizText a text_str _ _ -> do
136+ if skip_row > 0
137+ then return (skip_row - 1, skip_col)
138+ else do
135− snoc_text_span a text_str ow cw mrow_ops y remaining_columns
139+ skip_col' <- snoc_text_span a text_str mrow_ops skip_col y remaining_columns
140+ return (skip_row, skip_col')
136 VertJoin t b _ _ -> do
141 VertJoin t b _ _ -> do
137− ops_for_row mrow_ops bg region t y remaining_columns
142+ (skip_row',skip_col') <- ops_for_row mrow_ops bg region t skip_dim y remaining_columns
138− ops_for_row mrow_ops bg region b (y + image_height t) remaining_columns
143+ (skip_row'',skip_col'') <- ops_for_row mrow_ops bg region b (skip_row', skip_col) (y + image_height t - (skip_row - skip_row')) remaining_columns
144+ return (skip_row'', min skip_col' skip_col'')
139 HorizJoin l r _ _ -> do
145 HorizJoin l r _ _ -> do
140− ops_for_row mrow_ops bg region l y remaining_columns
146+ (skip_row',skip_col') <- ops_for_row mrow_ops bg region l skip_dim y remaining_columns
141 -- Don't output the right part unless there is at least a single column left after
147 -- Don't output the right part unless there is at least a single column left after
142 -- outputting the left part.
148 -- outputting the left part.
143− if image_width l < remaining_columns
149+ if image_width l - (skip_col - skip_col') > remaining_columns
150+ then return (skip_row,skip_col')
151+ else do
144− then ops_for_row mrow_ops bg region r y (remaining_columns - image_width l)
152+ (skip_row'',skip_col'') <- ops_for_row mrow_ops bg region r (skip_row, skip_col') y (remaining_columns - image_width l + (skip_col - skip_col'))
145− else return ()
153+ return (min skip_row' skip_row'', skip_col'')
146 BGFill width height -> do
154 BGFill width height -> do
147− let actual_height = if y + height > region_height region
155+ let min_height = if y + height > region_height region
148− then region_height region - y
156+ then region_height region - y
149− else height
157+ else height
158+ min_width = if width > remaining_columns
159+ then remaining_columns
160+ else width
161+ actual_height = if skip_row > min_height
162+ then 0
163+ else min_height - skip_row
150− actual_width = if width > remaining_columns
164+ actual_width = if skip_col > min_width
151− then remaining_columns
165+ then 0
152− else width
166+ else min_width - skip_col
153 forM_ [y .. y + actual_height - 1] $ \y' -> snoc_bg_fill mrow_ops bg actual_width y'
167 forM_ [y .. y + actual_height - 1] $ \y' -> snoc_bg_fill mrow_ops bg actual_width y'
154− Translation _offset i -> ops_for_row mrow_ops bg region i y remaining_columns
168+ let skip_row' = if actual_height > skip_row
169+ then 0
170+ else skip_row - min_height
171+ skip_col' = if actual_width > skip_col
172+ then 0
173+ else skip_col - min_width
174+ return (skip_row',skip_col')
175+ Translation (dx,dy) i -> do
176+ if dx < 0
177+ -- Translation left
178+ -- Extract the delta and add it to skip_col.
179+ then ops_for_row mrow_ops bg region (translate (0, dy) i) (skip_row, skip_col + dw) y remaining_columns
180+ -- Translation right
181+ else if dy < 0
182+ -- Translation up
183+ -- Extract the delta and add it to skip_row.
184+ then ops_for_row mrow_ops bg region (translate (dx, 0) i) (skip_row + dh, skip_col) y remaining_columns
185+ -- Translation down
186+ -- Pad the start of lines and above the image with a
187+ -- background_fill image.
188+ else ops_for_row mrow_ops bg region (background_fill ow dh <-> (background_fill dw ih <|> i)) skip_dim y remaining_columns
189+ where
190+ dw = toEnum $ abs dx
191+ dh = toEnum $ abs dy
192+ ow = image_width image
193+ ih = image_height i
194+ ImageCrop (max_w,max_h) i ->
195+ if y >= max_h
196+ then return skip_dim
197+ else ops_for_row mrow_ops bg region i skip_dim y (min remaining_columns max_w)
198+ ImagePad (min_w,min_h) i -> do
199+ let hpad = if image_width i < min_w
200+ then background_fill (min_w - image_width i) (image_height i)
201+ else empty_image
202+ let vpad = if image_height i < min_h
203+ then background_fill (image_width i) (min_h - image_height i)
204+ else empty_image
205+ ops_for_row mrow_ops bg region ((i <|> hpad) <-> vpad) skip_dim y remaining_columns
155
206
156snoc_text_span :: (Foldable.Foldable t)
207snoc_text_span :: (Foldable.Foldable t)
157 => Attr
208 => Attr
158 -> t Char
209 -> t Char
159− -> Word -- width of text span in output display columns
160− -> Word -- width of text span in characters
161 -> MRowOps s
210 -> MRowOps s
162 -> Word
211 -> Word
163 -> Word
212 -> Word
213+ -> Word
164− -> ST s ()
214+ -> ST s Word
165−snoc_text_span a text_str ow cw mrow_ops y remaining_columns = do
215+snoc_text_span a text_str mrow_ops skip_col y remaining_columns = do
166− if ow > remaining_columns
167− then do
168− snoc_op mrow_ops y $ AttributeChange a
216+ snoc_op mrow_ops y $ AttributeChange a
169− let (ow', cw', txt) = Foldable.foldl'
217+ let (ow', dw', cw', txt) = Foldable.foldl'
170− build_cropped_txt
218+ build_cropped_txt
171− ( 0, 0, B.empty )
219+ ( 0, 0, 0, B.empty )
172− text_str
220+ text_str
173− snoc_op mrow_ops y $ TextSpan ow' cw' (UTF8.fromRep txt)
221+ snoc_op mrow_ops y $ TextSpan ow' cw' (UTF8.fromRep txt)
174− else do
222+ return $ skip_col - dw'
175− snoc_op mrow_ops y $ AttributeChange a
176− let utf8_bs = ({-# SCC "BSUTF8.fromString" #-} BSUTF8.fromString) $ Foldable.foldMap (\c -> [c]) text_str
177− snoc_op mrow_ops y $ TextSpan ow cw (UTF8.fromRep utf8_bs)
178 where
223 where
179− build_cropped_txt (ow', char_count', b0) c = {-# SCC "build_cropped_txt" #-}
224+ build_cropped_txt (ow', dw', char_count', b0) c = {-# SCC "build_cropped_txt" #-}
180 let w = wcwidth c
225 let w = wcwidth c
181 -- Characters with unknown widths occupy 1 column.
226 -- Characters with unknown widths occupy 1 column.
182 --
227 --
@@ -185,9 +230,18 @@ snoc_text_span a text_str ow cw mrow_ops y remaining_columns = do
185 -- the character. If so then this replacement process may need to be implemented
230 -- the character. If so then this replacement process may need to be implemented
186 -- manually for consistent behavior across terminals.
231 -- manually for consistent behavior across terminals.
187 w' = toEnum $ if w < 0 then 1 else w
232 w' = toEnum $ if w < 0 then 1 else w
233+ in if dw' == skip_col
234+ then if ow' == remaining_columns
235+ then ( ow', dw', char_count', b0 )
188− in if (w' + ow') > remaining_columns
236+ else if (w' + ow') > remaining_columns
189− then ( ow', char_count', b0 )
237+ then ( remaining_columns, dw', char_count' + ooverflow, B.append b0 $ B.pack $ encode $ genericReplicate ooverflow '…' )
190− else ( ow' + w', char_count' + 1, B.append b0 $ B.pack $ encode [c] )
238+ else ( ow' + w', dw', char_count' + 1, B.append b0 $ B.pack $ encode [c] )
239+ else if (w' + dw') > skip_col
240+ then ( doverflow, skip_col, doverflow, B.append b0 $ B.pack $ encode $ genericReplicate doverflow '…' )
241+ else ( ow', w' + dw', char_count', b0 )
242+ where
243+ doverflow = skip_col - dw'
244+ ooverflow = remaining_columns - ow'
191
245
192snoc_bg_fill :: MRowOps s -> Background -> Word -> Word -> ST s ()
246snoc_bg_fill :: MRowOps s -> Background -> Word -> Word -> ST s ()
193snoc_bg_fill _row_ops _bg 0 _row
247snoc_bg_fill _row_ops _bg 0 _row