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 +12/−0
- ktx-codec.cabal +15/−14
- src/Codec/Ktx/KeyValue.hs +51/−1
- src/Codec/Ktx2.hs +24/−0
- src/Codec/Ktx2/Read.hs +26/−1
- src/Codec/Ktx2/Write.hs +6/−25
- test/Spec.hs +8/−7
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 =