packages feed

ktx-codec (empty) → 0.0.1.0

raw patch · 7 files changed

+395/−0 lines, 7 filesdep +basedep +binarydep +bytestringsetup-changed

Dependencies added: base, binary, bytestring, containers, directory, filepath, ktx-codec, shower, text, vector

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for ktx++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Alexander Bondarenko (c) 2020++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Alexander Bondarenko nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,1 @@+# ktx
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ ktx-codec.cabal view
@@ -0,0 +1,69 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.33.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: fefddec4091fd40932865a839c649f4c8d82ecce52ef3f70f527a25c3d04c1d5++name:           ktx-codec+version:        0.0.1.0+synopsis:       Khronos texture format+description:    KTX is a format for storing textures for OpenGL® and OpenGL® ES applications.+                It is distinguished by the simplicity of the loader required to instantiate a+                GL texture object from the file contents.+                .+                https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/+category:       Graphics+author:         Alexander Bondarenko+maintainer:     aenor.realm@gmail.com+copyright:      2020 Alexander Bondarenko+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://gitlab.com/dpwiz/ktx++library+  exposed-modules:+      Codec.Ktx+  other-modules:+      Paths_ktx_codec+  hs-source-dirs:+      src+  default-extensions: ApplicativeDo BlockArguments DeriveGeneric DuplicateRecordFields FlexibleContexts GeneralizedNewtypeDeriving LambdaCase OverloadedStrings PatternSynonyms RecordWildCards StrictData+  build-depends:+      base >=4.7 && <5+    , binary >=0.8.7 && <1+    , bytestring >=0.10 && <0.11+    , containers >=0.6 && <0.7+    , text >=1.2 && <1.3+    , vector >=0.12 && <0.13+  default-language: Haskell2010++test-suite ktx-codec-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_ktx_codec+  hs-source-dirs:+      test+  default-extensions: ApplicativeDo BlockArguments DeriveGeneric DuplicateRecordFields FlexibleContexts GeneralizedNewtypeDeriving LambdaCase OverloadedStrings PatternSynonyms RecordWildCards StrictData+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , binary >=0.8.7 && <1+    , bytestring >=0.10 && <0.11+    , containers >=0.6 && <0.7+    , directory >=1.3 && <1.4+    , filepath >=1.4 && <1.5+    , ktx-codec+    , shower >=0.2 && <0.3+    , text >=1.2 && <1.3+    , vector >=0.12 && <0.13+  default-language: Haskell2010
+ src/Codec/Ktx.hs view
@@ -0,0 +1,254 @@+module Codec.Ktx where++import Debug.Trace++import Data.Binary (Binary(..), decodeFileOrFail)+import Data.Binary.Get (Get, ByteOffset, getWord32le, getWord32be, getByteString, skip)+import Data.Binary.Put (Put, putByteString, putWord32le)+import Data.ByteString (ByteString)+import Data.Coerce (coerce)+import Data.Foldable (for_)+import Data.Map.Strict (Map)+import Data.Text (Text)+import Data.Vector (Vector)+import Data.Word (Word32)+import GHC.Generics (Generic)++import qualified Data.Text.Encoding as Text+import qualified Data.Map.Strict as Map+import qualified Data.Vector as Vector+import qualified Data.ByteString as BS++fromFile :: FilePath -> IO (Either (ByteOffset, String) Ktx)+fromFile = decodeFileOrFail++data Ktx = Ktx+  { header :: Header+  , kvs    :: KeyValueData+  , images :: MipLevels+  } deriving (Show, Generic)++instance Binary Ktx where+  get = do+    header <- get+    kvs <- getKeyValueData header+    images <- getImages header+    pure Ktx{..}++  put Ktx{..} = do+    put header+    putKeyValueData kvs+    putImages images++-- * Header++data Header = Header+  { identifier            :: ByteString+  , endianness            :: Word32+  , glType                :: Word32+  , glTypeSize            :: Word32+  , glFormat              :: Word32+  , glInternalFormat      :: Word32+  , glBaseInternalFormat  :: Word32+  , pixelWidth            :: Word32+  , pixelHeight           :: Word32+  , pixelDepth            :: Word32+  , numberOfArrayElements :: Word32+  , numberOfFaces         :: Word32+  , numberOfMipmapLevels  :: Word32+  , bytesOfKeyValueData   :: Word32+  } deriving (Show, Generic)++instance Binary Header where+  get = do+    identifier <- getByteString 12+    if identifier == canonicalIdentifier then+      pure ()+    else+      fail $ "KTX identifier mismatch: " <> show identifier++    endianness <- getWord32le+    let+      getNext =+        if endianness == endiannessLE then+          getWord32le+        else+          getWord32be++    glType                <- getNext+    glTypeSize            <- getNext+    glFormat              <- getNext+    glInternalFormat      <- getNext+    glBaseInternalFormat  <- getNext+    pixelWidth            <- getNext+    pixelHeight           <- getNext+    pixelDepth            <- getNext+    numberOfArrayElements <- getNext+    numberOfFaces         <- getNext+    numberOfMipmapLevels  <- getNext+    bytesOfKeyValueData   <- getNext++    pure Header{..}++  put Header{..} = do+    putByteString identifier+    putWord32le endianness+    putWord32le glType+    putWord32le glTypeSize+    putWord32le glFormat+    putWord32le glInternalFormat+    putWord32le glBaseInternalFormat+    putWord32le pixelWidth+    putWord32le pixelHeight+    putWord32le pixelDepth+    putWord32le numberOfArrayElements+    putWord32le numberOfFaces+    putWord32le numberOfMipmapLevels+    putWord32le bytesOfKeyValueData++endiannessLE :: Word32+endiannessLE = 0x04030201++canonicalIdentifier :: ByteString+canonicalIdentifier = BS.pack+  [ 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB -- «KTX 11»+  , 0x0D, 0x0A, 0x1A, 0x0A                         -- \r\n\x1A\n+  ]++-- * Key-value data++type KeyValueData = Map Key Value++newtype Key = Key Text+  deriving (Eq, Ord, Show, Generic)++newtype Value = Value ByteString+  deriving (Show, Generic)++getKeyValueData :: Header -> Get KeyValueData+getKeyValueData Header{..} = Map.fromList <$> go bytesOfKeyValueData []+  where+    go remains acc+      | remains == 0 =+          pure acc++      | remains < 0 =+          fail ""++      | otherwise = do+          keyAndValueByteSize <- getSize+          keyAndValue <- getByteString (fromIntegral keyAndValueByteSize)+          _valuePadding <- skip . fromIntegral $ 3 - ((keyAndValueByteSize + 3) `rem` 4)++          let (key, value) = BS.span (/= 0x00) keyAndValue+          go (remains - keyAndValueByteSize) $+            ( Key $ Text.decodeUtf8 key+            , Value value+            ) : acc++    getSize =+      if endianness == endiannessLE then+        getWord32le+      else+        getWord32be++putKeyValueData :: Map Key Value -> Put+putKeyValueData kvs =+  for_ (Map.toList kvs) \(Key key, Value value) -> do+    let keyAndValue = Text.encodeUtf8 key <> BS.singleton 0x00 <> value+    putWord32le (fromIntegral $ BS.length keyAndValue)+    putByteString keyAndValue++-- * Images++type MipLevels = Vector MipLevel++data MipLevel = MipLevel+  { imageSize     :: Word32+  , arrayElements :: Vector ArrayElement+  }+  deriving (Show, Generic)++newtype ArrayElement = ArrayElement+  { faces :: Vector Face+  }+  deriving (Show, Generic)++newtype Face = Face+  { zSlices :: Vector ZSlice+  }+  deriving (Show, Generic)++newtype ZSlice = ZSlice+  { block :: ByteString+  }+  deriving (Generic)++instance Show ZSlice where+  show ZSlice{..} =+    let+      size = BS.length block+    in+      mconcat+        [ "ZSlice ("+        , show size+        , ") "+        , show (BS.take 32 block)+        ]++getImages :: Header -> Get MipLevels+getImages Header{..} = do+  traceM $ "numberOfMipmapLevels: " <> show numberOfMipmapLevels'+  some_ numberOfMipmapLevels' do+    imageSize <- getImageSize++    let+      sliceSize = fromIntegral $+        if numberOfFaces == 6 then+          imageSize+        else+          imageSize+            `div` numberOfArrayElements'+            `div` numberOfFaces+            `div` pixelDepth'+    traceM $ "  sliceSize: " <> show sliceSize+    traceM $ "  numberOfArrayElements: " <> show numberOfArrayElements++    elements <- some_ numberOfArrayElements' $+      some_ numberOfFaces $+        some_ pixelDepth' $+          ZSlice <$> getByteString sliceSize++    pure MipLevel+      { imageSize     = imageSize+      , arrayElements = coerce elements+      }++  where+    some_ n action = Vector.forM (Vector.fromList [1..n]) \_ix -> action++    numberOfMipmapLevels'+      | numberOfMipmapLevels == 0 = 1+      | otherwise                 = numberOfMipmapLevels++    numberOfArrayElements'+      | numberOfArrayElements == 0 = 1+      | otherwise                  = numberOfArrayElements++    pixelDepth'+      | pixelDepth == 0 = 1+      | otherwise       = pixelDepth++    getImageSize =+      if endianness == endiannessLE then+        getWord32le+      else+        getWord32be++putImages :: MipLevels -> Put+putImages mipLevels = Vector.forM_ mipLevels \MipLevel{..} -> do+  put imageSize+  Vector.forM_ arrayElements \ArrayElement{..} ->+    Vector.forM_ faces \Face{..} ->+      Vector.forM_ zSlices \ZSlice{..} ->+        putByteString block
+ test/Spec.hs view
@@ -0,0 +1,36 @@+module Main where++import Control.Monad (guard)+import Data.List (isSuffixOf)+import Debug.Trace (traceM)+import Shower (printer)+import System.Directory (listDirectory)+import System.FilePath ((</>))++import qualified Codec.Ktx as Ktx1++assetsPath :: FilePath+assetsPath = ".." </> "assets"++main :: IO ()+main = do+  assets <- listDirectory assetsPath++  mapM_ testKtx1 do+    fp <- assets+    guard $ ".ktx" `isSuffixOf` fp+    pure $ assetsPath </> fp++testKtx1 :: FilePath -> IO ()+testKtx1 source =+  Ktx1.fromFile source >>= \case+    Left (offset, err) -> do+      traceM err+      fail $ unwords+        [ "KTX-1 load error in"+        , source+        , "at"+        , show offset+        ]+    Right ktx -> do+      printer ktx