Skip to content

Commit 0eb1f19

Browse files
committed
added animation decoder and encoder
1 parent 2f5ba6d commit 0eb1f19

19 files changed

+285
-30
lines changed

JxlCoder.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'JxlCoder'
3-
s.version = '1.2.8'
3+
s.version = '1.3.0'
44
s.summary = 'JXL coder for iOS and MacOS'
55
s.description = 'Provides support for JXL files in iOS and MacOS'
66
s.homepage = 'https://github.com/awxkee/jxl-coder-swift'

JxlSDWebImageCoder.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'JxlSDWebImageCoder'
3-
s.version = '1.0.7'
3+
s.version = '1.3.0'
44
s.summary = 'JXL encoder and decoder for SDWebImage'
55
s.description = 'JXL plugin for SDWebImage in iOS and MacOS'
66
s.homepage = 'https://github.com/awxkee/jxl-coder-swift'

JxlSDWebImageCoder/JxlSDWebImageCoder.swift

+64-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import AppKit.NSImage
2020
public typealias JxlPlatformImage = NSImage
2121
#endif
2222

23-
2423
public class JxlSDWebImageCoder: NSObject, SDImageCoder {
2524
public override init() {
2625
}
@@ -29,7 +28,7 @@ public class JxlSDWebImageCoder: NSObject, SDImageCoder {
2928
guard let data else {
3029
return false
3130
}
32-
return (try? JXLCoder.isJXL(data: data)) ?? false
31+
return JXLCoder.isJXL(data: data)
3332
}
3433

3534
public func decodedImage(with data: Data?, options: [SDImageCoderOption : Any]? = nil) -> JxlPlatformImage? {
@@ -50,3 +49,66 @@ public class JxlSDWebImageCoder: NSObject, SDImageCoder {
5049
return try? JXLCoder.encode(image: image)
5150
}
5251
}
52+
53+
public class JxlAnimatedSDWebImageCoder: NSObject, SDAnimatedImageCoder {
54+
55+
private let dec: JXLAnimatedDecoder?
56+
private let animatedData: Data?
57+
58+
public required init?(animatedImageData data: Data?, options: [SDImageCoderOption : Any]? = nil) {
59+
if let data, let mDecoder = try? JXLAnimatedDecoder(data: data) {
60+
self.animatedData = data
61+
dec = mDecoder
62+
} else {
63+
return nil
64+
}
65+
}
66+
67+
public override init() {
68+
dec = nil
69+
animatedData = nil
70+
}
71+
72+
public func canDecode(from data: Data?) -> Bool {
73+
guard let data else { return false }
74+
return JXLCoder.isJXL(data: data)
75+
}
76+
77+
public func decodedImage(with data: Data?, options: [SDImageCoderOption : Any]? = nil) -> JxlPlatformImage? {
78+
guard let data else {
79+
return nil
80+
}
81+
return try? JXLCoder.decode(data: data)
82+
}
83+
84+
public func canEncode(to format: SDImageFormat) -> Bool {
85+
true
86+
}
87+
88+
public func encodedData(with image: JxlPlatformImage?, format: SDImageFormat, options: [SDImageCoderOption : Any]? = nil) -> Data? {
89+
guard let image else { return nil }
90+
return try? JXLCoder.encode(image: image)
91+
}
92+
93+
public var animatedImageData: Data? { animatedData }
94+
95+
public var animatedImageFrameCount: UInt {
96+
guard let dec else { return 0 }
97+
return UInt(dec.numberOfFrames)
98+
}
99+
100+
public var animatedImageLoopCount: UInt {
101+
guard let dec else { return 0 }
102+
return UInt(dec.loopsCount)
103+
}
104+
105+
public func animatedImageFrame(at index: UInt) -> JxlPlatformImage? {
106+
guard let dec else { return nil }
107+
return try? dec.get(frame: Int(index))
108+
}
109+
110+
public func animatedImageDuration(at index: UInt) -> TimeInterval {
111+
guard let dec else { return 0 }
112+
return TimeInterval(dec.frameDuration(Int(index))) / 1000.0
113+
}
114+
}

README.md

+29-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# JxlCoder
22

33
## What's This?
4-
This package is provides support for Jpeg XL images for all apple platforms. Supports encode JXL ( Jpeg XL) on iOS, MacOS and decode JXL ( Jpeg XL ) images in convinient and fast way.
4+
This package is provides support for Jpeg XL images for all apple platforms.
5+
Supports encode JXL ( Jpeg XL ) on iOS, MacOS and decode JXL ( Jpeg XL ) images in convinient and fast way for the single image and animation.
56

67
A package to decode Jpeg XL on iOS, MacOS or encode JXL images. Also provider JXL support on iOS for Nuke and SDWebImage. Have support for older versions of iOS, MacOSX and all the simulators that doesn't have support Jpeg XL images. Also support Objective C interoping for old projects via Cocoapods
78

8-
Supported ICC Profiles and HDR images.
9+
Supported ICC Profiles and HDR images. Also supports animated JPEG XL images decoding and encoding.
10+
Contains integration for SDWebImage to decode single image and animated Jpeg XL images.
911

1012
Package based on `libjxl`
1113
</br>
@@ -44,6 +46,27 @@ let uiImage: UIImage? = JXLCoder.decode(data: Data()) // or any max CGSize of im
4446
let data: Data = try JXLCoder.encode(data: UIImage())
4547
```
4648

49+
## Usage for animations
50+
```swift
51+
// Decoding
52+
let decoder = try! JXLAnimatedDecoder(data: animationJxlData)
53+
let framesCount = Int(decoder.numberOfFrames)
54+
print("frames count \(framesCount)")
55+
let duration = decoder.frameDuration(currentFrame)
56+
let frame: UIImage = try! decoder.get(frame: currentFrame)
57+
58+
// Encoding
59+
let animEncoder = try! JXLAnimatedEncoder(width: frameToAnimate.size.width,
60+
height: frameToAnimate.size.height)
61+
try! animEncoder.add(frame: frameToAnimate, duration: 150) // duration is in ms
62+
try! animEncoder.add(frame: frameToAnimate, duration: 150)
63+
try! animEncoder.add(frame: frameToAnimate, duration: 150)
64+
try! animEncoder.add(frame: frameToAnimate, duration: 150)
65+
try! animEncoder.add(frame: frameToAnimate, duration: 150)
66+
// etc and then finish the encoding
67+
let animationJxlData = try! animEncoder.finish()
68+
```
69+
4770
## Nuke Plugin
4871

4972
If you wish to use `JXL` with <a href="https://github.com/kean/Nuke" target="_blank">`Nuke`</a> you may add `JxlCoder` library to project and activate the plugin on app init
@@ -91,7 +114,7 @@ extension JxlNukePlugin {
91114
```
92115

93116
## Jxl SDWebImagePlugin
94-
### Use provided code or include pod `JxlSDWebImageCoder`
117+
### Use provided code or include pod `JxlSDWebImageCoder` or include the code below
95118
```swift
96119
#if canImport(JxlCoder)
97120
import JxlCoder
@@ -128,9 +151,10 @@ public class JxlSDWebImageCoder: NSObject, SDImageCoder {
128151
}
129152
}
130153

131-
// after register the plugin
154+
// don't forget to register the plugin after
132155
SDImageCodersManager.shared.addCoder(JxlSDWebImageCoder())
133-
156+
// IMPORTANT: if you will use the animated Jpeg XL image you have to use other plugin
157+
SDImageCodersManager.shared.addCoder(JxlAnimatedSDWebImageCoder())
134158
```
135159

136160
Currently, JXL nuke plugin do not support animated JXLs so you have to do it yourself
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// JXLAnimatedDeocder.swift
3+
// Jxl Coder [https://github.com/awxkee/jxl-coder-swift]
4+
//
5+
// Created by Radzivon Bartoshyk on 28/10/2023.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
//
25+
26+
import Foundation
27+
import jxlc
28+
29+
public class JXLAnimatedDecoder {
30+
31+
private let dec: CJpegXLAnimatedDecoder
32+
33+
public init(data: Data) throws {
34+
dec = try CJpegXLAnimatedDecoder(data)
35+
}
36+
37+
public var numberOfFrames: Int {
38+
Int(dec.framesCount())
39+
}
40+
41+
public func frameDuration(_ frame: Int) -> Int {
42+
return Int(dec.frameDuration(Int32(frame)))
43+
}
44+
45+
public var loopsCount: Int {
46+
Int(dec.loopCount())
47+
}
48+
49+
public func get(frame: Int) throws -> JXLPlatformImage {
50+
try dec.get(Int32(frame))
51+
}
52+
53+
}
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// JXLAnimatedDeocder.swift
3+
// Jxl Coder [https://github.com/awxkee/jxl-coder-swift]
4+
//
5+
// Created by Radzivon Bartoshyk on 28/10/2023.
6+
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
//
25+
26+
import Foundation
27+
import jxlc
28+
29+
public class JXLAnimatedEncoder {
30+
31+
private let enc: CJpegXLAnimatedEncoder
32+
33+
public init(width: Int, height: Int,
34+
numLoops: Int = 0, // 0 - means infinity
35+
colorSpace: JXLColorSpace = .rgba,
36+
compressionOption: JXLCompressionOption = .lossy,
37+
effort: Int = 4, quality: Int = 0) throws {
38+
enc = try CJpegXLAnimatedEncoder(Int32(width),
39+
height: Int32(height),
40+
numLoops: Int32(numLoops),
41+
colorSpace: colorSpace,
42+
compressionOption: compressionOption,
43+
effort: Int32(effort),
44+
quality: Int32(quality))
45+
}
46+
47+
/**
48+
- Parameter frame: all the frames must match provided width and height in constructor
49+
- Parameter duration: length of the frame in milliseconds
50+
*/
51+
public func add(frame: JXLPlatformImage, duration ms: Int) throws {
52+
try enc.addFrame(frame, duration: Int32(ms))
53+
}
54+
55+
public func finish() throws -> Data {
56+
try enc.finish()
57+
}
58+
}

Sources/JxlCoder/JXLCoder.swift

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
//
22
// JXLCoder.swift
3-
// Jxl Coder
3+
// Jxl Coder [https://github.com/awxkee/jxl-coder-swift]
44
//
55
// Created by Radzivon Bartoshyk on 27/08/2023.
66
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
//
725

826
import Foundation
927
#if canImport(jxlc)
@@ -27,7 +45,7 @@ public class JXLCoder {
2745
/***
2846
- Returns: If provided data is possible valid JXL image
2947
**/
30-
public static func isJXL(data: Data) throws -> Bool {
48+
public static func isJXL(data: Data) -> Bool {
3149
return startsWith(magic1, ofLength: magic1.count, in: data) || startsWith(magic2, ofLength: magic2.count, in: data)
3250
}
3351

Sources/JxlCoder/JXLSupport.swift

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
//
2-
// JXLSupport.swift
3-
// Jxl Coder
2+
// JXLCoder.swift
3+
// Jxl Coder [https://github.com/awxkee/jxl-coder-swift]
44
//
55
// Created by Radzivon Bartoshyk on 27/08/2023.
66
//
7+
// Permission is hereby granted, free of charge, to any person obtaining a copy
8+
// of this software and associated documentation files (the "Software"), to deal
9+
// in the Software without restriction, including without limitation the rights
10+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
// copies of the Software, and to permit persons to whom the Software is
12+
// furnished to do so, subject to the following conditions:
13+
//
14+
// The above copyright notice and this permission notice shall be included in
15+
// all copies or substantial portions of the Software.
16+
//
17+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
// THE SOFTWARE.
24+
//
725

826
import Foundation
927
#if !os(macOS)

Sources/jxlc/JpegXLAnimatedDecoder.h Sources/jxlc/CJpegXLAnimatedDecoder.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// JpegXLAnimatedDecoder.h
2+
// CJpegXLAnimatedDecoder.h
33
// JxclCoder [https://github.com/awxkee/jxl-coder-swift]
44
//
55
// Created by Radzivon Bartoshyk on 27/10/2023.
@@ -29,7 +29,7 @@
2929
#import "JXLSystemImage.hpp"
3030
#import <Foundation/Foundation.h>
3131

32-
@interface JpegXLAnimatedDecoder : NSObject
32+
@interface CJpegXLAnimatedDecoder : NSObject
3333
-(nullable id)initWith:(nonnull NSData*)data error:(NSError * _Nullable *_Nullable)error;
3434
-(NSUInteger)framesCount;
3535
-(int)frameDuration:(int)frame;

Sources/jxlc/JpegXLAnimatedDecoder.mm Sources/jxlc/CJpegXLAnimatedDecoder.mm

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// JpegXLAnimatedDecoder.mm
2+
// CJpegXLAnimatedDecoder.mm
33
// JxclCoder [https://github.com/awxkee/jxl-coder-swift]
44
//
55
// Created by Radzivon Bartoshyk on 27/10/2023.
@@ -24,7 +24,7 @@
2424
//
2525

2626
#import <Foundation/Foundation.h>
27-
#import "JpegXLAnimatedDecoder.h"
27+
#import "CJpegXLAnimatedDecoder.h"
2828
#import "JxlAnimatedDecoder.hpp"
2929
#include <vector>
3030

@@ -40,7 +40,7 @@ static void JXLDCGData8ProviderReleaseDataCallback(void *info, const void *data,
4040
delete dataWrapper;
4141
}
4242

43-
@implementation JpegXLAnimatedDecoder {
43+
@implementation CJpegXLAnimatedDecoder {
4444
JxlAnimatedDecoder* dec;
4545
std::vector<uint8_t> mSrc;
4646
}
@@ -151,6 +151,7 @@ -(int)frameDuration:(int)frame {
151151
-(void)deinit {
152152
if (dec) {
153153
delete dec;
154+
dec = nullptr;
154155
}
155156
mSrc.clear();
156157
}

0 commit comments

Comments
 (0)