Skip to content

Commit 2f5ba6d

Browse files
committed
working on animated JXL
1 parent 339e7d0 commit 2f5ba6d

15 files changed

+1270
-96
lines changed

.swiftpm/xcode/xcuserdata/radzivonbartoshyk.xcuserdatad/xcschemes/xcschememanagement.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<key>Jxl Coder-Package.xcscheme_^#shared#^_</key>
88
<dict>
99
<key>orderHint</key>
10-
<integer>3</integer>
10+
<integer>0</integer>
1111
</dict>
1212
<key>JxlCoder-Package.xcscheme_^#shared#^_</key>
1313
<dict>

Sources/jxlc/JXLSystemImage.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,34 @@
3939
#define JXLSystemImage UIImage
4040
#endif
4141

42+
typedef NS_ENUM(NSInteger, JXLColorSpace) {
43+
kRGB NS_SWIFT_NAME(rgb),
44+
kRGBA NS_SWIFT_NAME(rgba)
45+
};
46+
47+
typedef NS_ENUM(NSInteger, JXLCompressionOption) {
48+
kLoseless NS_SWIFT_NAME(loseless),
49+
kLossy NS_SWIFT_NAME(lossy)
50+
};
51+
52+
typedef NS_ENUM(NSInteger, JXLPreferredPixelFormat) {
53+
kOptimal NS_SWIFT_NAME(optimal),
54+
kR8 NS_SWIFT_NAME(r8),
55+
kFloat16 NS_SWIFT_NAME(float16),
56+
};
57+
58+
typedef NS_ENUM(NSInteger, JxlSampler) {
59+
kNearestNeighbor NS_SWIFT_NAME(nearestNeighbor),
60+
kBilinear NS_SWIFT_NAME(bilinear),
61+
kCubic NS_SWIFT_NAME(cubic),
62+
kBSpline NS_SWIFT_NAME(bSpline),
63+
kMitchell NS_SWIFT_NAME(mitchell),
64+
kLanczos NS_SWIFT_NAME(lanczos),
65+
kCatmullRom NS_SWIFT_NAME(catmullRom),
66+
kHermite NS_SWIFT_NAME(hermite),
67+
kHann NS_SWIFT_NAME(hann)
68+
};
69+
4270
@interface JXLSystemImage (JXLColorData)
4371
- (nullable uint8_t*)jxlRGBAPixels:(nonnull size_t*)bufferSize width:(nonnull int*)xSize height:(nonnull int*)ySize;
4472
@end

Sources/jxlc/JXLSystemImage.mm

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ - (nullable uint8_t*)jxlRGBAPixels:(nonnull size_t*)bufferSize width:(nonnull in
6868
*bufferSize = (size_t)(stride * height);
6969
*xSize = (int)width;
7070
*ySize = (int)height;
71-
71+
7272
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
7373
CGBitmapInfo bitmapInfo = (int)kCGImageAlphaPremultipliedLast | (int)kCGBitmapByteOrder32Big;
7474

@@ -94,6 +94,40 @@ - (nullable uint8_t*)jxlRGBAPixels:(nonnull size_t*)bufferSize width:(nonnull in
9494

9595
return targetMemory;
9696
}
97+
98+
- (bool)jxlRGBAVPixels:(std::vector<uint8_t>&)buf width:(nonnull int*)xSize height:(nonnull int*)ySize {
99+
CGImageRef imageRef = [self makeCGImage];
100+
NSUInteger width = CGImageGetWidth(imageRef);
101+
NSUInteger height = CGImageGetHeight(imageRef);
102+
int stride = (int)4 * (int)width * sizeof(uint8_t);
103+
buf.resize(stride * height);
104+
*xSize = (int)width;
105+
*ySize = (int)height;
106+
107+
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
108+
CGBitmapInfo bitmapInfo = (int)kCGImageAlphaPremultipliedLast | (int)kCGBitmapByteOrder32Big;
109+
110+
CGContextRef targetContext = CGBitmapContextCreate(buf.data(), width, height, 8, stride, colorSpace, bitmapInfo);
111+
112+
[NSGraphicsContext saveGraphicsState];
113+
[NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithCGContext:targetContext flipped:FALSE]];
114+
CGColorSpaceRelease(colorSpace);
115+
116+
[self drawInRect: NSMakeRect(0, 0, width, height)
117+
fromRect: NSZeroRect
118+
operation: NSCompositingOperationCopy
119+
fraction: 1.0];
120+
121+
[NSGraphicsContext restoreGraphicsState];
122+
123+
CGContextRelease(targetContext);
124+
125+
if (![self unpremultiply:buf.data() width:width height:height]) {
126+
return false;
127+
}
128+
129+
return true;
130+
}
97131
#else
98132
- (nullable uint8_t*)jxlRGBAPixels:(nonnull size_t*)bufferSize width:(nonnull int*)xSize height:(nonnull int*)ySize {
99133
CGImageRef imageRef = [self CGImage];

Sources/jxlc/JpegXLAnimatedDecoder.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// JpegXLAnimatedDecoder.h
3+
// JxclCoder [https://github.com/awxkee/jxl-coder-swift]
4+
//
5+
// Created by Radzivon Bartoshyk on 27/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+
#ifndef JPEGXL_ANIMATED_DECODER_H
27+
#define JPEGXL_ANIMATED_DECODER_H
28+
29+
#import "JXLSystemImage.hpp"
30+
#import <Foundation/Foundation.h>
31+
32+
@interface JpegXLAnimatedDecoder : NSObject
33+
-(nullable id)initWith:(nonnull NSData*)data error:(NSError * _Nullable *_Nullable)error;
34+
-(NSUInteger)framesCount;
35+
-(int)frameDuration:(int)frame;
36+
-(int)loopCount;
37+
-(nullable JXLSystemImage *)get:(int)frame
38+
error:(NSError *_Nullable * _Nullable)error;
39+
@end
40+
41+
#endif /* JPEGXL_ANIMATED_DECODER_H */

Sources/jxlc/JpegXLAnimatedDecoder.mm

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
//
2+
// JpegXLAnimatedDecoder.mm
3+
// JxclCoder [https://github.com/awxkee/jxl-coder-swift]
4+
//
5+
// Created by Radzivon Bartoshyk on 27/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/Foundation.h>
27+
#import "JpegXLAnimatedDecoder.h"
28+
#import "JxlAnimatedDecoder.hpp"
29+
#include <vector>
30+
31+
template <typename DataType>
32+
class JXLDDataWrapper {
33+
public:
34+
JXLDDataWrapper(const std::vector<DataType>& src): data(src) {}
35+
const std::vector<DataType> data;
36+
};
37+
38+
static void JXLDCGData8ProviderReleaseDataCallback(void *info, const void *data, size_t size) {
39+
auto dataWrapper = static_cast<JXLDDataWrapper<uint8_t>*>(info);
40+
delete dataWrapper;
41+
}
42+
43+
@implementation JpegXLAnimatedDecoder {
44+
JxlAnimatedDecoder* dec;
45+
std::vector<uint8_t> mSrc;
46+
}
47+
48+
-(nullable id)initWith:(nonnull NSData*)data error:(NSError * _Nullable *_Nullable)error {
49+
try {
50+
const uint8_t* ptr = reinterpret_cast<const uint8_t*>([data bytes]);
51+
mSrc.resize([data length]);
52+
std::copy(ptr, ptr + [data length], mSrc.begin());
53+
dec = new JxlAnimatedDecoder(mSrc);
54+
} catch (AnimatedDecoderError& err) {
55+
NSString *str = [[NSString alloc] initWithCString:err.what() encoding:NSUTF8StringEncoding];
56+
*error = [[NSError alloc] initWithDomain:@"JpegXLAnimatedDecoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: str }];
57+
return nil;
58+
} catch (std::bad_alloc &err) {
59+
NSString *str = [[NSString alloc] initWithCString:err.what() encoding:NSUTF8StringEncoding];
60+
*error = [[NSError alloc] initWithDomain:@"JpegXLAnimatedDecoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: str }];
61+
return nil;
62+
}
63+
return self;
64+
}
65+
66+
-(nullable JXLSystemImage *)get:(int)frame
67+
error:(NSError *_Nullable * _Nullable)error {
68+
try {
69+
JxlFrame jxlFrame = dec->getFrame(frame);
70+
auto wrapper = new JXLDDataWrapper<uint8_t>(jxlFrame.pixels);
71+
72+
CGDataProviderRef provider = CGDataProviderCreateWithData(wrapper,
73+
wrapper->data.data(),
74+
wrapper->data.size(),
75+
JXLDCGData8ProviderReleaseDataCallback);
76+
if (!provider) {
77+
delete wrapper;
78+
*error = [[NSError alloc] initWithDomain:@"JXLCoder"
79+
code:500
80+
userInfo:@{ NSLocalizedDescriptionKey: @"CoreGraphics cannot allocate required provider" }];
81+
return nullptr;
82+
}
83+
84+
int bitsPerComponent = sizeof(uint8_t) * 8;
85+
int components = 4;
86+
int bitsPerPixel = bitsPerComponent*components;
87+
int stride = 4 * dec->getWidth() * sizeof(uint8_t);
88+
89+
CGColorSpaceRef colorSpace;
90+
if (jxlFrame.iccProfile.size() > 0) {
91+
CFDataRef iccData = CFDataCreate(kCFAllocatorDefault, jxlFrame.iccProfile.data(), jxlFrame.iccProfile.size());
92+
colorSpace = CGColorSpaceCreateWithICCData(iccData);
93+
CFRelease(iccData);
94+
} else {
95+
colorSpace = CGColorSpaceCreateDeviceRGB();
96+
}
97+
98+
if (!colorSpace) {
99+
colorSpace = CGColorSpaceCreateDeviceRGB();
100+
}
101+
102+
int flags;
103+
flags = (int)kCGImageByteOrderDefault;
104+
if (components == 4) {
105+
flags |= (int)kCGImageAlphaLast;
106+
} else {
107+
flags |= (int)kCGImageAlphaNone;
108+
}
109+
110+
CGImageRef imageRef = CGImageCreate(dec->getWidth(), dec->getHeight(), bitsPerComponent,
111+
bitsPerPixel,
112+
stride,
113+
colorSpace, flags, provider, NULL, false, kCGRenderingIntentDefault);
114+
if (!imageRef) {
115+
*error = [[NSError alloc] initWithDomain:@"JXLCoder"
116+
code:500
117+
userInfo:@{ NSLocalizedDescriptionKey: @"CoreGraphics cannot allocate CGImageRef" }];
118+
return NULL;
119+
}
120+
JXLSystemImage *image = nil;
121+
#if JXL_PLUGIN_MAC
122+
image = [[NSImage alloc] initWithCGImage:imageRef size:CGSizeZero];
123+
#else
124+
image = [UIImage imageWithCGImage:imageRef scale:1 orientation:UIImageOrientationUp];
125+
#endif
126+
127+
return image;
128+
} catch (AnimatedDecoderError& err) {
129+
NSString *str = [[NSString alloc] initWithCString:err.what() encoding:NSUTF8StringEncoding];
130+
*error = [[NSError alloc] initWithDomain:@"JpegXLAnimatedDecoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: str }];
131+
return nil;
132+
} catch (std::bad_alloc &err) {
133+
NSString *str = [[NSString alloc] initWithCString:err.what() encoding:NSUTF8StringEncoding];
134+
*error = [[NSError alloc] initWithDomain:@"JpegXLAnimatedDecoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: str }];
135+
return nil;
136+
}
137+
}
138+
139+
-(NSUInteger)framesCount {
140+
return static_cast<NSUInteger>(dec->getNumberOfFrames());
141+
}
142+
143+
-(int)loopCount {
144+
return static_cast<int>(dec->getLoopCount());
145+
}
146+
147+
-(int)frameDuration:(int)frame {
148+
return static_cast<int>(dec->getFrameDuration(frame));
149+
}
150+
151+
-(void)deinit {
152+
if (dec) {
153+
delete dec;
154+
}
155+
mSrc.clear();
156+
}
157+
158+
@end

Sources/jxlc/JpegXLAnimatedEncoder.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// CAnimatedEncoder.h
3+
// JxclCoder [https://github.com/awxkee/jxl-coder-swift]
4+
//
5+
// Created by Radzivon Bartoshyk on 26/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+
#ifndef CANIMATED_ENCODER_H
27+
#define CANIMATED_ENCODER_H
28+
29+
#import "JXLSystemImage.hpp"
30+
#import <Foundation/Foundation.h>
31+
32+
@interface JpegXLAnimatedEncoder : NSObject
33+
-(nullable id)initWith:(int)width height:(int)height numLoops:(int)numLoops colorSpace:(JXLColorSpace)colorSpace
34+
compressionOption:(JXLCompressionOption)compressionOption
35+
effort:(int)effort
36+
quality:(int)quality error:(NSError * _Nullable *_Nullable)error;
37+
-(nullable void*)addFrame:(nonnull JXLSystemImage *)platformImage duration:(int)duration error:(NSError * _Nullable *_Nullable)error;
38+
-(nullable NSData*)finish:(NSError * _Nullable *_Nullable)error;
39+
@end
40+
41+
#endif /* Header_h */

0 commit comments

Comments
 (0)