packages feed

ktx-codec 0.0.2.1 → 0.1.0.0

raw patch · 7 files changed

+142/−48 lines, 7 filesdep ~basedep ~binarydep ~bytestringPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, binary, bytestring, containers, directory, filepath, shower, text, vector

API changes (from Hackage documentation)

- Codec.Ktx.KeyValue: instance Codec.Ktx.KeyValue.FromValue Data.ByteString.Internal.ByteString
- Codec.Ktx2.Read: instance Codec.Ktx2.Read.ReadChunk Data.ByteString.Internal.ByteString
- Codec.Ktx2.Read: instance Codec.Ktx2.Read.ReadLevel Data.ByteString.Internal.ByteString
- Codec.Ktx2.Read: instance GHC.Show.Show (Codec.Ktx2.Read.Context Data.ByteString.Internal.ByteString)
+ Codec.Ktx.KeyValue: insertBytes :: Text -> ByteString -> KeyValueData -> KeyValueData
+ Codec.Ktx.KeyValue: insertNumber :: (Num a, Show a) => Text -> a -> KeyValueData -> KeyValueData
+ Codec.Ktx.KeyValue: insertText :: Text -> Text -> KeyValueData -> KeyValueData
+ Codec.Ktx.KeyValue: instance Codec.Ktx.KeyValue.FromValue Data.ByteString.Internal.Type.ByteString
+ Codec.Ktx.KeyValue: instance Codec.Ktx.KeyValue.FromValue GHC.Num.Integer.Integer
+ Codec.Ktx.KeyValue: lookup :: FromValue a => Text -> KeyValueData -> Maybe a
+ Codec.Ktx.KeyValue: setWriterWith :: (Text -> Text) -> KeyValueData -> KeyValueData
+ Codec.Ktx.KeyValue: writerKtxCodec :: Value
+ Codec.Ktx.KeyValue: writerKtxCodecWith :: (Text -> Text) -> Value
+ Codec.Ktx2: Ktx2 :: Header -> Vector Block -> KeyValueData -> ByteString -> [(Maybe Word64, ByteString)] -> Ktx2
+ Codec.Ktx2: [dfdBlocks] :: Ktx2 -> Vector Block
+ Codec.Ktx2: [header] :: Ktx2 -> Header
+ Codec.Ktx2: [kvd] :: Ktx2 -> KeyValueData
+ Codec.Ktx2: [levels] :: Ktx2 -> [(Maybe Word64, ByteString)]
+ Codec.Ktx2: [sgd] :: Ktx2 -> ByteString
+ Codec.Ktx2: data Ktx2
+ Codec.Ktx2: instance GHC.Classes.Eq Codec.Ktx2.Ktx2
+ Codec.Ktx2: instance GHC.Generics.Generic Codec.Ktx2.Ktx2
+ Codec.Ktx2: instance GHC.Show.Show Codec.Ktx2.Ktx2
+ Codec.Ktx2.Read: fromBytes :: MonadIO io => ByteString -> io Ktx2
+ Codec.Ktx2.Read: fromFile :: MonadIO io => FilePath -> io Ktx2
+ Codec.Ktx2.Read: instance Codec.Ktx2.Read.ReadChunk Data.ByteString.Internal.Type.ByteString
+ Codec.Ktx2.Read: instance Codec.Ktx2.Read.ReadLevel Data.ByteString.Internal.Type.ByteString
+ Codec.Ktx2.Read: instance GHC.Show.Show (Codec.Ktx2.Read.Context Data.ByteString.Internal.Type.ByteString)
- Codec.Ktx2.Write: toChunks :: Header -> Vector Block -> KeyValueData -> ByteString -> [(Maybe Word64, ByteString)] -> ByteString
+ Codec.Ktx2.Write: toChunks :: Ktx2 -> ByteString
- Codec.Ktx2.Write: toFile :: MonadIO io => FilePath -> Header -> Vector Block -> KeyValueData -> ByteString -> [(Maybe Word64, ByteString)] -> io ()
+ Codec.Ktx2.Write: toFile :: MonadIO io => FilePath -> Ktx2 -> io ()

Files

ChangeLog.md view
@@ -1,5 +1,17 @@ # Changelog for ktx-codec +## [0.1.0.0] - 2025-12-11++Making editing KTX2 files simpler.++* Add `Codec.Ktx2` with the `Ktx2` type pulled from `Codec.Ktx2.Write` arguments.+* Changed `Codec.Ktx2.Write` functions to use that `Ktx2` instead. Breaking.+* Add `Codec.Ktx2.Read.fromFile` to read the whole file at once and put it into `Ktx2` envelope.+* Add `Codec.Ktx.KeyValue.writerKtxCodec`/`writerCodecWith`/`editWriter` so you can brag about using this library when writing.+* Add container-like functions for `Codec.Ktx.KeyValue`: `lookup`, `insertBytes`, `insertNumber`, and `insertText`.+  +[0.1.0.0]: https://gitlab.com/dpwiz/ktx/-/tree/v0.1.0.0-codec+ ## [0.0.2.1] - 2023-04-08  * Add `Codec.Ktx2.Write`.
ktx-codec.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.1.+-- This file has been generated from package.yaml by hpack version 0.38.1. -- -- see: https://github.com/sol/hpack  name:           ktx-codec-version:        0.0.2.1+version:        0.1.0.0 synopsis:       Khronos texture format description:    This package implements low-level encoding and decoding for .ktx and .ktx2 files.                 .@@ -18,7 +18,7 @@ category:       Graphics author:         IC Rainbow maintainer:     aenor.realm@gmail.com-copyright:      2023 IC Rainbow+copyright:      2025 IC Rainbow license:        BSD3 license-file:   LICENSE build-type:     Simple@@ -37,6 +37,7 @@   exposed-modules:       Codec.Ktx       Codec.Ktx.KeyValue+      Codec.Ktx2       Codec.Ktx2.DFD       Codec.Ktx2.DFD.Khronos.BasicV2       Codec.Ktx2.Header@@ -65,10 +66,10 @@   build-depends:       base >=4.7 && <5     , binary >=0.8.7 && <1-    , bytestring >=0.10 && <0.12-    , containers ==0.6.*-    , text >=1.2 && <2.1-    , vector ==0.12.*+    , bytestring >=0.10 && <0.14+    , containers >=0.6 && <1+    , text >=1.2 && <3+    , vector >=0.12 && <0.14   default-language: Haskell2010  test-suite ktx-codec-test@@ -96,16 +97,16 @@   build-depends:       base >=4.7 && <5     , binary >=0.8.7 && <1-    , bytestring >=0.10 && <0.12-    , containers ==0.6.*-    , directory ==1.3.*-    , filepath ==1.4.*+    , bytestring >=0.10 && <0.14+    , containers >=0.6 && <1+    , directory >=1.3 && <2+    , filepath >=1.4 && <2     , ktx-codec-    , shower ==0.2.*+    , shower >=0.2 && <1     , tasty     , tasty-hunit-    , text >=1.2 && <2.1-    , vector ==0.12.*+    , text >=1.2 && <3+    , vector >=0.12 && <0.14     , zstd   default-language: Haskell2010   if !flag(tests)
src/Codec/Ktx/KeyValue.hs view
@@ -1,12 +1,21 @@ module Codec.Ktx.KeyValue   ( KeyValueData+  , lookup+  , insertBytes+  , insertNumber+  , insertText      -- * Predefined keys++    -- $predefined   , pattern KTXcubemapIncomplete   , pattern KTXanimData   , pattern KTXastcDecodeMode   , pattern KTXwriterScParams   , pattern KTXwriter+  , setWriterWith+  , writerKtxCodecWith+  , writerKtxCodec   , pattern KTXswizzle   , pattern KTXmetalPixelFormat   , pattern KTXdxgiFormat__@@ -31,6 +40,8 @@   , putData   ) where +import Prelude hiding (lookup)+ import Data.Binary.Get (Get, getWord32le, getByteString, isolate, skip) import Data.Binary.Put (Put, putByteString, putWord32le) import Data.ByteString (ByteString)@@ -39,11 +50,15 @@ import Data.Map.Strict (Map) import Data.Map.Strict qualified as Map import Data.Text (Text)-import Data.Text qualified as Text (pack)+import Data.Text qualified as Text (pack, unpack) import Data.Text.Encoding qualified as Text+import Data.Version import Data.Word (Word32) import GHC.Generics (Generic)+import Text.Read (readMaybe) +import Paths_ktx_codec qualified as Paths+ type KeyValueData = Map Text Value  {- | A wrapper for raw data.@@ -69,6 +84,13 @@ instance FromValue ByteString where   fromValue (Value bs) = Just bs +instance FromValue Integer where+  fromValue val =+    fromValue val >>= readMaybe . Text.unpack++lookup :: FromValue a => Text -> KeyValueData -> Maybe a+lookup key kvd = Map.lookup key kvd >>= fromValue+ text :: Text -> Value text t = Value $ BS.snoc (Text.encodeUtf8 t) 0x00 @@ -78,6 +100,15 @@ number :: (Num a, Show a) => a -> Value number = text . Text.pack . show +insertText :: Text -> Text -> KeyValueData -> KeyValueData+insertText key value = Map.insert key (text value)++insertBytes :: Text -> ByteString -> KeyValueData -> KeyValueData+insertBytes key value = Map.insert key (bytes value)++insertNumber :: (Num a, Show a) => Text -> a -> KeyValueData -> KeyValueData+insertNumber key value = Map.insert key (number value)+ -- | Extract all valid (null-terminated utf8) values. textual :: KeyValueData -> Map Text Text textual = Map.mapMaybe fromValue@@ -137,6 +168,8 @@     putByteString keyAndValue     putByteString $ BS.replicate paddingSize 0 +-- $predefined https://github.khronos.org/KTX-Specification/ktxspec.v2.html#_predefined_keyvalue_pairs+ pattern KTXcubemapIncomplete :: Text pattern KTXcubemapIncomplete = "KTXcubemapIncomplete" @@ -155,8 +188,25 @@ pattern KTXswizzle :: Text pattern KTXswizzle = "KTXswizzle" +{- | KTX file writers may, and are strongly encouraged to, identify themselves by including a value with the key @KTXwriter@++The value is a NUL-terminated UTF-8 string that will uniquely identify the tool writing the file, for example: @AcmeCo TexTool v1.0@.+Only the most recent writer should be identified. Editing tools must overwrite this value when rewriting a file originally written by a different tool.+-} pattern KTXwriter :: Text pattern KTXwriter = "KTXwriter"++-- | Replace writer info with your own, using this package version as baseline.+setWriterWith :: (Text -> Text) -> KeyValueData -> KeyValueData+setWriterWith f = Map.insert KTXwriter (writerKtxCodecWith f)++-- | Attach your application/library version to the writer tag.+writerKtxCodecWith :: (Text -> Text) -> Value+writerKtxCodecWith f = text . f $ "ktx-codec " <> Text.pack (showVersion Paths.version)++-- | The value for the KTXwriter we ought to write when using this package to write or modify KTX files.+writerKtxCodec :: Value+writerKtxCodec = writerKtxCodecWith id  pattern KTXwriterScParams :: Text pattern KTXwriterScParams = "KTXwriterScParams"
+ src/Codec/Ktx2.hs view
@@ -0,0 +1,24 @@+module Codec.Ktx2+  ( Ktx2(..)+  ) where++import Codec.Ktx.KeyValue (KeyValueData)+import Codec.Ktx2.DFD qualified as DFD+import Codec.Ktx2.Header (Header)+import Data.ByteString (ByteString)+import Data.Vector (Vector)+import Data.Word (Word64)+import GHC.Generics (Generic)++{- | Shared wrapper for reading and writing files.++When uploading textures to GPU prefer reading only the needed chunks and using direct transfers.+-}+data Ktx2 = Ktx2+  { header :: Header+  , dfdBlocks :: Vector DFD.Block+  , kvd :: KeyValueData+  , sgd :: ByteString+  , levels :: [(Maybe Word64, ByteString)]+  }+  deriving (Eq, Show, Generic)
src/Codec/Ktx2/Read.hs view
@@ -24,6 +24,10 @@   , BytesContext   , bytes +  -- * Reading everything at once+  , fromFile+  , fromBytes+   -- * Reading blocks    -- ** Image data@@ -44,7 +48,7 @@   , ReadLevel(..)   ) where -import Control.Exception (Exception, throwIO)+import Control.Exception (Exception, bracket, throwIO) import Control.Monad.IO.Class (MonadIO(..)) import Data.Binary (Binary(..)) import Data.Binary.Get (Get, ByteOffset, getByteString, runGetOrFail)@@ -54,6 +58,7 @@ import Data.ByteString.Unsafe qualified as BSU import Data.String (fromString) import Data.Text (Text)+import Data.Traversable (for) import Data.Vector (Vector) import Data.Vector qualified as Vector import Foreign (Ptr, plusPtr)@@ -62,6 +67,8 @@  import Codec.Ktx.KeyValue (KeyValueData) import Codec.Ktx.KeyValue qualified as KeyValue+import Codec.Ktx2 (Ktx2)+import Codec.Ktx2 qualified as Ktx2 import Codec.Ktx2.Header (Header(..)) import Codec.Ktx2.Level (Level(..)) import Codec.Ktx2.DFD (DFD(..))@@ -154,6 +161,24 @@   pure $ Context src header  -- * File contents++fromFile :: MonadIO io => FilePath -> io Ktx2+fromFile source = liftIO $ bracket (open source) close fromContext++fromBytes :: MonadIO io => ByteString -> io Ktx2+fromBytes source = bytes source >>= fromContext++fromContext :: (MonadIO io, ReadChunk a) => Context a -> io Ktx2+fromContext ktx@Context{header} = do+  DFD{dfdBlocks} <- dataFormatDescriptor ktx+  kvd <- keyValueData ktx+  sgd <- supercompressionGlobalData ktx+  levels' <- levels ktx+  annLevels <-+    for (Vector.toList levels') \l@Level{uncompressedByteLength} -> do+      bs <- levelData ktx l+      pure (Just uncompressedByteLength, bs)+  pure Ktx2.Ktx2{levels=annLevels, ..}  -- | Read the level index. levels
src/Codec/Ktx2/Write.hs view
@@ -7,44 +7,25 @@ import Control.Monad.IO.Class (MonadIO(..)) import Data.Binary (Binary(..)) import Data.Binary.Put (runPut, putLazyByteString, putByteString)-import Data.ByteString (ByteString) import Data.ByteString qualified as BS import Data.ByteString.Lazy qualified as BSL import Data.Foldable (traverse_) import Data.Map qualified as Map-import Data.Vector (Vector) import Data.Vector qualified as Vector-import Data.Word (Word64) -import Codec.Ktx.KeyValue (KeyValueData) import Codec.Ktx.KeyValue qualified as KeyValueData+import Codec.Ktx2 (Ktx2)+import Codec.Ktx2 qualified as Ktx2 import Codec.Ktx2.DFD (DFD(..)) import Codec.Ktx2.DFD qualified as DFD-import Codec.Ktx2.Header (Header) import Codec.Ktx2.Header qualified as Header import Codec.Ktx2.Level qualified as Level -toFile-  :: MonadIO io-  => FilePath-  -> Header-  -> Vector DFD.Block-  -> KeyValueData-  -> ByteString-  -> [(Maybe Word64, ByteString)]-  -> io ()-toFile path headerBase dfdBlocks kvd sgd levels =-  liftIO . BSL.writeFile path $-    toChunks headerBase dfdBlocks kvd sgd levels+toFile :: MonadIO io => FilePath -> Ktx2 -> io ()+toFile path = liftIO . BSL.writeFile path . toChunks -toChunks-  :: Header-  -> Vector DFD.Block-  -> KeyValueData-  -> ByteString-  -> [(Maybe Word64, ByteString)]-  -> BSL.ByteString-toChunks headerBase dfdBlocks kvd sgd levels =+toChunks :: Ktx2 -> BSL.ByteString+toChunks Ktx2.Ktx2{header=headerBase, dfdBlocks, kvd, sgd, levels} =   runPut do     put header     Vector.mapM_ put levelIndex
test/Spec.hs view
@@ -21,6 +21,7 @@  import Codec.Ktx qualified as Ktx1 import Codec.Ktx.KeyValue qualified as KeyValue+import Codec.Ktx2 qualified as Ktx2 import Codec.Ktx2.DFD (DFD(..)) import Codec.Ktx2.DFD.Khronos.BasicV2 qualified as BasicV2 import Codec.Ktx2.Header qualified as Header@@ -127,6 +128,7 @@         , BS.take 16 readLevel         , BS.take 16 writeLevel         )+ testKtx2Context   :: ( Read.ReadChunk a      , Read.ReadLevel a@@ -206,13 +208,12 @@       , bytes       ) -  pure $-    Write.toChunks-      (Read.header ctx)-      dfdBlocks-      kvd-      sgd-      levelBuffers+  pure $ Write.toChunks Ktx2.Ktx2+    { header = Read.header ctx+    , dfdBlocks+    , levels = levelBuffers+    , ..+    }  printer :: Show a => a -> IO () printer x =