never executed always true always false
1 {-# LANGUAGE RecordWildCards #-}
2 {- |
3 Colors are three dimensional and can be projected into many color spaces
4 with different properties.
5
6 Interpolating directly in the RGB color space is unintuitive and rarely useful.
7 If you want to transition through color, you most likely want either the XYZ space
8 (for physically accurate color transitions) or the LAB space (for esthetically
9 pleasing colors).
10 -}
11 module Reanimate.ColorComponents
12 ( ColorComponents(..)
13 , rgbComponents
14 , hsvComponents
15 , labComponents
16 , xyzComponents
17 , lchComponents
18 , interpolate
19 , interpolateRGB8
20 , interpolateRGBA8
21 , toRGB8
22 , fromRGB8
23 ) where
24
25 import Codec.Picture
26 import Codec.Picture.Types
27 import Data.Colour
28 import Data.Colour.CIE
29 import Data.Colour.CIE.Illuminant (d65)
30 import Data.Colour.RGBSpace
31 import Data.Colour.RGBSpace.HSV
32 import Data.Colour.SRGB
33 import Data.Fixed
34 import Reanimate.Ease
35
36 -- | Constructor and destructor for color's three components.
37 data ColorComponents = ColorComponents
38 { colorUnpack :: Colour Double -> (Double, Double, Double)
39 -- ^ Unpack a color into its three components.
40 , colorPack :: Double -> Double -> Double -> Colour Double
41 -- ^ Restore a color from three coordinates.
42 }
43
44 -- | > interpolate rgbComponents yellow blue
45 --
46 -- <<docs/gifs/doc_rgbComponents.gif>>
47 rgbComponents :: ColorComponents
48 rgbComponents = ColorComponents rgbUnpack sRGB
49 where
50 rgbUnpack :: Colour Double -> (Double, Double, Double)
51 rgbUnpack c =
52 case toSRGB c of
53 RGB r g b -> (r,g,b)
54
55 -- | > interpolate hsvComponents yellow blue
56 --
57 -- <<docs/gifs/doc_hsvComponents.gif>>
58 hsvComponents :: ColorComponents
59 hsvComponents = ColorComponents unpack pack
60 where
61 unpack = hsvView.toSRGB
62 pack a b c = uncurryRGB sRGB $ hsv a b c
63
64 -- | > interpolate labComponents yellow blue
65 --
66 -- <<docs/gifs/doc_labComponents.gif>>
67 labComponents :: ColorComponents
68 labComponents = ColorComponents unpack pack
69 where
70 unpack = cieLABView d65
71 pack = cieLAB d65
72
73 -- | > interpolate xyzComponents yellow blue
74 --
75 -- <<docs/gifs/doc_xyzComponents.gif>>
76 xyzComponents :: ColorComponents
77 xyzComponents = ColorComponents cieXYZView cieXYZ
78
79 -- | > interpolate lchComponents yellow blue
80 --
81 -- <<docs/gifs/doc_lchComponents.gif>>
82 lchComponents :: ColorComponents
83 lchComponents = ColorComponents unpack pack
84 where
85 toDeg,toRad :: Double -> Double
86 toRad deg = deg/180 * pi
87 toDeg rad = rad/pi * 180
88 unpack :: Colour Double -> (Double, Double, Double)
89 unpack color =
90 let (l,a,b) = cieLABView d65 color
91 c = sqrt (a*a + b*b)
92 h :: Double
93 h = (toDeg(atan2 b a) + 360) `mod'` 360
94 isZero = round (c*10000) == (0::Integer)
95 in (l, c, if isZero then 0/0 else h)
96 pack l c h =
97 cieLAB d65 l (cos (toRad h) * c) (sin (toRad h) * c)
98
99 -- | Smoothly interpolate between two colors using the given color components.
100 interpolate :: ColorComponents -> Colour Double -> Colour Double -> (Double -> Colour Double)
101 interpolate ColorComponents{..} from to = \d ->
102 colorPack (a1 + (a2-a1)*d) (b1 + (b2-b1)*d) (c1 + (c2-c1)*d)
103 where
104 (a1,b1,c1) = colorUnpack from
105 (a2,b2,c2) = colorUnpack to
106
107 -- | Convenience interpolation function for RGB8 values.
108 interpolateRGB8 :: ColorComponents -> PixelRGB8 -> PixelRGB8 -> (Double -> PixelRGB8)
109 interpolateRGB8 comps from to = toRGB8 . interpolate comps (fromRGB8 from) (fromRGB8 to)
110
111 -- | Convenience interpolation function for RGBA8 values.
112 interpolateRGBA8 :: ColorComponents -> PixelRGBA8 -> PixelRGBA8 -> (Double -> PixelRGBA8)
113 interpolateRGBA8 comps from to = \t ->
114 case interp t of
115 PixelRGB8 r g b ->
116 let alpha = fromToS (fromIntegral $ pixelOpacity from) (fromIntegral $ pixelOpacity to) t
117 in PixelRGBA8 r g b (round alpha)
118 where
119 interp = interpolateRGB8 comps (dropTransparency from) (dropTransparency to)
120
121 -- | Convenience function for expressing a color as an RGB8 value.
122 toRGB8 :: Colour Double -> PixelRGB8
123 toRGB8 c = PixelRGB8 r g b
124 where
125 RGB r g b = toSRGBBounded c
126
127 -- | Convenience function for expressing an RGB8 value as a color.
128 fromRGB8 :: PixelRGB8 -> Colour Double
129 fromRGB8 (PixelRGB8 r g b) = sRGB24 r g b