diff --git a/Color/tests/Graphics/Color/Model/YCbCrSpec.hs b/Color/tests/Graphics/Color/Model/YCbCrSpec.hs new file mode 100644 index 0000000..e0803bc --- /dev/null +++ b/Color/tests/Graphics/Color/Model/YCbCrSpec.hs @@ -0,0 +1,30 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +module Graphics.Color.Model.YCbCrSpec (spec) where + +import Graphics.Color.Model +import Graphics.Color.Model.Common +import Graphics.Color.Model.RGBSpec () +import qualified Codec.Picture.Types as JuicyPixels + +instance (Elevator e, Random e) => Arbitrary (Color YCbCr e) where + arbitrary = + ColorYCbCr <$> arbitraryElevator <*> arbitraryElevator <*> arbitraryElevator + +spec :: Spec +spec = + describe "YCbCr" $ do + colorModelSpec @YCbCr @Word "YCbCr" + -- prop "rgb2ycbcr . ycbcr2rgb" $ \(rgb :: Color RGB Double) -> + -- rgb `epsilonEqColor` ycbcr2rgb (rgb2ycbcr rgb) + -- describe "Match JuicyPixels" $ do + -- prop "rgb2ycbcr" $ \(rgb@(ColorRGB r g b) :: Color RGB Word8) -> + -- case JuicyPixels.convertPixel (JuicyPixels.PixelRGB8 r g b) of + -- JuicyPixels.PixelYCbCr8 y cb cr -> + -- toWord8 <$> rgb2ycbcr (toFloat <$> rgb) `approxIntegralColorExpect1` ColorYCbCr y cb cr + -- prop "ycbcr2rgb" $ \(ycbcr@(ColorYCbCr y cb cr) :: Color YCbCr Word8) -> + -- case JuicyPixels.convertPixel (JuicyPixels.PixelYCbCr8 y cb cr) of + -- JuicyPixels.PixelRGB8 r g b -> + -- toWord8 <$> ycbcr2rgb (toFloat <$> ycbcr) `approxIntegralColorExpect1` ColorRGB r g b diff --git a/Color/tests/Graphics/Color/Space/Common.hs b/Color/tests/Graphics/Color/Space/Common.hs index 0ec84c7..649ff14 100644 --- a/Color/tests/Graphics/Color/Space/Common.hs +++ b/Color/tests/Graphics/Color/Space/Common.hs @@ -9,7 +9,9 @@ module Graphics.Color.Space.Common , module Graphics.Color.Model.Common , colorSpaceSpec , colorSpaceLenientSpec + , colorSpaceCommonSpec , prop_toFromColorXYZ + , prop_LuminanceColorXYZ , prop_toFromLenientColorXYZ , prop_toFromBaseSpace ) where @@ -40,6 +42,10 @@ prop_toFromLenientColorXYZ :: prop_toFromLenientColorXYZ epsilon c = epsilonEqColorTol epsilon c (fromColorXYZ (toColorXYZ c :: Color (XYZ i) Double)) +prop_LuminanceColorXYZ :: forall cs i e . ColorSpace cs i e => Color cs e -> Property +prop_LuminanceColorXYZ c = + (luminance c :: Color (Y i) Float) `epsilonEqColor` + luminance (toColorXYZ c :: Color (XYZ i) Float) prop_toFromBaseSpace :: forall cs i e. (ColorSpace cs i e, ColorSpace (BaseSpace cs) i e, RealFloat e) @@ -47,6 +53,14 @@ prop_toFromBaseSpace :: -> Property prop_toFromBaseSpace c = c `epsilonEqColor` fromBaseSpace (toBaseSpace c) +prop_toFromBaseSpaceLenient :: + forall cs i e. (ColorSpace cs i e, ColorSpace (BaseSpace cs) i e, RealFloat e) + => e + -> Color cs e + -> Property +prop_toFromBaseSpaceLenient epsilon c = epsilonEqColorTol epsilon c $ fromBaseSpace (toBaseSpace c) + + prop_toFromBaseModel :: forall cs i e. ColorSpace cs i e => Color cs e @@ -55,15 +69,12 @@ prop_toFromBaseModel c = c === fromBaseModel (toBaseModel c) colorSpaceCommonSpec :: forall cs i e. - (Arbitrary (Color cs e), ColorSpace (BaseSpace cs) i e, ColorSpace cs i e, RealFloat e) + (Arbitrary (Color cs e), ColorSpace cs i e) => Spec -> Spec -colorSpaceCommonSpec extra = do +colorSpaceCommonSpec extra = describe "ColorSpace" $ do - prop "luminance . toColorXYZ" $ \(c :: Color cs e) -> - (luminance c :: Color (Y i) Float) `epsilonEqColor` - luminance (toColorXYZ c :: Color (XYZ i) Float) + prop "luminance . toColorXYZ" $ prop_LuminanceColorXYZ @cs @i @e prop "toFromBaseModel" $ prop_toFromBaseModel @cs @i @e - prop "toFromBaseSpace" $ prop_toFromBaseSpace @cs @i @e extra colorSpaceSpec :: @@ -71,7 +82,8 @@ colorSpaceSpec :: (Arbitrary (Color cs e), ColorSpace (BaseSpace cs) i e, ColorSpace cs i e, RealFloat e) => Spec colorSpaceSpec = - colorSpaceCommonSpec @cs @i @e $ + colorSpaceCommonSpec @cs @i @e $ do + prop "toFromBaseSpace" $ prop_toFromBaseSpace @cs @i @e prop "toFromColorXYZ" $ prop_toFromColorXYZ @cs @i @e colorSpaceLenientSpec :: @@ -81,5 +93,6 @@ colorSpaceLenientSpec :: -> Spec colorSpaceLenientSpec tol = let tolStr = "(lenient=" ++ show tol ++ ")" - in colorSpaceCommonSpec @cs @i @e $ + in colorSpaceCommonSpec @cs @i @e $ do + prop ("toFromBaseSpace " ++ tolStr) $ prop_toFromBaseSpaceLenient @cs @i @e tol prop ("toFromColorXYZ " ++ tolStr) $ prop_toFromLenientColorXYZ @cs @i @e tol diff --git a/Color/tests/Graphics/Color/Space/RGB/Alternative/YCbCrSpec.hs b/Color/tests/Graphics/Color/Space/RGB/Alternative/YCbCrSpec.hs new file mode 100644 index 0000000..822f2b0 --- /dev/null +++ b/Color/tests/Graphics/Color/Space/RGB/Alternative/YCbCrSpec.hs @@ -0,0 +1,52 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +module Graphics.Color.Space.RGB.Alternative.YCbCrSpec (spec) where + +import qualified Codec.Picture.Types as JuicyPixels +import Graphics.Color.Space.Common +import Graphics.Color.Space.RGB.SRGB +import qualified Graphics.Color.Space.RGB.Derived.SRGB as Derived +import Graphics.Color.Space.RGB.Alternative.YCbCr + +instance (Elevator e, Random e) => Arbitrary (Color SRGB e) where + arbitrary = + ColorRGB <$> arbitraryElevator <*> arbitraryElevator <*> arbitraryElevator + +instance (Illuminant i, Elevator e, Random e) => Arbitrary (Color (Derived.SRGB i) e) where + arbitrary = + ColorRGB <$> arbitraryElevator <*> arbitraryElevator <*> arbitraryElevator + + +instance (Elevator e, Random e) => Arbitrary (Color (YCbCr cs) e) where + arbitrary = + ColorYCbCr <$> arbitraryElevator <*> arbitraryElevator <*> arbitraryElevator + +spec :: Spec +spec = + describe "YCbCr" $ do + describe "sRGB" $ do + colorModelSpec @(YCbCr SRGB) @Word "YCbCr" + colorSpaceCommonSpec @(YCbCr SRGB) @_ @Double $ pure () + describe "Derived-sRGB" $ do + colorModelSpec @(YCbCr (Derived.SRGB D65)) @Word "YCbCr" + colorSpaceCommonSpec @(YCbCr (Derived.SRGB D65)) @_ @Double $ pure () + prop "toColorYCbCr . toColorYCbCr" $ \(rgb :: Color (Derived.SRGB D65) Double) -> + rgb `epsilonEqColor` + fromColorYCbCr (toColorYCbCr rgb :: Color (YCbCr (Derived.SRGB D65)) Double) + prop "toColorYCbCr == srgb2ycbcr" $ \(rgb :: Color SRGB Double) -> + srgb2ycbcr rgb `epsilonEqColor` + (toColorYCbCr rgb :: Color (YCbCr SRGB) Double) + prop "fromColorYCbCr == ycbcr2srgb" $ \(ycbcr :: Color (YCbCr SRGB) Double) -> + ycbcr2srgb ycbcr `epsilonEqColor` (fromColorYCbCr ycbcr :: Color SRGB Double) + describe "Match JuicyPixels" $ do + prop "rgb2ycbcr" $ \(rgb@(ColorRGB r g b) :: Color SRGB Word8) -> + case JuicyPixels.convertPixel (JuicyPixels.PixelRGB8 r g b) of + JuicyPixels.PixelYCbCr8 y cb cr -> + (fromBaseSpace rgb :: Color (YCbCr SRGB) Word8) + `approxIntegralColorExpect1` ColorYCbCr y cb cr + prop "ycbcr2rgb" $ \(ycbcr@(ColorYCbCr y cb cr) :: Color (YCbCr SRGB) Word8) -> + case JuicyPixels.convertPixel (JuicyPixels.PixelYCbCr8 y cb cr) of + JuicyPixels.PixelRGB8 r g b -> + (toBaseSpace ycbcr) `approxIntegralColorExpect1` ColorRGB r g b