packages feed

JuicyPixels-blp-0.4.0.0: src/Codec/Picture/Blp/Internal/Data.hs

{-|
The structure of a BLP1 file as the parser reads it and the encoder writes
it. See @docs/format.md@ in the source repository for the full format.

This is an internal module: its API may change between minor versions.
-}
module Codec.Picture.Blp.Internal.Data(
    BlpStruct(..)
  , blpCompression
  , BlpCompression(..)
  , BlpExt(..)
  , BlpFormat(..)
  , BlpEncodeOptions(..)
  , BlpEncodeError(..)
  ) where

import Codec.Picture
import Data.ByteString (ByteString)
import Data.Vector (Vector)
import Data.Word
import GHC.Generics

-- | A parsed BLP1 file: the 156 byte header and the image data after it. The
-- content type (header dword @0x04@) follows from 'blpExt', see
-- 'blpCompression'.
data BlpStruct = BlpStruct {
  -- | Header dword @0x08@: alpha bits per pixel. 0, 1, 4 and 8 are valid for
  -- paletted content, 0 and 8 for JPEG; any other value means no alpha.
  blpAlphaBits :: !Word32
  -- | Width of mip 0 in pixels.
, blpWidth :: !Word32
  -- | Height of mip 0 in pixels.
, blpHeight :: !Word32
  -- | Header dword @0x14@. It has no known effect and is kept as read.
, blpExtra :: !Word32
  -- | Header dword @0x18@: zero means only mip 0 is present.
, blpHasMipmaps :: !Word32
  -- | Everything after the header, including the mip levels.
, blpExt :: BlpExt
} deriving (Show, Generic)

-- | The part of the file after the header.
data BlpExt =
    -- | JPEG content. Each mip level is @blpJpegHeader <> level@, a
    -- four-component JPEG whose components are B, G, R and A.
    BlpJpeg {
      blpJpegHeader :: !ByteString -- ^ Shared JPEG header, at most 624 bytes
    , blpJpegData :: ![ByteString] -- ^ Rest of each mip level, mip 0 first
    }
    -- | Paletted content. Each mip level holds the index list followed by the
    -- alpha list, packed at 'blpAlphaBits' bits per pixel; the level's size
    -- tells where one ends and the other begins.
  | BlpPaletted {
      blpPalette :: !(Vector PixelRGBA8) -- ^ 256 entries stored as B, G, R and padding
    , blpPalettedMipMaps :: ![ByteString] -- ^ Raw data of each mip level, mip 0 first
    }
  deriving (Show, Generic)

-- | Header dword @0x04@.
data BlpCompression =
    BlpCompressionJPEG -- ^ 0: JPEG
  | BlpCompressionUncompressed -- ^ 1: 256 colour palette
  deriving (Eq, Ord, Enum, Bounded, Show, Generic)

-- | Header dword @0x04@ (@content@ in the specification) of a file.
blpCompression :: BlpStruct -> BlpCompression
blpCompression blp = case blpExt blp of
  BlpJpeg {} -> BlpCompressionJPEG
  BlpPaletted {} -> BlpCompressionUncompressed

-- | How to encode a texture.
data BlpFormat =
    -- | JPEG with the given quality, from 1 to 100. The texture gets alpha
    -- when the image has an alpha channel.
    BlpFormatJpeg !Int
    -- | 256 colour palette, with 8-bit alpha or without alpha. Colours are
    -- quantised with dithering.
  | BlpFormatPaletted !Bool
  deriving (Eq, Show)

-- | Options for encoding a texture.
data BlpEncodeOptions = BlpEncodeOptions {
  -- | Content type and its settings.
  blpFormat :: !BlpFormat
  -- | Number of mip levels to write, mip 0 included. Values below 1 mean 1,
  -- and values above what the image has mean the full chain down to 1x1
  -- (at most 16 levels).
, blpMipLevels :: !Int
} deriving (Eq, Show)

-- | Why an image cannot be encoded.
data BlpEncodeError =
    BlpEmptyImage -- ^ The image has a side of zero pixels.
  | BlpImageTooLarge !Int !Int -- ^ Width or height is above 65535, the format's limit.
  | BlpInvalidJpegQuality !Int -- ^ JPEG quality outside 1 to 100.
  | BlpFileTooLarge -- ^ The file would exceed 4 GiB, the most its offsets can address.
  deriving (Eq, Show)