on master help sign in
Pair image characters with display widths.

This way repeated calls to wcwidth are not required within the inner loops of generating spans.

commit30fd552a797aa48689e8731d271c9131b078e7f3 authorCorey O'Connor <coreyoconnor@gmail.com> dateSun 3 Jul 2011, 13:31 (15 years ago) parent71cd98f
10 files changed+89−69
src/Graphics/Vty/Debug.hs+2−27 src/Graphics/Vty/Debug/Image.hs+30 src/Graphics/Vty/Image.hs+18−11 test/Bench.hs+1−1 test/Makefile+11−1 test/Verify.hs+3−4 test/Verify/Graphics/Vty/DisplayRegion.hs+2−2 test/Verify/Graphics/Vty/Image.hs+4−5 test/verify_image_ops.hs+13−13 test/verify_image_trans.hs+5−5
src/Graphics/Vty/Debug.hs+2−27
@@ -1,11 +1,12 @@
11-- Copyright 2009-2010 Corey O'Connor
22{-# LANGUAGE ScopedTypeVariables #-}
33module Graphics.Vty.Debug ( module Graphics.Vty.Debug
4+ , module Graphics.Vty.Debug.Image
45 )
56where
67
78import Graphics.Vty.Attributes
8−import Graphics.Vty.Image
9+import Graphics.Vty.Debug.Image
910import Graphics.Vty.Picture
1011import Graphics.Vty.Span
1112import Graphics.Vty.DisplayRegion
@@ -49,31 +50,5 @@ region_for_window (DebugWindow w h) = DisplayRegion w h
4950
5051type TestWindow = DebugWindow
5152
52−type ImageConstructLog = [ImageConstructEvent]
53−data ImageConstructEvent = ImageConstructEvent
54− deriving ( Show, Eq )
55−
56−forward_image_ops = map forward_transform debug_image_ops
57−
58−forward_transform, reverse_transform :: ImageOp -> (Image -> Image)
59−
60−forward_transform (ImageOp f _) = f
61−reverse_transform (ImageOp _ r) = r
62−
63−data ImageOp = ImageOp ImageEndo ImageEndo
64−type ImageEndo = Image -> Image
65−
66−debug_image_ops =
67− [ id_image_op
68− -- , render_single_column_char_op
69− -- , render_double_column_char_op
70− ]
71−
72−id_image_op :: ImageOp
73−id_image_op = ImageOp id id
74−
75−-- render_char_op :: ImageOp
76−-- render_char_op = ImageOp id id
77−
7853instance Show Picture where
7954 show (Picture _ image _ ) = "Picture ?? " ++ show image ++ " ??"
src/Graphics/Vty/Debug/Image.hsnew file+30
@@ -0,0 +1,30 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+module Graphics.Vty.Debug.Image where
3+
4+import Graphics.Vty.Image
5+
6+type ImageConstructLog = [ImageConstructEvent]
7+data ImageConstructEvent = ImageConstructEvent
8+ deriving ( Show, Eq )
9+
10+forward_image_ops = map forward_transform debug_image_ops
11+
12+forward_transform, reverse_transform :: ImageOp -> (Image -> Image)
13+
14+forward_transform (ImageOp f _) = f
15+reverse_transform (ImageOp _ r) = r
16+
17+data ImageOp = ImageOp ImageEndo ImageEndo
18+type ImageEndo = Image -> Image
19+
20+debug_image_ops =
21+ [ id_image_op
22+ -- , render_single_column_char_op
23+ -- , render_double_column_char_op
24+ ]
25+
26+id_image_op :: ImageOp
27+id_image_op = ImageOp id id
28+
29+-- render_char_op :: ImageOp
30+-- render_char_op = ImageOp id id
src/Graphics/Vty/Image.hs+18−11
@@ -45,7 +45,10 @@ import Data.Word
4545infixr 5 <|>
4646infixr 4 <->
4747
48−type StringSeq = Seq.Seq Char
48+-- | We pair each character with it's display length. This way we only compute the length once per
49+-- character.
50+-- * Though currently the width of some strings is still compute multiple times.
51+type DisplayString = Seq.Seq (Char, Word)
4952
5053-- | An image in VTY defines:
5154--
@@ -69,8 +72,7 @@ data Image =
6972 HorizText
7073 { attr :: !Attr
7174 -- All character data is stored as Char sequences with the ISO-10646 encoding.
72− , text :: StringSeq
73− -- in columns
75+ , text :: DisplayString
7476 , output_width :: !Word -- >= 0
7577 , char_width :: !Word -- >= 1
7678 }
@@ -113,7 +115,7 @@ data Image =
113115
114116instance Show Image where
115117 show ( HorizText { output_width = ow, text = txt } )
116− = "HorizText [" ++ show ow ++ "] (" ++ show txt ++ ")"
118+ = "HorizText [" ++ show ow ++ "] (" ++ show (fmap fst txt) ++ ")"
117119 show ( BGFill { output_width = c, output_height = r } )
118120 = "BGFill (" ++ show c ++ "," ++ show r ++ ")"
119121 show ( HorizJoin { part_left = l, part_right = r, output_width = c } )
@@ -128,14 +130,13 @@ instance Show Image where
128130 = "ImagePad " ++ show size ++ " ( " ++ show i ++ " )"
129131 show ( EmptyImage ) = "EmptyImage"
130132
131−-- | Currently append in the Monoid instance is equivalent to <->. Future versions will just stack
132−-- the images.
133+-- | Currently append in the Monoid instance is equivalent to <->.
133134instance Monoid Image where
134135 mempty = empty_image
135136 mappend = (<->)
136137
137138-- A horizontal text image of 0 characters in width simplifies to the EmptyImage
138−horiz_text :: Attr -> StringSeq -> Word -> Image
139+horiz_text :: Attr -> DisplayString -> Word -> Image
139140horiz_text a txt ow
140141 | ow == 0 = EmptyImage
141142 | otherwise = HorizText a txt ow (toEnum $ Seq.length txt)
@@ -288,7 +289,9 @@ vert_cat = foldr (<->) EmptyImage
288289-- | an image of a single character. This is a standard Haskell 31-bit character assumed to be in
289290-- the ISO-10646 encoding.
290291char :: Attr -> Char -> Image
291−char !a !c = HorizText a (Seq.singleton c) (safe_wcwidth c) 1
292+char !a !c =
293+ let display_width = safe_wcwidth c
294+ in HorizText a (Seq.singleton (c, display_width)) display_width 1
292295
293296-- | A string of characters layed out on a single row with the same display attribute. The string is
294297-- assumed to be a sequence of ISO-10646 characters.
@@ -300,7 +303,9 @@ char !a !c = HorizText a (Seq.singleton c) (safe_wcwidth c) 1
300303-- directly to iso_10646_string or string.
301304--
302305iso_10646_string :: Attr -> String -> Image
303−iso_10646_string !a !str = horiz_text a (Seq.fromList str) (safe_wcswidth str)
306+iso_10646_string !a !str =
307+ let display_text = Seq.fromList $ map (\c -> (c, safe_wcwidth c)) str
308+ in horiz_text a display_text (safe_wcswidth str)
304309
305310-- | Alias for iso_10646_string. Since the usual case is that a literal string like "foo" is
306311-- represented internally as a list of ISO 10646 31 bit characters.
@@ -316,14 +321,16 @@ string = iso_10646_string
316321utf8_string :: Attr -> [Word8] -> Image
317322utf8_string !a !str = string a ( decode str )
318323
324+-- | Returns the display width of a character. Assumes all characters with unknown widths are 0 width
319325safe_wcwidth :: Char -> Word
320326safe_wcwidth c = case wcwidth c of
321− i | i < 0 -> 0 -- error "negative wcwidth"
327+ i | i < 0 -> 0
322328 | otherwise -> toEnum i
323329
330+-- | Returns the display width of a string. Assumes all characters with unknown widths are 0 width
324331safe_wcswidth :: String -> Word
325332safe_wcswidth str = case wcswidth str of
326− i | i < 0 -> 0 -- error "negative wcswidth"
333+ i | i < 0 -> 0
327334 | otherwise -> toEnum i
328335
329336-- | Renders a UTF-8 encoded bytestring.
test/Bench.hs+1−1
@@ -1,6 +1,6 @@
11module Main where
22
3−import Graphics.Vty
3+import Graphics.Vty hiding ( pad )
44
55import Control.Concurrent( threadDelay )
66import Control.Monad( liftM2 )
test/Makefile+11−1
@@ -26,7 +26,17 @@ $(VERIF_TESTS)
2626$(shell mkdir -p objects )
2727
2828# TODO: Tests should also be buildable referencing the currently installed vty
29−GHC_ARGS=--make -i../src -package parallel -package deepseq-1.1.0.2 -hide-package transformers -hide-package monads-fd -hide-package monads-tf -package QuickCheck-2.4 -ignore-package vty ../cbits/gwinsz.c ../cbits/set_term_timing.c ../cbits/mk_wcwidth.c -O -funbox-strict-fields -Wall -threaded -fno-full-laziness -fspec-constr -odir objects -hidir objects
29+GHC_ARGS=--make -i../src \
30+ -package parallel \
31+ -package deepseq-1.1.0.2 \
32+ -hide-package transformers \
33+ -hide-package monads-fd \
34+ -hide-package monads-tf \
35+ -package QuickCheck-2.4 \
36+ -ignore-package vty \
37+ ../cbits/gwinsz.c ../cbits/set_term_timing.c ../cbits/mk_wcwidth.c \
38+ -O -funbox-strict-fields -Wall -threaded -fno-full-laziness -fspec-constr -rtsopts \
39+ -odir objects -hidir objects
3040
3141GHC_PROF_ARGS=-prof -auto-all $(GHC_ARGS)
3242
test/Verify.hs+3−4
@@ -11,8 +11,6 @@ module Verify ( module Verify
1111 , Result(..)
1212 , monadicIO
1313 , liftIO
14− , liftIOResult
15− , liftResult
1614 , liftBool
1715 )
1816 where
@@ -82,6 +80,7 @@ instance Arbitrary DoubleColumnChar where
8280 [ toEnum 0x3040 .. toEnum 0x3098 ]
8381 ++ [ toEnum 0x309B .. toEnum 0xA4CF]
8482
85−instance Arbitrary Word where
86− arbitrary = choose (0, 1024) >>= return . toEnum
83+
84+liftIOResult :: Testable prop => IO prop -> Property
85+liftIOResult = morallyDubiousIOProperty
8786
test/Verify/Graphics/Vty/DisplayRegion.hs+2−2
@@ -21,7 +21,7 @@ instance Show EmptyWindow where
2121
2222instance Arbitrary DebugWindow where
2323 arbitrary = do
24− w <- suchThat arbitrary (/= 0)
25− h <- suchThat arbitrary (/= 0)
24+ w <- choose (1,1024)
25+ h <- choose (1,1024)
2626 return $ DebugWindow w h
2727
test/Verify/Graphics/Vty/Image.hs+4−5
@@ -5,12 +5,11 @@ module Verify.Graphics.Vty.Image ( module Verify.Graphics.Vty.Image
55 )
66 where
77
8−import Graphics.Vty.Debug
8+import Verify.Graphics.Vty.Attributes
9+import Graphics.Vty.Debug.Image
910import Graphics.Vty.Image
10−import Graphics.Vty.DisplayRegion
1111
1212import Verify
13−import Verify.Graphics.Vty.Attributes
1413
1514import Data.Word
1615
@@ -51,7 +50,7 @@ instance Arbitrary SingleRowSingleAttrImage where
5150 -- The text must contain at least one character. Otherwise the image simplifies to the
5251 -- IdImage which has a height of 0. If this is to represent a single row then the height
5352 -- must be 1
54− single_column_row_text <- listOf1 arbitrary
53+ single_column_row_text <- resize 512 (listOf1 arbitrary)
5554 attr <- arbitrary
5655 return $ SingleRowSingleAttrImage
5756 attr
@@ -81,7 +80,7 @@ data SingleAttrSingleSpanStack = SingleAttrSingleSpanStack
8180
8281instance Arbitrary SingleAttrSingleSpanStack where
8382 arbitrary = do
84− NonEmpty image_list <- arbitrary
83+ image_list <- resize 512 (listOf1 arbitrary)
8584 let image = vert_cat [ i | SingleRowSingleAttrImage { row_image = i } <- image_list ]
8685 return $ SingleAttrSingleSpanStack
8786 image
test/verify_image_ops.hs+13−13
@@ -106,19 +106,19 @@ disjoint_width_vert_join_bg_fill (NonEmpty stack_0) (NonEmpty stack_1) =
106106 _ -> True
107107
108108main = run_test $ do
109− verify "two_sw_horiz_concat" two_sw_horiz_concat
110− verify "many_sw_horiz_concat" many_sw_horiz_concat
111− verify "two_sw_vert_concat" two_sw_vert_concat
112− verify "horiz_concat_sw_assoc" horiz_concat_sw_assoc
113− verify "many_dw_horiz_concat" many_dw_horiz_concat
114− verify "two_dw_horiz_concat" two_dw_horiz_concat
115− verify "two_dw_vert_concat" two_dw_vert_concat
116− verify "horiz_concat_dw_assoc" horiz_concat_dw_assoc
109+ _ <- verify "two_sw_horiz_concat" two_sw_horiz_concat
110+ _ <- verify "many_sw_horiz_concat" many_sw_horiz_concat
111+ _ <- verify "two_sw_vert_concat" two_sw_vert_concat
112+ _ <- verify "horiz_concat_sw_assoc" horiz_concat_sw_assoc
113+ _ <- verify "many_dw_horiz_concat" many_dw_horiz_concat
114+ _ <- verify "two_dw_horiz_concat" two_dw_horiz_concat
115+ _ <- verify "two_dw_vert_concat" two_dw_vert_concat
116+ _ <- verify "horiz_concat_dw_assoc" horiz_concat_dw_assoc
117117 liftIO $ putStrLn $ replicate 80 '-'
118− verify "single row vert concats to correct height" vert_contat_single_row
119− verify "disjoint_height_horiz_join" disjoint_height_horiz_join
120− verify "disjoint_height_horiz_join BG fill" disjoint_height_horiz_join_bg_fill
121− verify "disjoint_width_vert_join" disjoint_width_vert_join
122− verify "disjoint_width_vert_join BG fill" disjoint_width_vert_join_bg_fill
118+ _ <- verify "single row vert concats to correct height" vert_contat_single_row
119+ _ <- verify "disjoint_height_horiz_join" disjoint_height_horiz_join
120+ _ <- verify "disjoint_height_horiz_join BG fill" disjoint_height_horiz_join_bg_fill
121+ _ <- verify "disjoint_width_vert_join" disjoint_width_vert_join
122+ _ <- verify "disjoint_width_vert_join BG fill" disjoint_width_vert_join_bg_fill
123123 return ()
124124
test/verify_image_trans.hs+5−5
@@ -2,7 +2,6 @@
22module Main where
33
44import Verify.Graphics.Vty.Image
5−import Graphics.Vty.Debug
65
76import Verify
87
@@ -18,14 +17,15 @@ verify_horiz_contat_wo_attr_change_simplifies (SingleRowSingleAttrImage _attr ch
1817 is_horiz_text_of_columns image char_count
1918
2019verify_horiz_contat_w_attr_change_simplifies :: SingleRowTwoAttrImage -> Bool
21−verify_horiz_contat_w_attr_change_simplifies ( SingleRowTwoAttrImage (SingleRowSingleAttrImage attr0 char_count0 image0)
22− (SingleRowSingleAttrImage attr1 char_count1 image1)
20+verify_horiz_contat_w_attr_change_simplifies ( SingleRowTwoAttrImage (SingleRowSingleAttrImage attr0 char_count0 _image0)
21+ (SingleRowSingleAttrImage attr1 char_count1 _image1)
2322 i
2423 )
2524 | char_count0 == 0 || char_count1 == 0 || attr0 == attr1 = is_horiz_text_of_columns i (char_count0 + char_count1)
2625 | otherwise = False == is_horiz_text_of_columns i (char_count0 + char_count1)
2726
27+main :: IO ()
2828main = run_test $ do
29− verify "verify_horiz_contat_wo_attr_change_simplifies" verify_horiz_contat_wo_attr_change_simplifies
30− verify "verify_horiz_contat_w_attr_change_simplifies" verify_horiz_contat_w_attr_change_simplifies
29+ _ <- verify "verify_horiz_contat_wo_attr_change_simplifies" verify_horiz_contat_wo_attr_change_simplifies
30+ _ <- verify "verify_horiz_contat_w_attr_change_simplifies" verify_horiz_contat_w_attr_change_simplifies
3131 return ()