-- |
-- Module: Codec.Picture.Blurhash.Internal.Common
-- Copyright: (c) 2020 Sam Protas
-- License: BSD3
-- Maintainer: Sam Protas <sam.protas@gmail.com>
-- Stability: experimental
-- Portability: portable
--
-- Internal Blurhash helper functions shared by encoding and decoding.
--
-- __Note__: This is an internal module not subject to PVP adherence.
module Codec.Picture.Blurhash.Internal.Common where

import Codec.Picture (Image, PixelRGB8(..), PixelRGBF(..), pixelMap)

-- | Helper function specified by other Blurhash implementations
signPow :: Float -> Float -> Float
signPow v ex = fixSign $ (abs v) ** ex
  where
    fixSign = if v < 0 then ((-1)*) else id

-- | Clamps it's 3rd argument between the min and max specified by the 1st and 2nd arguments
--   respectively
clamp :: Ord a => a -> a -> a -> a
clamp minVal maxVal = min maxVal . max minVal

-- | Convert an RGB8 Image to an RGBF Image using the color conversion specified by Blurhash
sRGBImageToLinear :: Image PixelRGB8 -> Image PixelRGBF
sRGBImageToLinear = pixelMap pixelToLinear

-- | Convert an RGB8 Pixel to an RGBF Pixel using the color conversion specified by Blurhash
pixelToLinear :: PixelRGB8 -> PixelRGBF
pixelToLinear (PixelRGB8 r g b) = PixelRGBF (toLinear r) (toLinear g) (toLinear b)
  where
    toLinear c = let v = realToFrac c / 255
                 in if v < 0.04045
                    then v / 12.92
                    else ((v + 0.055) / 1.055) ** 2.4

-- | Convert an RGBF Image to an RGB8 Image using the color conversion specified by Blurhash
linearImageToSRGB :: Image PixelRGBF -> Image PixelRGB8
linearImageToSRGB = pixelMap linearPixelToSRGB

-- | Convert an RGBF Pixel to an RGB8 Pixel using the color conversion specified by Blurhash
linearPixelToSRGB :: PixelRGBF -> PixelRGB8
linearPixelToSRGB (PixelRGBF r g b) = PixelRGB8 (linearToSRGB r) (linearToSRGB g) (linearToSRGB b)

-- | Convert an RGBF color to an RGB8 color using the color conversion specified by Blurhash
linearToSRGB :: (RealFrac a, Integral b, Floating a) => a -> b
linearToSRGB p =
  floor $ if v < 0.0031308
          then v * 12.92 * 255 + 0.5
          else (1.055 * (v ** (1 / 2.4)) - 0.055) * 255 + 0.5
  where
    v = clamp 0 1 p