Skip to content

Commit

Permalink
added proper effort and distance
Browse files Browse the repository at this point in the history
  • Loading branch information
awxkee committed Sep 27, 2023
1 parent 57d2ab7 commit d12910e
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 52 deletions.
2 changes: 1 addition & 1 deletion JxlCoder.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'JxlCoder'
s.version = '1.0.12'
s.version = '1.1.0'
s.summary = 'JXL coder for iOS and MacOS'
s.description = 'Provides support for JXL files in iOS and MacOS'
s.homepage = 'https://github.com/awxkee/jxl-coder-swift'
Expand Down
14 changes: 6 additions & 8 deletions Sources/JxlCoder/JXLCoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,19 @@ public class JXLCoder {
}

/***
- Parameter compressionDistance: Sets the distance level for lossy compression: target max butteraugli
* distance, lower = higher quality. Range: 0 .. 15.
* 0.0 = mathematically lossless (however, use JxlEncoderSetFrameLossless
* instead to use true lossless, as setting distance to 0 alone is not the only
* requirement). 1.0 = visually lossless. Recommended range: 0.5 .. 3.0. Default
* value: 1.0.
- Parameter quality: 0...100
- Parameter effort: 1...9
- Returns: JXL data of the image
**/
public static func encode(image: JXLPlatformImage,
colorSpace: JXLColorSpace = .rgb,
compressionOption: JXLCompressionOption = .lossy,
compressionDistance: Float = 1.0) throws -> Data {
effort: Int = 7,
quality: Int = 100) throws -> Data {
return try shared.encode(image, colorSpace: colorSpace,
compressionOption: compressionOption,
compressionDistance: Double(compressionDistance))
effort: Int32(effort),
quality: Int32(quality))
}

/***
Expand Down
7 changes: 4 additions & 3 deletions Sources/jxlc/JxlInternalCoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ typedef NS_ENUM(NSInteger, JXLCompressionOption) {
- (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream sampleSize:(CGSize)sampleSize error:(NSError *_Nullable * _Nullable)error;
- (CGSize)getSize:(nonnull NSInputStream *)inputStream error:(NSError *_Nullable * _Nullable)error;
- (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
colorSpace:(JXLColorSpace)colorSpace
compressionOption:(JXLCompressionOption)compressionOption
compressionDistance:(double)compressionDistance error:(NSError * _Nullable *_Nullable)error;
colorSpace:(JXLColorSpace)colorSpace
compressionOption:(JXLCompressionOption)compressionOption
effort:(int)effort
quality:(int)quality error:(NSError * _Nullable *_Nullable)error;
@end

#endif /* JXLCoder_h */
73 changes: 39 additions & 34 deletions Sources/jxlc/JxlInternalCoder.mm
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,20 @@ static inline float JXLGetDistance(const int quality)
return(6.24f+(float) pow(2.5f,(30.0-(float)quality)/5.0)/6.25f);
}


@implementation JxlInternalCoder
- (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
colorSpace:(JXLColorSpace)colorSpace
compressionOption:(JXLCompressionOption)compressionOption
compressionDistance:(double)compressionDistance error:(NSError * _Nullable *_Nullable)error {
colorSpace:(JXLColorSpace)colorSpace
compressionOption:(JXLCompressionOption)compressionOption
effort:(int)effort
quality:(int)quality error:(NSError * _Nullable *_Nullable)error {

if (quality < 0 || quality > 100) {
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Quality must be clamped in 0...100" }];
return nil;
}

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

Expand All @@ -73,10 +78,10 @@ - (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Can' create preview of image" }];
return nil;
}

JxlPixelType jColorspace;
JxlCompressionOption jCompressionOption;

switch (colorSpace) {
case kRGB:
jColorspace = rgb;
Expand All @@ -85,7 +90,7 @@ - (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
jColorspace = rgba;
break;
}

switch (compressionOption) {
case kLoseless:
jCompressionOption = loseless;
Expand All @@ -97,7 +102,7 @@ - (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
std::vector<uint8_t> pixels;
pixels.insert(pixels.end(), (uint8_t*)rgbaData, rgbaData + bufferSize);
free(rgbaData);

if (jColorspace == rgb) {
auto resizedVector = [RgbRgbaConverter convertRGBAtoRGB:pixels width:width height:height];
if (resizedVector.size() == 1) {
Expand All @@ -106,23 +111,23 @@ - (nullable NSData *)encode:(nonnull JXLSystemImage *)platformImage
}
pixels = resizedVector;
}

JXLDataWrapper<uint8_t>* wrapper = new JXLDataWrapper<uint8_t>();
auto encoded = EncodeJxlOneshot(pixels, width, height, &wrapper->data, jColorspace, jCompressionOption, compressionDistance);
auto encoded = EncodeJxlOneshot(pixels, width, height, &wrapper->data, jColorspace, jCompressionOption, JXLGetDistance(quality), effort);
if (!encoded) {
delete wrapper;
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot encode JXL image" }];
return nil;
}

pixels.resize(1);

auto data = [[NSData alloc] initWithBytesNoCopy:wrapper->data.data()
length:wrapper->data.size()
deallocator:^(void * _Nonnull bytes, NSUInteger length) {
delete wrapper;
}];

return data;
}

Expand All @@ -133,7 +138,7 @@ - (CGSize)getSize:(nonnull NSInputStream *)inputStream error:(NSError *_Nullabl
std::vector<uint8_t> imageData;
[inputStream open];
if ([inputStream streamStatus] == NSStreamStatusOpen) {

while ([inputStream hasBytesAvailable]) {
NSInteger bytes_read = [inputStream read:buffer.data() maxLength:buffer_length];
if (bytes_read > 0) {
Expand All @@ -154,19 +159,19 @@ - (CGSize)getSize:(nonnull NSInputStream *)inputStream error:(NSError *_Nullabl
break;
}
}

[inputStream close];
} else {
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot open input stream" }];
return CGSizeZero;
}

size_t width, height;
if (!DecodeBasicInfo(imageData.data(), imageData.size(), &width, &height)) {
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot decode image info" }];
return CGSizeZero;
}

return CGSizeMake(width, height);
}

Expand All @@ -179,7 +184,7 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
std::vector<uint8_t> imageData;
[inputStream open];
if ([inputStream streamStatus] == NSStreamStatusOpen) {

while ([inputStream hasBytesAvailable]) {
NSInteger bytes_read = [inputStream read:buffer.data() maxLength:buffer_length];
if (bytes_read > 0) {
Expand All @@ -200,15 +205,15 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
break;
}
}

[inputStream close];

// Now you have the contents in the 'buffer' vector
} else {
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Cannot open input stream" }];
return nil;
}

std::vector<uint8_t> iccProfile;
size_t xSize, ySize;
bool useFloats;
Expand All @@ -223,15 +228,15 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
*error = [[NSError alloc] initWithDomain:@"JXLCoder" code:500 userInfo:@{ NSLocalizedDescriptionKey: @"Failed to decode JXL image" }];
return nil;
}

if (jxlExposedOrientation == Rotate90CW || jxlExposedOrientation == Rotate90CCW
|| jxlExposedOrientation == AntiTranspose
|| jxlExposedOrientation == OrientTranspose) {
size_t xz = xSize;
xSize = ySize;
ySize = xz;
}

if (sampleSize.width > 0 && sampleSize.height > 0) {
auto scaleResult = [RgbaScaler scaleData:outputData width:(int)xSize height:(int)ySize
newWidth:(int)sampleSize.width newHeight:(int)sampleSize.height
Expand All @@ -243,7 +248,7 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
xSize = sampleSize.width;
ySize = sampleSize.height;
}

CGColorSpaceRef colorSpace;
if (iccProfile.size() > 0) {
CFDataRef iccData = CFDataCreate(kCFAllocatorDefault, iccProfile.data(), iccProfile.size());
Expand All @@ -252,13 +257,13 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
} else {
colorSpace = CGColorSpaceCreateDeviceRGB();
}

if (!colorSpace) {
colorSpace = CGColorSpaceCreateDeviceRGB();
}

int stride = components*(int)xSize * (int)(useFloats ? sizeof(uint16_t) : sizeof(uint8_t));

int flags;
if (useFloats) {
flags = (int)kCGBitmapByteOrder16Host | (int)kCGBitmapFloatComponents;
Expand All @@ -275,10 +280,10 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
flags |= (int)kCGImageAlphaNone;
}
}

auto dataWrapper = new JXLDataWrapper<uint8_t>();
dataWrapper->data = outputData;

CGDataProviderRef provider = CGDataProviderCreateWithData(dataWrapper,
dataWrapper->data.data(),
dataWrapper->data.size(),
Expand All @@ -290,10 +295,10 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
userInfo:@{ NSLocalizedDescriptionKey: @"CoreGraphics cannot allocate required provider" }];
return NULL;
}

int bitsPerComponent = (useFloats ? sizeof(uint16_t) : sizeof(uint8_t)) * 8;
int bitsPerPixel = bitsPerComponent*components;

CGImageRef imageRef = CGImageCreate(xSize, ySize, bitsPerComponent,
bitsPerPixel,
stride,
Expand All @@ -310,7 +315,7 @@ - (nullable JXLSystemImage *)decode:(nonnull NSInputStream *)inputStream
#else
image = [UIImage imageWithCGImage:imageRef scale:1 orientation:UIImageOrientationUp];
#endif

return image;
}
@end
15 changes: 10 additions & 5 deletions Sources/jxlc/JxlWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ bool DecodeBasicInfo(const uint8_t *jxl, size_t size, size_t *xsize, size_t *ysi
bool EncodeJxlOneshot(const std::vector<uint8_t> &pixels, const uint32_t xsize,
const uint32_t ysize, std::vector<uint8_t> *compressed,
JxlPixelType colorspace, JxlCompressionOption compression_option,
float compression_distance) {
float compression_distance, int effort) {
auto enc = JxlEncoderMake(/*memory_manager=*/nullptr);
auto runner = JxlThreadParallelRunnerMake(
/*memory_manager=*/nullptr,
Expand Down Expand Up @@ -279,22 +279,27 @@ bool EncodeJxlOneshot(const std::vector<uint8_t> &pixels, const uint32_t xsize,
return false;
}

JxlEncoderFrameSettings *frame_settings =
JxlEncoderFrameSettings *frameSettings =
JxlEncoderFrameSettingsCreate(enc.get(), nullptr);

if (JXL_ENC_SUCCESS !=
JxlEncoderAddImageFrame(frame_settings, &pixel_format,
JxlEncoderAddImageFrame(frameSettings, &pixel_format,
(void *) pixels.data(),
sizeof(uint8_t) * pixels.size())) {
return false;
}

if (compression_option == loseless &&
JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, JXL_TRUE)) {
JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frameSettings, JXL_TRUE)) {
return false;
} else if (compression_option == loosy &&
JXL_ENC_SUCCESS !=
JxlEncoderSetFrameDistance(frame_settings, compression_distance)) {
JxlEncoderSetFrameDistance(frameSettings, compression_distance)) {
return false;
}

if (JxlEncoderFrameSettingsSetOption(frameSettings,
JXL_ENC_FRAME_SETTING_EFFORT, effort) != JXL_ENC_SUCCESS) {
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/jxlc/JxlWorker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ bool DecodeBasicInfo(const uint8_t *jxl, size_t size, size_t *xsize, size_t *ysi
bool EncodeJxlOneshot(const std::vector<uint8_t> &pixels, const uint32_t xsize,
const uint32_t ysize, std::vector<uint8_t> *compressed,
JxlPixelType colorspace, JxlCompressionOption compression_option,
float compression_distance);
float compression_distance, int effort);

template <typename DataType>
class JXLDataWrapper {
Expand Down

0 comments on commit d12910e

Please sign in to comment.