haskell-toys-0.1.0.0: Little command line toys written in Haskell

Copyright(c) 2016, M J Oldfield
StabilityExperimental
Safe HaskellNone
LanguageHaskell2010

Toy.JuicyPixels

Description

Rationale

Juicy Pixels is a great package for loading and saving images, and I often use it in little command line tools for munging images. However, over time I find those scripts have rather too much common code for comfort.

This module is an attempt to abstract that common boilerplate code.

Examples

Here is the complete source for a program to reverse the bit-order of each image on the command line. For example, foo.gif will be bit-reversed and saved as foo-f.png

import Toy.JuicyPixels
import Codec.Picture

import Data.Word
import Data.Bits
import qualified Data.List as L

main = transformImagesInArgsPNG flipImage (++ "-f")

flipImage :: ImageRGB8 -> ImageRGB8
flipImage = pixelMap (liftRGB flipByte)

flipByte :: Word8 -> Word8
flipByte = memoizeWord8 flipByte'

flipByte' :: Word8 -> Word8
flipByte' x = L.foldl' setBit 0 $ [ 7 - i | i <- [0..7], testBit x i ]

The next example illustrates writing to stdout rather than files. It prints the frequency with which pixels are seen in each image on the command line: the results are essentially useless for photos, but perhaps useful with little icons.

import Toy.JuicyPixels
import Codec.Picture

import qualified Data.List as L

import Text.Printf

main = describeImagesInArgs countPixels

countPixels :: ImageRGB8 -> String
countPixels = concatMap pp . freqs . pixelList

freqs :: (Ord a, Eq a) => [a] -> [(a,Int)]
freqs = map (ps -> (head ps, length ps)) . L.group . L.sort

pp (PixelRGB8 r g b, n) = printf "%3d %3d %3d: %8dn" r g b n

Synopsis

Documentation

type ImageRGB8 = Image PixelRGB8 Source

ImageRGB8 is a convenient shorthand for Image PixelRGB8.

loadImage :: FilePath -> IO (Either String ImageRGB8) Source

loadImage is just like readImage in Codec.Picture but it forces the pixel type to PixelRGB8.

loadImageThen :: (ImageRGB8 -> IO ()) -> FilePath -> IO () Source

loadImageThen loads an image, and, if successful invokes the supplied handler. Said handler should return an IO action.

transformImagePNG :: (ImageRGB8 -> ImageRGB8) -> (String -> String) -> FilePath -> IO () Source

transformImagePNG is just transformImage specialized for writing PNG files.

transformImagesInArgsPNG :: (ImageRGB8 -> ImageRGB8) -> (String -> String) -> IO () Source

transformImagesInArgsPNG is just transformImagesInArgs specialized for writing PNG files.

transformImagePNG' :: (ImageRGB8 -> ImageRGB8) -> FilePath -> IO () Source

transformImagePNG' is just transformImagePNG where the output basename is just the input with -x appended.

transformImage :: (FilePath -> a -> IO ()) -> String -> (ImageRGB8 -> a) -> (String -> String) -> FilePath -> IO () Source

transformImage takes a function which saves something, a suitable suffix for the file in which something is saved, a function for making a something from an Image, a function for transforming the basename, and finally the name of a file to transform.

This apparently bizarre API is useful to make transformers by specifying the first four arguments to leave a FilePath -> IO () signature remaining.

The first two arguments specify how to save the result. The next specify the transformation: both the image data and to the file's basename.

A version specialized to saving PNG files is included: transformImagePNG.

transformImagesInArgs :: (FilePath -> a -> IO ()) -> String -> (ImageRGB8 -> a) -> (String -> String) -> IO () Source

transformArgsAsImages maps transformImage over all the command line arguments.

describeImage :: Show a => (ImageRGB8 -> a) -> FilePath -> IO () Source

iPixelList :: ImageRGB8 -> [(Int, Int, PixelRGB8)] Source

iPixelList turns an image into a list of (x,y,pixel) tuples.

pixelList :: ImageRGB8 -> [PixelRGB8] Source

pixelList turns an image into a list of pixels.

liftRGB :: (Word8 -> Word8) -> PixelRGB8 -> PixelRGB8 Source

liftRGB turns a byte transform to a PixelRGB8 transform by applying the byte transform to all the components independently.

memoizeWord8 :: (Word8 -> Word8) -> Word8 -> Word8 Source

Given a byte transform, memoizeWord8 returns a functionally identical transform which replaces calculation by an unboxed array lookup. Hopefully this will be much faster.