hakyll-images-1.3.0: library/Codec/Picture/Saving/WithMetadata.hs
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
-- | Helper functions to save dynamic images to other file format,
-- while preserving metadata when possible.
--
-- This module is modified from `Codec.Picture.Saving` from JuicyPixels
module Codec.Picture.Saving.WithMetadata
( imageToJpgWithMetadata,
imageToPngWithMetadata,
imageToBitmapWithMetadata,
)
where
import Codec.Picture.Bitmap (encodeBitmapWithMetadata)
import Codec.Picture.Jpg (encodeDirectJpegAtQualityWithMetadata, encodeJpegAtQualityWithMetadata)
import Codec.Picture.Metadata (Metadatas)
import Codec.Picture.Png (PngSavable (encodePngWithMetadata))
import Codec.Picture.Types
( ColorConvertible (promoteImage),
ColorSpaceConvertible (convertImage),
DynamicImage (..),
Image (..),
Pixel (PixelBaseComponent),
Pixel8,
PixelF,
PixelRGB16,
PixelRGB8 (..),
PixelRGBA8,
PixelRGBF (..),
dropAlphaLayer,
pixelMap,
)
import Data.Bits (unsafeShiftR)
import qualified Data.ByteString.Lazy as L
import qualified Data.Vector.Storable as V
import Data.Word (Word16, Word32, Word8)
componentToLDR :: Float -> Word8
componentToLDR = truncate . (255 *) . min 1.0 . max 0.0
toStandardDef :: Image PixelRGBF -> Image PixelRGB8
toStandardDef = pixelMap pixelConverter
where
pixelConverter (PixelRGBF rf gf bf) = PixelRGB8 r g b
where
r = componentToLDR rf
g = componentToLDR gf
b = componentToLDR bf
greyScaleToStandardDef :: Image PixelF -> Image Pixel8
greyScaleToStandardDef = pixelMap componentToLDR
from16to8 ::
( PixelBaseComponent source ~ Word16,
PixelBaseComponent dest ~ Word8
) =>
Image source ->
Image dest
from16to8
Image
{ imageWidth = w,
imageHeight = h,
imageData = arr
} = Image w h transformed
where
transformed = V.map toWord8 arr
toWord8 v = fromIntegral (v `unsafeShiftR` 8)
from32to8 ::
( PixelBaseComponent source ~ Word32,
PixelBaseComponent dest ~ Word8
) =>
Image source ->
Image dest
from32to8
Image
{ imageWidth = w,
imageHeight = h,
imageData = arr
} = Image w h transformed
where
transformed = V.map toWord8 arr
toWord8 v = fromIntegral (v `unsafeShiftR` 24)
from32to16 ::
( PixelBaseComponent source ~ Word32,
PixelBaseComponent dest ~ Word16
) =>
Image source ->
Image dest
from32to16
Image
{ imageWidth = w,
imageHeight = h,
imageData = arr
} = Image w h transformed
where
transformed = V.map toWord16 arr
toWord16 v = fromIntegral (v `unsafeShiftR` 16)
-- | This function will try to do anything to encode an image
-- as JPEG, make all color conversion and such. Equivalent
-- of 'decodeImage' for jpeg encoding
-- Save Y or YCbCr Jpeg only, all other colorspaces are converted.
-- To save a RGB or CMYK JPEG file, use the
-- 'Codec.Picture.Jpg.Internal.encodeDirectJpegAtQualityWithMetadata' function
imageToJpgWithMetadata :: Int -> Metadatas -> DynamicImage -> L.ByteString
imageToJpgWithMetadata quality meta dynImage =
let encodeAtQuality = encodeJpegAtQualityWithMetadata (fromIntegral quality) meta
encodeWithMeta = encodeDirectJpegAtQualityWithMetadata (fromIntegral quality) meta
in case dynImage of
ImageYCbCr8 img -> encodeAtQuality img
ImageCMYK8 img -> imageToJpgWithMetadata quality meta . ImageRGB8 $ convertImage img
ImageCMYK16 img -> imageToJpgWithMetadata quality meta . ImageRGB16 $ convertImage img
ImageRGB8 img -> encodeAtQuality (convertImage img)
ImageRGBF img -> imageToJpgWithMetadata quality meta . ImageRGB8 $ toStandardDef img
ImageRGBA8 img -> encodeAtQuality (convertImage $ dropAlphaLayer img)
ImageYF img -> imageToJpgWithMetadata quality meta . ImageY8 $ greyScaleToStandardDef img
ImageY8 img -> encodeWithMeta img
ImageYA8 img -> encodeWithMeta $ dropAlphaLayer img
ImageY16 img -> imageToJpgWithMetadata quality meta . ImageY8 $ from16to8 img
ImageYA16 img -> imageToJpgWithMetadata quality meta . ImageYA8 $ from16to8 img
ImageY32 img -> imageToJpgWithMetadata quality meta . ImageY8 $ from32to8 img
ImageRGB16 img -> imageToJpgWithMetadata quality meta . ImageRGB8 $ from16to8 img
ImageRGBA16 img -> imageToJpgWithMetadata quality meta . ImageRGBA8 $ from16to8 img
-- | This function will try to do anything to encode an image
-- as PNG, make all color conversion and such. Equivalent
-- of 'decodeImage' for PNG encoding
imageToPngWithMetadata :: Metadatas -> DynamicImage -> L.ByteString
imageToPngWithMetadata meta (ImageYCbCr8 img) = encodePngWithMetadata meta (convertImage img :: Image PixelRGB8)
imageToPngWithMetadata meta (ImageCMYK8 img) = encodePngWithMetadata meta (convertImage img :: Image PixelRGB8)
imageToPngWithMetadata meta (ImageCMYK16 img) = encodePngWithMetadata meta (convertImage img :: Image PixelRGB16)
imageToPngWithMetadata meta (ImageRGB8 img) = encodePngWithMetadata meta img
imageToPngWithMetadata meta (ImageRGBF img) = encodePngWithMetadata meta $ toStandardDef img
imageToPngWithMetadata meta (ImageRGBA8 img) = encodePngWithMetadata meta img
imageToPngWithMetadata meta (ImageY8 img) = encodePngWithMetadata meta img
imageToPngWithMetadata meta (ImageYF img) = encodePngWithMetadata meta $ greyScaleToStandardDef img
imageToPngWithMetadata meta (ImageYA8 img) = encodePngWithMetadata meta img
imageToPngWithMetadata meta (ImageY16 img) = encodePngWithMetadata meta img
imageToPngWithMetadata meta (ImageY32 img) = imageToPngWithMetadata meta . ImageY16 $ from32to16 img
imageToPngWithMetadata meta (ImageYA16 img) = encodePngWithMetadata meta img
imageToPngWithMetadata meta (ImageRGB16 img) = encodePngWithMetadata meta img
imageToPngWithMetadata meta (ImageRGBA16 img) = encodePngWithMetadata meta img
-- | This function will try to do anything to encode an image
-- as bitmap, make all color conversion and such. Equivalent
-- of 'decodeImage' for Bitmap encoding
imageToBitmapWithMetadata :: Metadatas -> DynamicImage -> L.ByteString
imageToBitmapWithMetadata meta (ImageYCbCr8 img) = encodeBitmapWithMetadata meta (convertImage img :: Image PixelRGB8)
imageToBitmapWithMetadata meta (ImageCMYK8 img) = encodeBitmapWithMetadata meta (convertImage img :: Image PixelRGB8)
imageToBitmapWithMetadata meta (ImageCMYK16 img) = imageToBitmapWithMetadata meta . ImageRGB16 $ convertImage img
imageToBitmapWithMetadata meta (ImageRGBF img) = encodeBitmapWithMetadata meta $ toStandardDef img
imageToBitmapWithMetadata meta (ImageRGB8 img) = encodeBitmapWithMetadata meta img
imageToBitmapWithMetadata meta (ImageRGBA8 img) = encodeBitmapWithMetadata meta img
imageToBitmapWithMetadata meta (ImageY8 img) = encodeBitmapWithMetadata meta img
imageToBitmapWithMetadata meta (ImageYF img) = encodeBitmapWithMetadata meta $ greyScaleToStandardDef img
imageToBitmapWithMetadata meta (ImageYA8 img) = encodeBitmapWithMetadata meta (promoteImage img :: Image PixelRGBA8)
imageToBitmapWithMetadata meta (ImageY16 img) = imageToBitmapWithMetadata meta . ImageY8 $ from16to8 img
imageToBitmapWithMetadata meta (ImageY32 img) = imageToBitmapWithMetadata meta . ImageY8 $ from32to8 img
imageToBitmapWithMetadata meta (ImageYA16 img) = imageToBitmapWithMetadata meta . ImageYA8 $ from16to8 img
imageToBitmapWithMetadata meta (ImageRGB16 img) = imageToBitmapWithMetadata meta . ImageRGB8 $ from16to8 img
imageToBitmapWithMetadata meta (ImageRGBA16 img) = imageToBitmapWithMetadata meta . ImageRGBA8 $ from16to8 img