Skip to content

Commit 22897f0

Browse files
authored
Merge pull request #788 from tsoding/louis
Migrate !asciify to Louis library
2 parents 7165d14 + ad67e5c commit 22897f0

3 files changed

Lines changed: 6 additions & 109 deletions

File tree

HyperNerd.cabal

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,12 @@ executable HyperNerd
137137
, uri-encode
138138
, random
139139
, cassava
140-
, discord-haskell >= 0.8.3
140+
, discord-haskell <= 0.8.3
141141
, template-haskell
142142
, http-types
143143
, JuicyPixels
144144
, vector
145+
, louis == 0.1.0.2
145146

146147
-- Directories containing source files.
147148
hs-source-dirs: src
@@ -205,7 +206,7 @@ test-suite HyperNerdTest
205206
, stm
206207
, regex-base
207208
, regex-tdfa
208-
, discord-haskell >= 0.8.3
209+
, discord-haskell <= 0.8.3
209210
, array
210211
, http-types
211212

src/Bot/Asciify.hs

Lines changed: 2 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -4,132 +4,27 @@
44

55
module Bot.Asciify
66
( asciifyReaction
7-
, asciifyFile
87
) where
98

109
import Bot.BttvFfz
1110
import Bot.Replies
12-
import Codec.Picture
1311
import Control.Applicative
14-
import Data.Bits
15-
import qualified Data.ByteString as BS
1612
import qualified Data.ByteString.Lazy as BSL
17-
import Data.Char
1813
import Data.Functor
19-
import Data.Functor.Compose
20-
import Data.List
2114
import qualified Data.Map as M
2215
import Data.Maybe
2316
import Data.Proxy
2417
import qualified Data.Text as T
2518
import Data.Time
2619
import Data.Time.Extra
27-
import qualified Data.Vector.Storable as V
28-
import Data.Word
2920
import Effect
3021
import Entity
22+
import Louis
3123
import Property
3224
import Reaction
3325
import Text.InterpolatedString.QM
3426
import Transport
3527

36-
type Chunk = Word8
37-
38-
renderChunk :: Chunk -> Char
39-
renderChunk x = chr (bgroup * groupSize + boffset + ord '')
40-
where
41-
bgroup =
42-
let b1 = (x .&. 0b00001000) `shiftR` 3
43-
b2 = (x .&. 0b10000000) `shiftR` 6
44-
in fromIntegral (b1 .|. b2)
45-
boffset =
46-
let b1 = (x .&. 0b00000111)
47-
b2 = (x .&. 0b01110000) `shiftR` 1
48-
in fromIntegral (b1 .|. b2)
49-
groupSize = 64
50-
51-
chunkifyGreyScale :: Image Pixel8 -> [[Chunk]]
52-
chunkifyGreyScale img =
53-
[ [chunkAt (i * 2, j * 4) | i <- [0 .. chunksWidth - 1]]
54-
| j <- [0 .. chunksHeight - 1]
55-
]
56-
where
57-
width = imageWidth img
58-
height = imageHeight img
59-
chunksWidth = width `div` 2
60-
chunksHeight = height `div` 4
61-
squashBits :: [Word8] -> Word8
62-
squashBits = foldl' (\acc x -> shiftL acc 1 .|. x) 0
63-
threshold =
64-
let imgData = imageData img
65-
in round $
66-
(/ (fromIntegral $ V.length imgData)) $
67-
V.foldl' (+) (0.0 :: Float) $ V.map fromIntegral imgData
68-
k :: Pixel8 -> Word8
69-
k x
70-
| x < threshold = 0
71-
| otherwise = 1
72-
f :: (Int, Int) -> Word8
73-
f (x, y)
74-
| 0 <= x && x < width && 0 <= y && y < height = k $ pixelAt img x y
75-
| otherwise = 0
76-
chunkAt :: (Int, Int) -> Chunk
77-
chunkAt (x, y) =
78-
squashBits $ reverse [f (i + x, j + y) | i <- [0, 1], j <- [0 .. 3]]
79-
80-
greyScaleImage :: DynamicImage -> Image Pixel8
81-
greyScaleImage = pixelMap greyScalePixel . convertRGBA8
82-
-- reference: https://www.mathworks.com/help/matlab/ref/rgb2gray.html
83-
where
84-
greyScalePixel :: PixelRGBA8 -> Pixel8
85-
greyScalePixel (PixelRGBA8 r g b a) = k
86-
where
87-
k = round ((r' * 0.299 + g' * 0.587 + b' * 0.114) * a')
88-
r' = fromIntegral r :: Float
89-
g' = fromIntegral g :: Float
90-
b' = fromIntegral b :: Float
91-
a' = (fromIntegral a :: Float) / 255.0
92-
93-
asciifyGreyScale :: Image Pixel8 -> [T.Text]
94-
asciifyGreyScale =
95-
map T.pack . getCompose . fmap renderChunk . Compose . chunkifyGreyScale
96-
97-
resizeImageWidth :: Pixel a => Int -> Image a -> Image a
98-
resizeImageWidth width' image
99-
| width /= width' =
100-
let ratio :: Float
101-
ratio = fromIntegral width' / fromIntegral width
102-
height' = floor (fromIntegral height * ratio)
103-
y_interval :: Float
104-
y_interval = fromIntegral height / fromIntegral height'
105-
x_interval :: Float
106-
x_interval = fromIntegral width / fromIntegral width'
107-
resizedData =
108-
[ imgData V.! idx
109-
| y <- [0 .. (height' - 1)]
110-
, x <- [0 .. (width' - 1)]
111-
, let idx =
112-
floor (fromIntegral y * y_interval) * width +
113-
floor (fromIntegral x * x_interval)
114-
]
115-
in Image width' height' $ V.fromList resizedData
116-
| otherwise = image
117-
where
118-
width = imageWidth image
119-
height = imageHeight image
120-
imgData = imageData image
121-
122-
asciifyDynamicImage :: DynamicImage -> [T.Text]
123-
asciifyDynamicImage = asciifyGreyScale . resizeImageWidth 60 . greyScaleImage
124-
125-
asciifyFile :: FilePath -> IO [T.Text]
126-
asciifyFile filePath = do
127-
bytes <- BS.readFile filePath
128-
either error return $ asciifyByteString bytes
129-
130-
asciifyByteString :: BS.ByteString -> Either String [T.Text]
131-
asciifyByteString bytes = asciifyDynamicImage <$> decodeImage bytes
132-
13328
newtype AsciifyState = AsciifyState
13429
{ asciifyStateLastUsed :: UTCTime
13530
}
@@ -175,7 +70,7 @@ asciifyReaction =
17570
replyOnNothing "Such emote does not exist" $
17671
asciifyCooldown $
17772
byteStringHttpRequestReaction $
178-
cmapR (asciifyByteString . BSL.toStrict) $
73+
cmapR (braillizeByteString . BSL.toStrict) $
17974
eitherReaction (Reaction (logMsg . T.pack . messageContent)) $
18075
dupCmapR
18176
(\Message { messageSender = Sender {senderChannel = channel}

stack.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ extra-deps:
4343
- hookup-0.2.2
4444
- irc-core-2.5.0
4545
- discord-haskell-0.8.3
46+
- louis-0.1.0.2
4647

4748
# Override default flag values for local packages and extra-deps
4849

0 commit comments

Comments
 (0)