Skip to content

Commit d12910e

Browse files
committed
added proper effort and distance
1 parent 57d2ab7 commit d12910e

File tree

6 files changed

+61
-52
lines changed

6 files changed

+61
-52
lines changed

JxlCoder.podspec

Lines changed: 1 addition & 1 deletion
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.0.12'
3+
s.version = '1.1.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'

Sources/JxlCoder/JXLCoder.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,19 @@ public class JXLCoder {
5858
}
5959

6060
/***
61-
- Parameter compressionDistance: Sets the distance level for lossy compression: target max butteraugli
62-
* distance, lower = higher quality. Range: 0 .. 15.
63-
* 0.0 = mathematically lossless (however, use JxlEncoderSetFrameLossless
64-
* instead to use true lossless, as setting distance to 0 alone is not the only
65-
* requirement). 1.0 = visually lossless. Recommended range: 0.5 .. 3.0. Default
66-
* value: 1.0.
61+
- Parameter quality: 0...100
62+
- Parameter effort: 1...9
6763
- Returns: JXL data of the image
6864
**/
6965
public static func encode(image: JXLPlatformImage,
7066
colorSpace: JXLColorSpace = .rgb,
7167
compressionOption: JXLCompressionOption = .lossy,
72-
compressionDistance: Float = 1.0) throws -> Data {
68+
effort: Int = 7,
69+
quality: Int = 100) throws -> Data {
7370
return try shared.encode(image, colorSpace: colorSpace,
7471
compressionOption: compressionOption,
75-
compressionDistance: Double(compressionDistance))
72+
effort: Int32(effort),
73+
quality: Int32(quality))
7674
}
7775

7876
/***

Sources/jxlc/JxlInternalCoder.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ typedef NS_ENUM(NSInteger, JXLCompressionOption) {
4343
- (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream sampleSize:(CGSize)sampleSize error:(NSError *_Nullable * _Nullable)error;
4444
- (CGSize)getSize:(nonnull NSInputStream *)inputStream error:(NSError *_Nullable * _Nullable)error;
4545
- (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
46-
colorSpace:(JXLColorSpace)colorSpace
47-
compressionOption:(JXLCompressionOption)compressionOption
48-
compressionDistance:(double)compressionDistance error:(NSError * _Nullable *_Nullable)error;
46+
colorSpace:(JXLColorSpace)colorSpace
47+
compressionOption:(JXLCompressionOption)compressionOption
48+
effort:(int)effort
49+
quality:(int)quality error:(NSError * _Nullable *_Nullable)error;
4950
@end
5051

5152
#endif /* JXLCoder_h */

Sources/jxlc/JxlInternalCoder.mm

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,20 @@ static inline float JXLGetDistance(const int quality)
5050
return(6.24f+(float) pow(2.5f,(30.0-(float)quality)/5.0)/6.25f);
5151
}
5252

53-
5453
@implementation JxlInternalCoder
5554
- (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
56-
colorSpace:(JXLColorSpace)colorSpace
57-
compressionOption:(JXLCompressionOption)compressionOption
58-
compressionDistance:(double)compressionDistance error:(NSError * _Nullable *_Nullable)error {
55+
colorSpace:(JXLColorSpace)colorSpace
56+
compressionOption:(JXLCompressionOption)compressionOption
57+
effort:(int)effort
58+
quality:(int)quality error:(NSError * _Nullable *_Nullable)error {
59+
60+
if (quality < 0 || quality > 100) {
61+
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Quality must be clamped in 0...100" }];
62+
return nil;
63+
}
5964

60-
if (compressionDistance < 0 || compressionDistance > 15) {
61-
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Compression distance must be clamped in 0...15" }];
65+
if (effort < 1 || effort > 9) {
66+
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Effort must be clamped in 1...9" }];
6267
return nil;
6368
}
6469

@@ -73,10 +78,10 @@ - (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
7378
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Can' create preview of image" }];
7479
return nil;
7580
}
76-
81+
7782
JxlPixelType jColorspace;
7883
JxlCompressionOption jCompressionOption;
79-
84+
8085
switch (colorSpace) {
8186
case kRGB:
8287
jColorspace = rgb;
@@ -85,7 +90,7 @@ - (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
8590
jColorspace = rgba;
8691
break;
8792
}
88-
93+
8994
switch (compressionOption) {
9095
case kLoseless:
9196
jCompressionOption = loseless;
@@ -97,7 +102,7 @@ - (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
97102
std::vector<uint8_t> pixels;
98103
pixels.insert(pixels.end(), (uint8_t*)rgbaData, rgbaData + bufferSize);
99104
free(rgbaData);
100-
105+
101106
if (jColorspace == rgb) {
102107
auto resizedVector = [RgbRgbaConverter convertRGBAtoRGB:pixels width:width height:height];
103108
if (resizedVector.size() == 1) {
@@ -106,23 +111,23 @@ - (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
106111
}
107112
pixels = resizedVector;
108113
}
109-
114+
110115
JXLDataWrapper<uint8_t>* wrapper = new JXLDataWrapper<uint8_t>();
111-
auto encoded = EncodeJxlOneshot(pixels, width, height, &wrapper->data, jColorspace, jCompressionOption, compressionDistance);
116+
auto encoded = EncodeJxlOneshot(pixels, width, height, &wrapper->data, jColorspace, jCompressionOption, JXLGetDistance(quality), effort);
112117
if (!encoded) {
113118
delete wrapper;
114119
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot encode JXL image" }];
115120
return nil;
116121
}
117-
122+
118123
pixels.resize(1);
119-
124+
120125
auto data = [[NSData alloc] initWithBytesNoCopy:wrapper->data.data()
121126
length:wrapper->data.size()
122127
deallocator:^(void * _Nonnull bytes, NSUInteger length) {
123128
delete wrapper;
124129
}];
125-
130+
126131
return data;
127132
}
128133

@@ -133,7 +138,7 @@ - (CGSize)getSize:(nonnull NSInputStream *)inputStream error:(NSError *_Nullabl
133138
std::vector<uint8_t> imageData;
134139
[inputStream open];
135140
if ([inputStream streamStatus] == NSStreamStatusOpen) {
136-
141+
137142
while ([inputStream hasBytesAvailable]) {
138143
NSInteger bytes_read = [inputStream read:buffer.data() maxLength:buffer_length];
139144
if (bytes_read > 0) {
@@ -154,19 +159,19 @@ - (CGSize)getSize:(nonnull NSInputStream *)inputStream error:(NSError *_Nullabl
154159
break;
155160
}
156161
}
157-
162+
158163
[inputStream close];
159164
} else {
160165
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot open input stream" }];
161166
return CGSizeZero;
162167
}
163-
168+
164169
size_t width, height;
165170
if (!DecodeBasicInfo(imageData.data(), imageData.size(), &width, &height)) {
166171
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot decode image info" }];
167172
return CGSizeZero;
168173
}
169-
174+
170175
return CGSizeMake(width, height);
171176
}
172177

@@ -179,7 +184,7 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
179184
std::vector<uint8_t> imageData;
180185
[inputStream open];
181186
if ([inputStream streamStatus] == NSStreamStatusOpen) {
182-
187+
183188
while ([inputStream hasBytesAvailable]) {
184189
NSInteger bytes_read = [inputStream read:buffer.data() maxLength:buffer_length];
185190
if (bytes_read > 0) {
@@ -200,15 +205,15 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
200205
break;
201206
}
202207
}
203-
208+
204209
[inputStream close];
205-
210+
206211
// Now you have the contents in the 'buffer' vector
207212
} else {
208213
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot open input stream" }];
209214
return nil;
210215
}
211-
216+
212217
std::vector<uint8_t> iccProfile;
213218
size_t xSize, ySize;
214219
bool useFloats;
@@ -223,15 +228,15 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
223228
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Failed to decode JXL image" }];
224229
return nil;
225230
}
226-
231+
227232
if (jxlExposedOrientation == Rotate90CW || jxlExposedOrientation == Rotate90CCW
228233
|| jxlExposedOrientation == AntiTranspose
229234
|| jxlExposedOrientation == OrientTranspose) {
230235
size_t xz = xSize;
231236
xSize = ySize;
232237
ySize = xz;
233238
}
234-
239+
235240
if (sampleSize.width > 0 && sampleSize.height > 0) {
236241
auto scaleResult = [RgbaScaler scaleData:outputData width:(int)xSize height:(int)ySize
237242
newWidth:(int)sampleSize.width newHeight:(int)sampleSize.height
@@ -243,7 +248,7 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
243248
xSize = sampleSize.width;
244249
ySize = sampleSize.height;
245250
}
246-
251+
247252
CGColorSpaceRef colorSpace;
248253
if (iccProfile.size() > 0) {
249254
CFDataRef iccData = CFDataCreate(kCFAllocatorDefault, iccProfile.data(), iccProfile.size());
@@ -252,13 +257,13 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
252257
} else {
253258
colorSpace = CGColorSpaceCreateDeviceRGB();
254259
}
255-
260+
256261
if (!colorSpace) {
257262
colorSpace = CGColorSpaceCreateDeviceRGB();
258263
}
259-
264+
260265
int stride = components*(int)xSize * (int)(useFloats ? sizeof(uint16_t) : sizeof(uint8_t));
261-
266+
262267
int flags;
263268
if (useFloats) {
264269
flags = (int)kCGBitmapByteOrder16Host | (int)kCGBitmapFloatComponents;
@@ -275,10 +280,10 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
275280
flags |= (int)kCGImageAlphaNone;
276281
}
277282
}
278-
283+
279284
auto dataWrapper = new JXLDataWrapper<uint8_t>();
280285
dataWrapper->data = outputData;
281-
286+
282287
CGDataProviderRef provider = CGDataProviderCreateWithData(dataWrapper,
283288
dataWrapper->data.data(),
284289
dataWrapper->data.size(),
@@ -290,10 +295,10 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
290295
userInfo:@{ NSLocalizedDescriptionKey: @"CoreGraphics cannot allocate required provider" }];
291296
return NULL;
292297
}
293-
298+
294299
int bitsPerComponent = (useFloats ? sizeof(uint16_t) : sizeof(uint8_t)) * 8;
295300
int bitsPerPixel = bitsPerComponent*components;
296-
301+
297302
CGImageRef imageRef = CGImageCreate(xSize, ySize, bitsPerComponent,
298303
bitsPerPixel,
299304
stride,
@@ -310,7 +315,7 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
310315
#else
311316
image = [UIImage imageWithCGImage:imageRef scale:1 orientation:UIImageOrientationUp];
312317
#endif
313-
318+
314319
return image;
315320
}
316321
@end

Sources/jxlc/JxlWorker.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ bool DecodeBasicInfo(const uint8_t *jxl, size_t size, size_t *xsize, size_t *ysi
217217
bool EncodeJxlOneshot(const std::vector<uint8_t> &pixels, const uint32_t xsize,
218218
const uint32_t ysize, std::vector<uint8_t> *compressed,
219219
JxlPixelType colorspace, JxlCompressionOption compression_option,
220-
float compression_distance) {
220+
float compression_distance, int effort) {
221221
auto enc = JxlEncoderMake(/*memory_manager=*/nullptr);
222222
auto runner = JxlThreadParallelRunnerMake(
223223
/*memory_manager=*/nullptr,
@@ -279,22 +279,27 @@ bool EncodeJxlOneshot(const std::vector<uint8_t> &pixels, const uint32_t xsize,
279279
return false;
280280
}
281281

282-
JxlEncoderFrameSettings *frame_settings =
282+
JxlEncoderFrameSettings *frameSettings =
283283
JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
284284

285285
if (JXL_ENC_SUCCESS !=
286-
JxlEncoderAddImageFrame(frame_settings, &pixel_format,
286+
JxlEncoderAddImageFrame(frameSettings, &pixel_format,
287287
(void *) pixels.data(),
288288
sizeof(uint8_t) * pixels.size())) {
289289
return false;
290290
}
291291

292292
if (compression_option == loseless &&
293-
JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, JXL_TRUE)) {
293+
JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frameSettings, JXL_TRUE)) {
294294
return false;
295295
} else if (compression_option == loosy &&
296296
JXL_ENC_SUCCESS !=
297-
JxlEncoderSetFrameDistance(frame_settings, compression_distance)) {
297+
JxlEncoderSetFrameDistance(frameSettings, compression_distance)) {
298+
return false;
299+
}
300+
301+
if (JxlEncoderFrameSettingsSetOption(frameSettings,
302+
JXL_ENC_FRAME_SETTING_EFFORT, effort) != JXL_ENC_SUCCESS) {
298303
return false;
299304
}
300305

Sources/jxlc/JxlWorker.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ bool DecodeBasicInfo(const uint8_t *jxl, size_t size, size_t *xsize, size_t *ysi
6565
bool EncodeJxlOneshot(const std::vector<uint8_t> &pixels, const uint32_t xsize,
6666
const uint32_t ysize, std::vector<uint8_t> *compressed,
6767
JxlPixelType colorspace, JxlCompressionOption compression_option,
68-
float compression_distance);
68+
float compression_distance, int effort);
6969

7070
template <typename DataType>
7171
class JXLDataWrapper {

0 commit comments

Comments
 (0)