UTFTConverter (empty) → 0.1.0.0
raw patch · 12 files changed
+1032/−0 lines, 12 filesdep +JuicyPixelsdep +UTFTConverterdep +basesetup-changed
Dependencies added: JuicyPixels, UTFTConverter, base, bytestring, directory, filepath, hspec, time
Files
- LICENSE +22/−0
- Makefile +29/−0
- README.md +73/−0
- Setup.lhs +4/−0
- UTFTConverter.cabal +167/−0
- main-src/Main.hs +118/−0
- src/Format.hs +24/−0
- src/Format/C.hs +84/−0
- src/Format/Converter.hs +219/−0
- src/Format/RGB565.hs +114/−0
- src/Format/Raw.hs +30/−0
- tests/Tests.hs +148/−0
+ LICENSE view
@@ -0,0 +1,22 @@+The MIT License (MIT)++Copyright (c) 2015 ++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.+
+ Makefile view
@@ -0,0 +1,29 @@+CFLAGS= -O2 -Wall+OUT= -o UTFTConverter+CC= ghc+++all: converter clean+++converter: main-src/Main.hs\+ src/Format/Converter.hs\+ src/Format/Raw.hs\+ src/Format/C.hs\+ src/Format/RGB565.hs++ $(CC) $(CFLAGS) $(OUT) \+ main-src/Main.hs\+ src/Format/Converter.hs\+ src/Format/Raw.hs\+ src/Format/C.hs\+ src/Format/RGB565.hs+++clean:+ rm -f src/*.hi+ rm -f src/*.o+ rm -f src/Format/*.hi+ rm -f src/Format/*.o+ rm -f main-src/*.hi+ rm -f main-src/*.o
+ README.md view
@@ -0,0 +1,73 @@+# UTFTConverter++This is my take at an offline converter from the below defined formats to `.c` or `.raw` format.++I will try to make the tool as identical as possible to the corresponding tool at [RinkyDink](http://www.rinkydinkelectronics.com/library.php?id=51).++## Usage:++ ```+ ./UTFTConverter <filespec> /c|r [/o <path>] [/t AVR|ARM|PIC32]++ <filespec>: File(s) to convert+ parameters: /c - Create output as .c array files+ /r - Create output as .raw files+ /o <path> - Set the output directory to <path>\n+ /t <platform> - Select target plaform+ AVR : Most Arduinos, Bobuion+ ARM : Arduino Due, Teensy, TI CC3200 LaunchPad+ PIC32 : All chipKit boards\n++ You must specify either /c or /r. All other parameters are optional.+ If /o is ommited the current directory will be used for output.+ If /t is ommited the target platform will be set to AVR.+ ```++## Differences to the Windows tool:++ * You can specifiy as many files as you want, not only a directory (`mydir/*.jpg` still works, the shell does all the work for you)+ * If you specify a target platform while converting to `.raw` the platform will not be printed+ * If you specify a non-existing directory as target, it will be created+ * The length of the array is NOT preceded by `0x`+ * You can use the flags in any order you want++## Binaries++ * precompiled binary for Ubuntu distributions in `bin/UTFTConverter_u` (compiled on Linux Mint 17)+ * precompiled binary for Windows in `bin/UTFTConverter_w.exe` (compiled on Windows 7) (TODO)++## Installation:++**Step 1**: Install the Glasgow Haskell Compiler and `cabal` OR the Haskell packaging tool++#### For Ubuntu distributions:+ * `sudo apt-get install ghc`+ * download the `cabal-install.tar.gz` from [cabal](http://hackage.haskell.org/package/cabal-install)+ * unpack the `.tar.gz file`, the cabal-install folder should include a `bootstrap.sh`+ * run `./bootstrap.sh`++#### For any operation system:+ * Follow the instructions for your operation system on [Haskell Platform](http://haskell.org/platform)+++**Step 2**: When you have `ghc` and `cabal` OR the Haskell Platform installed:++#### If you want the library AND the binary (the easier way) (still to be uploaded on hackage):+ * run `cabal install UTFTConverter`+ * the executable is now at `~/.cabal/bin/UTFTConverter`++#### If you want to build it by hand:+ * download the JuicyPixels library - `cabal install JuicyPixels`+ * download the git repository - `git clone http://github.com/cirquit/UTFTConverter`+ * run `make` and the binary is in the same directory OR+ * run `cabal install` and the binary is in `/dist/build/` and in your home directory under `~/.cabal/bin/UTFTConverter`++#### To do:++ * maybe add resizing with a basic linear algorithm+ * I'm planning to make a simple API that you can start locally which accepts any below defined formats and responses with the parsed `.raw` file. (another project)+ * add `stack` support++#### About:++While working on a streaming project on the Arduino, I needed to convert `.jpg's` to the `.c` or `.raw` format on the fly and send them to the Arduino. Unfortunately, the tools that were included in the corresponding UTFT library were compiled for Windows and there was no public API to do so. That's why I thought it would be a nice first project, that some people could benefit from.
+ Setup.lhs view
@@ -0,0 +1,4 @@+#! /usr/bin/env runhaskell++import Distribution.Simple+main = defaultMain
+ UTFTConverter.cabal view
@@ -0,0 +1,167 @@+name: UTFTConverter+category: Graphics, Text+version: 0.1.0.0+license: MIT+cabal-version: >=1.10+license-file: LICENSE+author: Alexander Isenko+maintainer: Alexander Isenko <alex.isenko@googlemail.com>+homepage: www.github.com/cirquit/UTFTConverter+bug-reports: www.github.com/cirquit/UTFTConverter/issues+build-type: Simple+tested-with: GHC == 7.8.3+synopsis: Processing popular picture formats into .c or .raw format in RGB565+description:+ __About:__+ .+ This package was created because I could not find a tool that can be used on UNIX systems to+ parse a picture into an array of RGB565 values and save them to a @.c@ or @.raw@ file. Both formats+ are used by the <http://www.rinkydinkelectronics.com/library.php?id=51 UTFT library> to display pictures on to a TFT display.+ .+ This package includes, but not limited to, an executable which is made to be as identical as possible+ to the corresponding tool that is also downloaded with the <http://www.rinkydinkelectronics.com/library.php?id=51 UTFT library>.+ .+ __Usage:__+ .+ >./UTFTConverter <filespec> /c|r [/o <path>] [/t AVR|ARM|PIC32]+ >+ ><filespec>: File(s) to convert+ >parameters: /c - Create output as .c array files+ > /r - Create output as .raw files+ > /o <path> - Set the output directory to <path>+ > /t <platform> - Select target plaform+ > AVR : Most Arduinos, Bobuion+ > ARM : Arduino Due, Teensy, TI CC3200 LaunchPad+ > PIC32 : All chipKit boards+ >+ >You must specify either /c or /r. All other parameters are optional.+ >If /o is ommited the current directory will be used for output.+ >If /t is ommited the target platform will be set to AVR.'+ .+ .+ __Supported formats:__+ .+ The exported library is using <http://hackage.haskell.org/package/JuicyPixels JuicyPixels> to convert incoming+ .+ * @.gif@+ .+ * @.png@+ .+ * @.jpg \/\ .jpe \/\ .jpeg@+ .+ * @.bmp@+ .+ * @.tga@+ .+ and translates every pixel to a @RGB565@ format, which is a 4 digit hex number.+ .+ __Library:__+ .+ The only really reusable library is the @Format.RGB565@.+ It support the conversion from+ .+ * @RGB@ to @RGB565@ as @(Word8, Word8, Word8) -> Int@+ * @Int@ to @Hex@ as @Int -> String@+ .+ __Example usage:__+ .+ >>> toRGB565 (0, 0, 255)+ 31++ >>> toRGB565Hex (0, 0, 255)+ "001F"++ >>> toHex 100+ 64++ >>> to4Hex 100+ "0064"++ >>> to6Hex 100+ "000064"++extra-source-files:+ Makefile+ README.md++source-repository head+ type: git+ location: git://github.com/cirquit/UTFTConverter.git++-- _______________________+--| |+--| Library configuration |+--|_______________________|++library+ build-depends:+ base >=4.7 && <4.8 ,+ filepath >=1.3 && <1.4 ,+ directory >=1.2 && <1.3 ,+ time >=1.4 && <1.5 ,+ bytestring >=0.10 && <0.11 ,+ JuicyPixels >=3.2 && <3.3++ exposed-modules:+ Format ,+ Format.C ,+ Format.Converter ,+ Format.Raw ,+ Format.RGB565++ other-extensions:+ BangPatterns ,+ TypeSynonymInstances++ hs-source-dirs: src+ default-language: Haskell2010++-- __________________________+--| |+--| Executable configuration |+--|__________________________|++executable UTFTConverter+ build-depends:+ base >=4.7 && <4.8 ,+ filepath >=1.3 && <1.4 ,+ directory >=1.2 && <1.3 ,+ time >=1.4 && <1.5 ,+ bytestring >=0.10 && <0.11 ,+ JuicyPixels >=3.2 && <3.3 ,+ UTFTConverter ==0.1.*++ other-extensions:+ BangPatterns ,+ TypeSynonymInstances++ main-is: Main.hs+ hs-source-dirs: main-src+ default-language: Haskell2010+ ghc-options: -Wall -O2++-- __________________________+--| |+--| Test suite configuration |+--|__________________________|++test-suite lib-tests+ build-depends:+ base >=4.7 && <4.8 ,+ filepath >=1.3 && <1.4 ,+ directory >=1.2 && <1.3 ,+ time >=1.4 && <1.5 ,+ bytestring >=0.10 && <0.11 ,+ JuicyPixels >=3.2 && <3.3 ,+ hspec ==2.1.* ,+ UTFTConverter ==0.1.*++ other-extensions:+ BangPatterns ,+ TypeSynonymInstances++ main-is: Tests.hs+ hs-source-dirs: tests+ default-language: Haskell2010+ ghc-options: -Wall -O2+ type: exitcode-stdio-1.0
+ main-src/Main.hs view
@@ -0,0 +1,118 @@+module Main where++import System.FilePath.Posix (takeExtension, (</>))+import System.Directory (getCurrentDirectory, createDirectoryIfMissing,+ doesFileExist)+import System.Environment (getArgs, getProgName)+import System.Exit (exitFailure)++import Format.Converter (pictureToC, pictureToRaw)+import Format.C (Platform(..))++type Args = [String]++data FileType = C | Raw++data Option = Option+ { outdir :: FilePath+ , platform :: Platform+ , filetype :: Maybe FileType+ , files :: [FilePath]+ }+++main :: IO ()+main = do+ args <- getArgs+ defopts <- defaultOptions+ opt <- argsParser args defopts+ case opt of+ Option _ _ _ [] -> putStrLn "ERROR: No valid files found to convert." >> help >> exitFailure+ Option outdir' platform' (Just C) files' -> printOpt (outdir', platform', C ) >> mapM_ (pictureToC platform' outdir') files'+ Option outdir' platform' (Just Raw) files' -> printOpt (outdir', platform', Raw) >> mapM_ (pictureToRaw outdir') files'+ Option _ _ Nothing _ -> putStrLn "ERROR: No filetype defined." >> help >> exitFailure++defaultOptions :: IO Option+defaultOptions = do+ dir <- getCurrentDirectory+ return $ Option dir AVR Nothing []++argsParser :: Args -> Option -> IO Option+argsParser [] opt = return opt++-- filetype parser+argsParser ("/c" :xs) opt+ | Just _ <- filetype opt = putStrLn "ERROR: You can only specifiy one target platform" >> exitFailure+ | otherwise = argsParser xs (opt { filetype = Just C } )+argsParser ("/r" :xs) opt+ | Just _ <- filetype opt = putStrLn "ERROR: You can only specifiy one target platform" >> exitFailure+ | otherwise = argsParser xs (opt { filetype = Just Raw } )++-- platform parser+argsParser ("/t":"AVR" :xs) opt = argsParser xs (opt { platform = AVR } )+argsParser ("/t":"PIC32":xs) opt = argsParser xs (opt { platform = PIC32 } )+argsParser ("/t":"ARM" :xs) opt = argsParser xs (opt { platform = ARM } )+argsParser ("/t" :xs) opt = do+ putStrLn "WARNING: Platform not specified or unknown. Using the default platform"+ argsParser xs opt++-- output directory parser++argsParser ("/o":"/t" :xs) opt = do+ putStrLn "WARNING: Output directory is missing. Using default output directory"+ argsParser ("/t":xs) opt+argsParser ("/o":fp :xs) opt = do+ case takeExtension fp of+ "" -> do+ createDirectoryIfMissing True fp+ dir <- getCurrentDirectory+ argsParser xs (opt { outdir = dir </> fp } )+ _ -> do+ putStrLn "WARNING: Output directory has an extention. Using default output directory"+ argsParser xs opt+argsParser ("/o" :xs) opt = do+ putStrLn "WARNING: Output directory is missing. Using default output directory"+ argsParser xs opt+++-- files parser+argsParser (fp :xs) opt = do+ exists <- doesFileExist fp+ case (exists, takeExtension fp) of+ (True, ".jpg") -> argsParser xs (opt { files = fp : files opt})+ (True, ".jpeg") -> argsParser xs (opt { files = fp : files opt})+ (True, ".jpe") -> argsParser xs (opt { files = fp : files opt})+ (True, ".bmp") -> argsParser xs (opt { files = fp : files opt})+ (True, ".png") -> argsParser xs (opt { files = fp : files opt})+ (True, ".gif") -> argsParser xs (opt { files = fp : files opt})+ (True, ".tga") -> argsParser xs (opt { files = fp : files opt})+ (True, _) -> putStrLn ("WARNING: This format is not supported ~ " ++ fp) >> argsParser xs opt+ (False, _) -> putStrLn ("WARNING: Unreconized flag ~ " ++ fp) >> argsParser xs opt++printOpt :: (FilePath, Platform, FileType) -> IO ()+printOpt (outdir', platform', C) = do+ putStrLn $ "Converting to : .c array file(s)"+ putStrLn $ "Target platform : " ++ show platform'+ putStrLn $ "Output directory : " ++ outdir' ++ "\n"+ putStrLn "Processing file(s):"+printOpt (outdir', _, Raw) = do+ putStrLn $ "Converting to : .raw file(s)"+ putStrLn $ "Output directory : " ++ outdir' ++ "\n"+ putStrLn "Processing file(s):"++help :: IO ()+help = do+ name <- getProgName+ putStrLn "\nUsage: "+ putStrLn $ " " ++ name ++ " <filespec> /c|r [/o <path>] [/t AVR|ARM|PIC32]\n"+ putStrLn "<filespec>: File(s) to convert"+ putStrLn "parameters: /c - Create output as .c array files"+ putStrLn " /r - Create output as .raw files"+ putStrLn " /o <path> - Set the output directory to <path>\n"+ putStrLn " /t <platform> - Select target plaform"+ putStrLn " AVR : Most Arduinos, Bobuion"+ putStrLn " ARM : Arduino Due, Teensy, TI CC3200 LaunchPad"+ putStrLn " PIC32 : All chipKit boards\n"+ putStrLn "You must specify either /c or /r. All other parameters are optional."+ putStrLn "If /o is ommited the current directory will be used for output."+ putStrLn "If /t is ommited the target platform will be set to AVR."
+ src/Format.hs view
@@ -0,0 +1,24 @@+-----------------------------------------------------------------------------+-- |+-- Module : Format+-- License : MIT+-- Maintainer : Alexander Isenko <alex.isenko@googlemail.com>+-- Summary of all modules+--+-- * @Format.C@ exports the Platform datatype and the needed formatting for the @.c@ file+-- * @Format.Raw@ exports the pretty prints the array of rgb values to 16 in a row+-- * @Format.RGB565@ exports the functions to convert from a RGB value to RGB565 and the needed hex conversions+-- * @Format.Converter@ exports the functions that create the @.c@ or @.raw@ files when the @FilePath@ is given+------------------------------------------------------------------------------+module Format+ (+ module Format.C+ , module Format.Raw+ , module Format.RGB565+ , module Format.Converter+ ) where++import Format.C+import Format.Raw+import Format.RGB565+import Format.Converter
+ src/Format/C.hs view
@@ -0,0 +1,84 @@++-----------------------------------------------------------------------------+-- |+-- Module : Format.C+-- License : MIT+-- Maintainer : Alexander Isenko <alex.isenko@googlemail.com>+--+-- @Format.C@ exports the Platform datatype and the needed formatting for the @.c@ file+-----------------------------------------------------------------------------++module Format.C (toCFile, Platform(..)) where++import Data.Time (UTCTime)+import Format.RGB565 (to4Hex)+++-- | The Platform datatype has three constructors, for every supported architecture+--+-- The Show instance is derived+data Platform = AVR+ | ARM+ | PIC32+ deriving Show++-- | toCFile gets five arguments+--+-- * @[String]@ are the hex strings+-- * @(String, String)@ is the (filename, file extention)+-- * @(Int, Int) is the (width, height)+-- * UTCTime is the current time+-- * Platform is the desired platform to convert to+--+-- The result is a the string which is the full c file with header + unsigned short array + comments+--+-- The created @\\n@ were reformatted by hand+--+-- __Example usage:__+--+-- @+-- λ> let hex = ["0000", "0000", \"FF00\", "00FF","0000", "0000", \"FF00\", "00FF", "0000", "0000", \"FF00\", "00FF", "0000", "0000", \"FF00\", "00FF"]+-- λ> time <- getCurrentTime+-- λ> toCFile hex ("example", ".jpg") (4,4) time AVR+-- "\/\/ Generated by : UTFTConverter v0.1+-- \/\/ Generated from : example.jpg+-- \/\/ Time generated : 2015-09-01 19:56:40.438958 UTC+-- \/\/ Image Size : 4x4 pixels+-- \/\/ Memory usage : 32 bytes+--+-- #include \<avr/pgmspace.h\>+-- const unsigned short example[16] PROGMEM={+-- 0x0000, 0x0000, 0xFF00, 0x00FF, 0x0000, 0x0000, 0xFF00, 0x00FF, 0x0000, 0x0000, 0xFF00, 0x00FF, 0x0000, 0x0000, 0xFF00, 0x00FF, \/\/ 0x0010 (16) pixels+-- };"+-- @++toCFile :: [String] -> (String, String) -> (Int, Int) -> UTCTime -> Platform -> String+toCFile content (name, ext) (w, h) time platform =+ let clength = show (length content)+ memusage = show (length content * 2)+ include | AVR <- platform = "\n#include <avr/pgmspace.h>\n"+ | otherwise = ""+ arrname | AVR <- platform = "const unsigned short " ++ name ++ '[' : clength ++ "] PROGMEM={\n"+ | otherwise = "const unsigned short " ++ name ++ '[' : clength ++ "] ={\n"+ in+ "// Generated by : UTFTConverter v0.1\n" +++ "// Generated from : " ++ name ++ ext ++ "\n" +++ "// Time generated : " ++ show time ++ "\n" +++ "// Image Size : " ++ show w ++ 'x' : show h ++ " pixels\n" +++ "// Memory usage : " ++ memusage ++ " bytes\n" +++ include +++ "\n" +++ arrname +++ toCArray content 1++toCArray :: [String] -> Int -> String+toCArray [] _ = "};"+toCArray (x:xs) n = printHex x ++ com ++ toCArray xs (n+1)+ where com | n `mod` 16 == 0 = printCom n+ | otherwise = ""++printHex :: String -> String+printHex hx = '0':'x': hx ++ ", "++printCom :: Int -> String+printCom n = ' ':' ':'/':'/':' ':'0':'x': to4Hex n ++ ' ':'(': show n ++ ") pixels\n"
+ src/Format/Converter.hs view
@@ -0,0 +1,219 @@+{-# LANGUAGE TypeSynonymInstances #-}++-----------------------------------------------------------------------------+-- |+-- Module : Format.Converter+-- License : MIT+-- Maintainer : Alexander Isenko <alex.isenko@googlemail.com>+--+-- @Format.Converter@ exports the functions that create the @.c@ or @.raw@ files when the @FilePath@ is given+--+-- Both @pictureToRaw@ and @pictureToC@ accept these picture formats+--+-- * @.gif@+-- * @.png@+-- * @.jpg \/\ .jpe \/\ .jpeg@+-- * @.bmp@+-- * @.tga@+--+-- If it's not any of those formats, an error gets thrown+-- If there is an error while decoding the pictures it gets printed out++-----------------------------------------------------------------------------++module Format.Converter (pictureToRaw, pictureToC) where++import qualified Data.ByteString as BS (readFile)+import Data.ByteString.Lazy (toStrict)+import System.FilePath.Posix (takeBaseName, (</>),+ takeExtension, takeFileName)+import Data.Time (getCurrentTime)++import Codec.Picture.Types+import Codec.Picture.Saving (imageToBitmap)+import Codec.Picture.Jpg (decodeJpeg)+import Codec.Picture.Bitmap (decodeBitmap)+import Codec.Picture.Png (decodePng)+import Codec.Picture.Gif (decodeGif)+import Codec.Picture.Tga (decodeTga)++import Format.RGB565 (toRGB565Hex)+import Format.C (toCFile, Platform())+import Format.Raw (toRawFile)++-- | pictureToRaw takes a picture, decodes it, parses every pixel to a 4 digit RGB565 hex and saves it to+-- a file with the same name and a @.raw@ extention in the specified directory+--+-- This function takes two arguments+--+-- * first @FilePath@ is the directory to save the file to+-- * second @FilePath@ is the filepath to the picture+--+-- __Possible errors:__+--+-- * Can throw an error if the picture format is not supported+-- * If there is an error while decoding the pictures the file gets skipped and an error message is printed out+--+-- __Results:__+--+-- If the conversion was successful, a message gets printed+--+-- @+-- cat_01_bmp_120x120.bmp --> cat_01_bmp_120x120.raw+-- @+--+-- __Example usage:__ (assumed that the picture is in the directory where ghci was started)+--+-- @+-- λ> dir <- getCurrentDirectory+-- λ> pictureToRaw dir "cat_01_bmp_120x120.bmp"+-- cat_01_bmp_120x120.bmp --> cat_01_bmp_120x120.raw+-- @++pictureToRaw :: FilePath -> FilePath -> IO ()+pictureToRaw saveTo fp = do+ case takeExtension fp of+ (".jpg") -> jpgToDynImg fp >>= dynimgToRaw saveTo fp+ (".jpeg") -> jpgToDynImg fp >>= dynimgToRaw saveTo fp+ (".jpe") -> jpgToDynImg fp >>= dynimgToRaw saveTo fp+ (".bmp") -> bmpToDynImg fp >>= dynimgToRaw saveTo fp+ (".png") -> pngToDynImg fp >>= dynimgToRaw saveTo fp+ (".gif") -> gifToDynImg fp >>= dynimgToRaw saveTo fp+ (".tga") -> tgaToDynImg fp >>= dynimgToRaw saveTo fp+ (_) -> error "Argument filter let through some unsupported types"+++-- | pictureToC takes a picture, decodes it, parses every pixel to a 4 digit RGB565 hex, adds the header+-- based on the desired platform and saves it to a file with the same name and a @.c@ extention in the specified+-- directory+--+-- This function takes three arguments+--+-- * @Platform@ is the desired platform to convert to+-- * first @FilePath@ is the directory to save the file to+-- * second @FilePath@ is the filepath to the picture+--+-- __Possible errors:__+--+-- * Can throw an error if the picture format is not supported+-- * If there is an error while decoding the pictures the file gets skipped and an error message is printed out+--+-- __Results:__+--+-- If the conversion was successful, a message gets printed+--+-- @+-- cat_01_bmp_120x120.bmp --> cat_01_bmp_120x120.c+-- @+--+-- __Example usage:__ (assumed that the picture is in the directory where ghci was started)+--+-- @+-- λ> dir <- getCurrentDirectory+-- λ> pictureToC AVR dir "cat_01_bmp_120x120.bmp"+-- cat_01_bmp_120x120.bmp --> cat_01_bmp_120x120.c+-- @++pictureToC :: Platform -> FilePath -> FilePath -> IO ()+pictureToC platform saveTo fp = do+ case takeExtension fp of+ (".jpg") -> jpgToDynImg fp >>= dynimgToC platform saveTo fp+ (".jpeg") -> jpgToDynImg fp >>= dynimgToC platform saveTo fp+ (".jpe") -> jpgToDynImg fp >>= dynimgToC platform saveTo fp+ (".bmp") -> bmpToDynImg fp >>= dynimgToC platform saveTo fp+ (".png") -> pngToDynImg fp >>= dynimgToC platform saveTo fp+ (".gif") -> gifToDynImg fp >>= dynimgToC platform saveTo fp+ (".tga") -> tgaToDynImg fp >>= dynimgToC platform saveTo fp+ (_) -> error "Argument filter let through some unsupported types"++jpgToDynImg :: FilePath -> IO (Maybe DynamicImage)+jpgToDynImg fp = do+ bs <- BS.readFile fp+ case decodeJpeg bs of+ Left err -> putStrLn ("Error happend while decoding the jpg: " ++ err) >> return Nothing+ Right dynimg ->+ case decodeBitmap (toStrict (imageToBitmap dynimg)) of+ Left err' -> putStrLn ("Error happend while decoding the converted bmp: " ++ err') >> return Nothing+ Right dynimg' -> return $ Just dynimg'++bmpToDynImg :: FilePath -> IO (Maybe DynamicImage)+bmpToDynImg fp = do+ bs <- BS.readFile fp+ case decodeBitmap bs of+ Left err -> putStrLn ("Error happend while decoding the bmp: " ++ err) >> return Nothing+ Right dynimg -> return $ Just dynimg++pngToDynImg :: FilePath -> IO (Maybe DynamicImage)+pngToDynImg fp = do+ bs <- BS.readFile fp+ case decodePng bs of+ Left err -> putStrLn ("Error happend while decoding the png: " ++ err) >> return Nothing+ Right dynimg -> return $ Just dynimg++gifToDynImg :: FilePath -> IO (Maybe DynamicImage)+gifToDynImg fp = do+ bs <- BS.readFile fp+ case decodeGif bs of+ Left err -> putStrLn ("Error happend while decoding the gif: " ++ err) >> return Nothing+ Right dynimg -> return $ Just dynimg++tgaToDynImg :: FilePath -> IO (Maybe DynamicImage)+tgaToDynImg fp = do+ bs <- BS.readFile fp+ case decodeTga bs of+ Left err -> putStrLn ("Error happend while decoding the tga: " ++ err) >> return Nothing+ Right dynimg -> return $ Just dynimg+++dynimgToRaw :: FilePath -> FilePath -> Maybe DynamicImage -> IO ()+dynimgToRaw _ _ Nothing = return ()+dynimgToRaw saveTo fp (Just dynimg) = do+ let img = fromDynamicImage dynimg+ name = takeBaseName fp+ fname = takeFileName fp+ content = toRawFile (encodePixels img)+ writeFile (saveTo </> name ++ ".raw") content+ putStrLn $ fname ++ " --> " ++ name ++ ".raw"++dynimgToC :: Platform -> FilePath -> FilePath -> Maybe DynamicImage -> IO ()+dynimgToC _ _ _ Nothing = return ()+dynimgToC platform saveTo fp (Just dynimg) = do+ time <- getCurrentTime+ let img@(Image w h _) = fromDynamicImage dynimg+ name = takeBaseName fp+ ext = takeExtension fp+ fname = takeFileName fp+ content = toCFile (encodePixels img) (name, ext) (w, h) time platform+ writeFile (saveTo </> name ++ ".c") content+ putStrLn $ fname ++ " --> " ++ name ++ ".c"++encodePixels :: Image PixelRGBA8 -> [String]+encodePixels img@(Image w h _) = [ format (pixelAt img y x) | x <- [0..(h-1)], y <- [0..(w-1)]]+ where format (PixelRGBA8 r g b _) = toRGB565Hex (r, g, b)++---- Copied from+---- See http://hackage.haskell.org/package/JuicyPixels-util-0.2/docs/Codec-Picture-RGBA8.html++class ToPixelRGBA8 a where+ toRGBA8 :: a -> PixelRGBA8++instance ToPixelRGBA8 Pixel8 where+ toRGBA8 b = PixelRGBA8 b b b 255++instance ToPixelRGBA8 PixelYA8 where+ toRGBA8 (PixelYA8 l a) = PixelRGBA8 l l l a++instance ToPixelRGBA8 PixelRGB8 where+ toRGBA8 (PixelRGB8 r g b) = PixelRGBA8 r g b 255++instance ToPixelRGBA8 PixelRGBA8 where+ toRGBA8 = id++fromDynamicImage :: DynamicImage -> Image PixelRGBA8+fromDynamicImage (ImageY8 img) = pixelMap toRGBA8 img+fromDynamicImage (ImageYA8 img) = pixelMap toRGBA8 img+fromDynamicImage (ImageRGB8 img) = pixelMap toRGBA8 img+fromDynamicImage (ImageRGBA8 img) = img+fromDynamicImage _ = error "fromDynamicImage in Converter got a not supported DynamicImage format!"++---- end of Codec.Picture.RGBA8
+ src/Format/RGB565.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE BangPatterns #-}++-----------------------------------------------------------------------------+-- |+-- Module : Format.RGB565+-- License : MIT+-- Maintainer : Alexander Isenko <alex.isenko@googlemail.com>+--+-- @Format.RGB565@ exports the functions to convert from a RGB value to RGB565 and the needed hex conversions+--+-----------------------------------------------------------------------------+module Format.RGB565 (toRGB565, toRGB565Hex, to6Hex, to4Hex, toHex) where++import Data.Bits (shiftL, shiftR, (.|.))+import Data.Word (Word8())+++-- | toRGB565Hex takes a RGB value and converts it into a RGB565 encoded 4 digit hex String+--+-- __Example usage:__+--+-- @+-- λ> toRGB565Hex (255, 0, 0)+-- \"F800\"+-- @++toRGB565Hex :: (Word8, Word8, Word8) -> String+toRGB565Hex rgb = to4Hex (toRGB565 rgb)++-- | toRGB565 takes a RGB value and converts it into a RGB565 encoded Int+--+-- __Example usage:__+--+-- @+-- λ> toRGB565 (255, 0, 0)+-- 63488+-- @++toRGB565 :: (Word8, Word8, Word8) -> Int+toRGB565 (r, g, b) = r' .|. g' .|. b'+ where r' = ((toInt r) `shiftR` 3) `shiftL` 11+ g' = ((toInt g) `shiftR` 2) `shiftL` 5+ b' = (toInt b) `shiftR` 3++toInt :: Word8 -> Int+toInt = fromIntegral++-- | toHex takes an Int and converts it into a hex String+--+-- __Example usage:__+--+-- @+-- λ> toHex 255+-- \"F8\"+-- @+toHex :: Int -> String+toHex = go ""+ where go !acc x =+ case quotRem x 16 of+ (0,r) -> hex r : acc+ (q,r) -> go (hex r : acc) q++-- | to4Hex takes an Int and converts it to a hex String and adds zeros until four digits are filled+--+-- __Example usage:__+--+-- @+-- λ> to4Hex 255+-- "00F8"+-- @+to4Hex :: Int -> String+to4Hex x = go hx+ where hx = toHex x+ go :: String -> String+ go !str+ | length str < 4 = go ('0':str)+ | otherwise = str++-- | to6Hex takes an Int and converts it to a hex String and adds zeros until six digits are filled+--+-- __Example usage:__+--+-- @+-- λ> to4Hex 255+-- "0000F8"+-- @+to6Hex :: Int -> String+to6Hex x = go hx+ where hx = toHex x+ go :: String -> String+ go !str+ | length str < 6 = go ('0':str)+ | otherwise = str++++hex :: Int -> Char+hex 0 = '0'+hex 1 = '1'+hex 2 = '2'+hex 3 = '3'+hex 4 = '4'+hex 5 = '5'+hex 6 = '6'+hex 7 = '7'+hex 8 = '8'+hex 9 = '9'+hex 10 = 'A'+hex 11 = 'B'+hex 12 = 'C'+hex 13 = 'D'+hex 14 = 'E'+hex 15 = 'F'+hex x = error $ "Exceeded bounds of 'hex' - " ++ show x
+ src/Format/Raw.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE BangPatterns #-}+-----------------------------------------------------------------------------+-- |+-- Module : Format+-- License : MIT+-- Maintainer : Alexander Isenko <alex.isenko@googlemail.com>+--+-- @Format.Raw@ exports the pretty prints the array of rgb values to 16 in a row+------------------------------------------------------------------------------++module Format.Raw (toRawFile) where++-- | toRawFile takes the hex strings and returns a concatinated string with a @\\n@+-- after every 16th hex number+--+-- __Example usage:__+--+-- @+-- λ> let hex = ["0000", "0000", \"FF00\", "00FF","0000", "0000", \"FF00\", "00FF", "0000", "0000", \"FF00\", "00FF", "0000", "0000", \"FF00\", "00FF", "0000"]+-- λ> toRawFile hex+-- "0000 0000 FF00 00FF 0000 0000 FF00 00FF 0000 0000 FF00 00FF 0000 0000 FF00 00FF \\n 0000 "+-- @+toRawFile :: [String] -> String+toRawFile l = toRawArray l 1+ where toRawArray :: [String] -> Int -> String+ toRawArray [] _ = []+ toRawArray (x:xs) n = x ++ nl ++ toRawArray xs (n + 1)+ where nl+ | n `mod` 16 == 0 = "\n"+ | otherwise = " "
+ tests/Tests.hs view
@@ -0,0 +1,148 @@+module Main where++import Format.RGB565 (toHex, to4Hex, toRGB565, toRGB565Hex)+import Format.Converter (pictureToRaw)+import Test.Hspec+import System.Directory (getCurrentDirectory, doesDirectoryExist,+ createDirectory, getDirectoryContents,+ removeDirectoryRecursive)+import System.FilePath.Posix ((</>))+import Data.List (foldl')++main :: IO ()+main = do+ dir <- getCurrentDirectory+ exist <- doesDirectoryExist "tmp"+ if exist+ then removeDirectoryRecursive "tmp"+ else return ()+ hspec $ do+ describe "UTFTConverter Library" $ do+ describe "Format.RGB565" $ do+ describe "toHex" $ do+ it "toHex should be 0" $ do+ toHex 0 `shouldBe` "0"+ it "toHex should be C" $ do+ toHex 12 `shouldBe` "C"+ it "toHex should be C6" $ do+ toHex 198 `shouldBe` "C6"+ it "toHex should be 7A" $ do+ toHex 122 `shouldBe` "7A"+ it "toHex should be FF" $ do+ toHex 255 `shouldBe` "FF"+ it "toHex should be 10000" $ do+ toHex 65536 `shouldBe` "10000"++ describe "to4Hex" $ do+ it "to4Hex 0 should be 0000" $ do+ to4Hex 0 `shouldBe` "0000"+ it "to4Hex 12 should be 000C" $ do+ to4Hex 12 `shouldBe` "000C"+ it "to4Hex 198 should be 00C6" $ do+ to4Hex 198 `shouldBe` "00C6"+ it "to4Hex 122 should be 007A" $ do+ to4Hex 122 `shouldBe` "007A"+ it "to4Hex 255 should be 00FF" $ do+ to4Hex 255 `shouldBe` "00FF"+ it "to4Hex 65535 should be FFFF" $ do+ to4Hex 65535 `shouldBe` "FFFF"++ describe "toRGB565" $ do+ it "toRGB565 (255, 255, 255) should be 65535" $ do+ toRGB565 (255, 255, 255) `shouldBe` 65535+ it "toRGB565 (123, 123, 123) should be 31695" $ do+ toRGB565 (123, 123, 123) `shouldBe` 31695+ it "toRGB565 (90, 255, 0) should be 24544" $ do+ toRGB565 ( 90, 255, 0) `shouldBe` 24544+ it "toRGB565 (0, 0, 0) should be 0" $ do+ toRGB565 ( 0, 0, 0) `shouldBe` 0++ describe "toRGB565Hex" $ do+ it "toRGB565Hex (255, 255, 255) should be FFFF" $ do+ toRGB565Hex (255, 255, 255) `shouldBe` "FFFF"+ it "toRGB565Hex (123, 123, 123) should be 7BCF" $ do+ toRGB565Hex (123, 123, 123) `shouldBe` "7BCF"+ it "toRGB565Hex (90, 255, 0) should be 5FE0" $ do+ toRGB565Hex ( 90, 255, 0) `shouldBe` "5FE0"+ it "toRGB565Hex (0, 0, 0) should be 0" $ do+ toRGB565Hex ( 0, 0, 0) `shouldBe` "0000"++ describe "Format.Converter" $ do+ it "tests/examples/cat_01 exists" $ do+ exists <- doesDirectoryExist (dir </> "tests" </> "examples" </> "cat_01")+ exists `shouldBe` True+ it "tests/examples/cat_02 exists" $ do+ exists <- doesDirectoryExist (dir </> "tests" </> "examples" </> "cat_02")+ exists `shouldBe` True+ it "tests/examples/cat_03 exists" $ do+ exists <- doesDirectoryExist (dir </> "tests" </> "examples" </> "cat_03")+ exists `shouldBe` True+ it "15 example files should be in tests/examples/cat_0?" $ do+ dir1 <- getExamplePicsPath dir ("tests" </> "examples" </> "cat_01")+ dir2 <- getExamplePicsPath dir ("tests" </> "examples" </> "cat_02")+ dir3 <- getExamplePicsPath dir ("tests" </> "examples" </> "cat_03")+ length (dir1 ++ dir2 ++ dir3) `shouldBe` 15+ it "cat_01 pics should be all converted to almost the same .raw-files (>99% similarity)" $ do+ createDirectory "tmp"+ pics <- getExamplePicsPath dir ("tests" </> "examples" </> "cat_01")+ mapM_ (pictureToRaw (dir </> "tmp")) pics+ rawfps <- getExamplePicsPath dir "tmp"+ [p1, p2, p3, p4, p5] <- mapM readFile rawfps+ let r1 = picSimilarity p1 p2+ r2 = picSimilarity p1 p3+ r3 = picSimilarity p1 p4+ r4 = picSimilarity p1 p5+ putStrLn $ "Pic similarity for p1 ~ p2: " ++ show r1+ putStrLn $ "Pic similarity for p1 ~ p3: " ++ show r2+ putStrLn $ "Pic similarity for p1 ~ p4: " ++ show r3+ putStrLn $ "Pic similarity for p1 ~ p5: " ++ show r4+ removeDirectoryRecursive (dir </> "tmp")+ ((r1 + r2 + r3 + r4) / 4) > 98 `shouldBe` True+ it "cat_02 pics should be all converted to almost the same .raw-files (>99% similarity)" $ do+ createDirectory "tmp"+ pics <- getExamplePicsPath dir ("tests" </> "examples" </> "cat_02")+ mapM_ (pictureToRaw (dir </> "tmp")) pics+ rawfps <- getExamplePicsPath dir "tmp"+ [p1, p2, p3, p4, p5] <- mapM readFile rawfps+ let r1 = picSimilarity p1 p2+ r2 = picSimilarity p1 p3+ r3 = picSimilarity p1 p4+ r4 = picSimilarity p1 p5+ putStrLn $ "Pic similarity for p1 ~ p2: " ++ show r1+ putStrLn $ "Pic similarity for p1 ~ p3: " ++ show r2+ putStrLn $ "Pic similarity for p1 ~ p4: " ++ show r3+ putStrLn $ "Pic similarity for p1 ~ p5: " ++ show r4+ removeDirectoryRecursive (dir </> "tmp")+ ((r1 + r2 + r3 + r4) / 4) > 98 `shouldBe` True+ it "cat_03 pics should be all converted to almost the same .raw-files (>99% similarity)" $ do+ createDirectory "tmp"+ pics <- getExamplePicsPath dir ("tests" </> "examples" </> "cat_03")+ mapM_ (pictureToRaw (dir </> "tmp")) pics+ rawfps <- getExamplePicsPath dir "tmp"+ [p1, p2, p3, p4, p5] <- mapM readFile rawfps+ let r1 = picSimilarity p1 p2+ r2 = picSimilarity p1 p3+ r3 = picSimilarity p1 p4+ r4 = picSimilarity p1 p5+ putStrLn $ "Pic similarity for p1 ~ p2: " ++ show r1+ putStrLn $ "Pic similarity for p1 ~ p3: " ++ show r2+ putStrLn $ "Pic similarity for p1 ~ p4: " ++ show r3+ putStrLn $ "Pic similarity for p1 ~ p5: " ++ show r4+ removeDirectoryRecursive (dir </> "tmp")+ ((r1 + r2 + r3 + r4) / 4) > 98 `shouldBe` True+++getExamplePicsPath :: FilePath -> FilePath -> IO [FilePath]+getExamplePicsPath curdir picdir = do+ content <- getDirectoryContents (curdir </> picdir)+ let picnames = filter (`notElem` [".", ".."]) content+ return $ map (\x -> curdir </> picdir </> x) picnames++picSimilarity :: String -> String -> Double+picSimilarity p1 p2 =+ let f1 = words p1+ f2 = words p2+ (fcount, count) = foldl' (\(fc, c) (a, b) -> if a == b+ then (fc , c + 1)+ else (fc + 1, c + 1)) (0,0) (zip f1 f2)+ in (100 - (fcount / count))