mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Add jpeg-xl
This commit is contained in:
parent
21afe24790
commit
a35f38c0f6
@ -44,6 +44,14 @@ public func compressImageToJPEG(_ image: UIImage, quality: Float) -> Data? {
|
||||
return data as Data
|
||||
}
|
||||
|
||||
public func compressImageToJPEGXL(_ image: UIImage, quality: Int) -> Data? {
|
||||
return compressJPEGXLData(image, Int32(quality))
|
||||
}
|
||||
|
||||
public func decompressImageFromJPEGXL(data: Data) -> UIImage? {
|
||||
return decompressJPEGXLData(data)
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension 11.0, iOS 11.0, *)
|
||||
public func compressImage(_ image: UIImage, quality: Float) -> Data? {
|
||||
let data = NSMutableData()
|
||||
|
@ -495,50 +495,128 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A
|
||||
}
|
||||
}
|
||||
case let .asset(asset):
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
|
||||
let scaledSize = size.aspectFittedOrSmaller(CGSize(width: 1280.0, height: 1280.0))
|
||||
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: Int64.random(in: Int64.min ... Int64.max))
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||
|
||||
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
|
||||
var attributes: [MessageAttribute] = []
|
||||
if let timer = item.timer, timer > 0 && timer <= 60 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
||||
}
|
||||
if let spoiler = item.spoiler, spoiler {
|
||||
attributes.append(MediaSpoilerMessageAttribute())
|
||||
}
|
||||
|
||||
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
||||
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
|
||||
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
||||
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
||||
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
||||
if let file = value.file {
|
||||
if let packId = value.interactivelySelectedFromPackId {
|
||||
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
||||
if context.sharedContext.immediateExperimentalUISettings.storiesJpegExperiment {
|
||||
let sizes: [Int32] = [2048, 1280]
|
||||
let formats: [MediaImageFormat] = [.jxl, .jpeg]
|
||||
let qualities: [Int32: [Int32]] = [
|
||||
MediaImageFormat.jxl.rawValue: [
|
||||
50,
|
||||
75
|
||||
],
|
||||
MediaImageFormat.jpeg.rawValue: [
|
||||
75
|
||||
]
|
||||
]
|
||||
for sizeSide in sizes {
|
||||
for format in formats {
|
||||
for quality in qualities[format.rawValue]! {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let resource = PhotoLibraryMediaResource(
|
||||
localIdentifier: asset.localIdentifier,
|
||||
uniqueId: Int64.random(in: Int64.min ... Int64.max),
|
||||
width: sizeSide,
|
||||
height: sizeSide,
|
||||
format: format,
|
||||
quality: quality
|
||||
)
|
||||
|
||||
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
|
||||
let scaledSize = size.aspectFittedOrSmaller(CGSize(width: CGFloat(sizeSide), height: CGFloat(sizeSide)))
|
||||
|
||||
let media: Media
|
||||
media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: format == .jxl ? "image/jxl" : "image/jpeg", size: nil, attributes: [
|
||||
.FileName(fileName: format == .jxl ? "image\(sizeSide)-q\(quality).jxl" : "image\(sizeSide)-q\(quality).jpg"),
|
||||
.ImageSize(size: PixelDimensions(scaledSize))
|
||||
])
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if let timer = item.timer, timer > 0 && timer <= 60 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
||||
}
|
||||
if let spoiler = item.spoiler, spoiler {
|
||||
attributes.append(MediaSpoilerMessageAttribute())
|
||||
}
|
||||
|
||||
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
||||
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
|
||||
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
||||
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
||||
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
||||
if let file = value.file {
|
||||
if let packId = value.interactivelySelectedFromPackId {
|
||||
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
||||
for entity in entities {
|
||||
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
||||
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
||||
bubbleUpEmojiOrStickersets.append(packId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: false))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
||||
for entity in entities {
|
||||
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
||||
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
||||
bubbleUpEmojiOrStickersets.append(packId)
|
||||
} else {
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let size = CGSize(width: CGFloat(asset.pixelWidth), height: CGFloat(asset.pixelHeight))
|
||||
let scaledSize = size.aspectFittedOrSmaller(CGSize(width: 1280.0, height: 1280.0))
|
||||
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: Int64.random(in: Int64.min ... Int64.max))
|
||||
|
||||
let media: Media
|
||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource, progressiveSizes: [], immediateThumbnailData: nil, hasVideo: false, isPersonal: false))
|
||||
media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
|
||||
|
||||
var attributes: [MessageAttribute] = []
|
||||
if let timer = item.timer, timer > 0 && timer <= 60 {
|
||||
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
|
||||
}
|
||||
if let spoiler = item.spoiler, spoiler {
|
||||
attributes.append(MediaSpoilerMessageAttribute())
|
||||
}
|
||||
|
||||
let text = trimChatInputText(convertMarkdownToAttributes(caption ?? NSAttributedString()))
|
||||
let entities = generateTextEntities(text.string, enabledTypes: .all, currentEntities: generateChatInputTextEntities(text))
|
||||
if !entities.isEmpty {
|
||||
attributes.append(TextEntitiesMessageAttribute(entities: entities))
|
||||
}
|
||||
|
||||
var bubbleUpEmojiOrStickersetsById: [Int64: ItemCollectionId] = [:]
|
||||
text.enumerateAttribute(ChatTextInputAttributes.customEmoji, in: NSRange(location: 0, length: text.length), using: { value, _, _ in
|
||||
if let value = value as? ChatTextInputTextCustomEmojiAttribute {
|
||||
if let file = value.file {
|
||||
if let packId = value.interactivelySelectedFromPackId {
|
||||
bubbleUpEmojiOrStickersetsById[file.fileId.id] = packId
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
var bubbleUpEmojiOrStickersets: [ItemCollectionId] = []
|
||||
for entity in entities {
|
||||
if case let .CustomEmoji(_, fileId) = entity.type {
|
||||
if let packId = bubbleUpEmojiOrStickersetsById[fileId] {
|
||||
if !bubbleUpEmojiOrStickersets.contains(packId) {
|
||||
bubbleUpEmojiOrStickersets.append(packId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: false))
|
||||
}
|
||||
|
||||
messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: false))
|
||||
case .tempFile:
|
||||
break
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ extension UIImage.Orientation {
|
||||
|
||||
private let fetchPhotoWorkers = ThreadPool(threadCount: 3, threadPriority: 0.2)
|
||||
|
||||
public func fetchPhotoLibraryResource(localIdentifier: String) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||
public func fetchPhotoLibraryResource(localIdentifier: String, width: Int32?, height: Int32?, format: MediaImageFormat?, quality: Int32?) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
||||
return Signal { subscriber in
|
||||
let queue = ThreadPoolQueue(threadPool: fetchPhotoWorkers)
|
||||
|
||||
@ -97,7 +97,12 @@ public func fetchPhotoLibraryResource(localIdentifier: String) -> Signal<MediaRe
|
||||
option.isNetworkAccessAllowed = true
|
||||
option.isSynchronous = false
|
||||
|
||||
let size = CGSize(width: 1280.0, height: 1280.0)
|
||||
let size: CGSize
|
||||
if let width, let height {
|
||||
size = CGSize(width: CGFloat(width), height: CGFloat(height))
|
||||
} else {
|
||||
size = CGSize(width: 1280.0, height: 1280.0)
|
||||
}
|
||||
|
||||
queue.addTask(ThreadPoolTask({ _ in
|
||||
let startTime = CACurrentMediaTime()
|
||||
@ -127,14 +132,27 @@ public func fetchPhotoLibraryResource(localIdentifier: String) -> Signal<MediaRe
|
||||
print("scaled completion \((CACurrentMediaTime() - startTime) * 1000.0) ms")
|
||||
#endif
|
||||
|
||||
if let scaledImage = scaledImage, let data = compressImageToJPEG(scaledImage, quality: 0.6) {
|
||||
#if DEBUG
|
||||
print("compression completion \((CACurrentMediaTime() - startTime) * 1000.0) ms")
|
||||
#endif
|
||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: data, range: 0 ..< Int64(data.count), complete: true))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putCompletion()
|
||||
switch format {
|
||||
case .none, .jpeg:
|
||||
if let scaledImage = scaledImage, let data = compressImageToJPEG(scaledImage, quality: 0.6) {
|
||||
#if DEBUG
|
||||
print("compression completion \((CACurrentMediaTime() - startTime) * 1000.0) ms")
|
||||
#endif
|
||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: data, range: 0 ..< Int64(data.count), complete: true))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
case .jxl:
|
||||
if let scaledImage = scaledImage, let data = compressImageToJPEGXL(scaledImage, quality: Int(quality ?? 75)) {
|
||||
#if DEBUG
|
||||
print("jpegxl compression completion \((CACurrentMediaTime() - startTime) * 1000.0) ms")
|
||||
#endif
|
||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: data, range: 0 ..< Int64(data.count), complete: true))
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
}
|
||||
semaphore.signal()
|
||||
}
|
||||
|
@ -220,6 +220,11 @@ public struct PhotoLibraryMediaResourceId {
|
||||
}
|
||||
}
|
||||
|
||||
public enum MediaImageFormat: Int32 {
|
||||
case jpeg
|
||||
case jxl
|
||||
}
|
||||
|
||||
public class PhotoLibraryMediaResource: TelegramMediaResource {
|
||||
public var size: Int64? {
|
||||
return nil
|
||||
@ -227,20 +232,52 @@ public class PhotoLibraryMediaResource: TelegramMediaResource {
|
||||
|
||||
public let localIdentifier: String
|
||||
public let uniqueId: Int64
|
||||
public let width: Int32?
|
||||
public let height: Int32?
|
||||
public let format: MediaImageFormat?
|
||||
public let quality: Int32?
|
||||
|
||||
public init(localIdentifier: String, uniqueId: Int64) {
|
||||
public init(localIdentifier: String, uniqueId: Int64, width: Int32? = nil, height: Int32? = nil, format: MediaImageFormat? = nil, quality: Int32? = nil) {
|
||||
self.localIdentifier = localIdentifier
|
||||
self.uniqueId = uniqueId
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.format = format
|
||||
self.quality = quality
|
||||
}
|
||||
|
||||
public required init(decoder: PostboxDecoder) {
|
||||
self.localIdentifier = decoder.decodeStringForKey("i", orElse: "")
|
||||
self.uniqueId = decoder.decodeInt64ForKey("uid", orElse: 0)
|
||||
self.width = decoder.decodeOptionalInt32ForKey("w")
|
||||
self.height = decoder.decodeOptionalInt32ForKey("h")
|
||||
self.format = decoder.decodeOptionalInt32ForKey("f").flatMap(MediaImageFormat.init(rawValue:))
|
||||
self.quality = decoder.decodeOptionalInt32ForKey("q")
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeString(self.localIdentifier, forKey: "i")
|
||||
encoder.encodeInt64(self.uniqueId, forKey: "uid")
|
||||
if let width = self.width {
|
||||
encoder.encodeInt32(width, forKey: "w")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "w")
|
||||
}
|
||||
if let height = self.height {
|
||||
encoder.encodeInt32(height, forKey: "h")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "h")
|
||||
}
|
||||
if let format = self.format {
|
||||
encoder.encodeInt32(format.rawValue, forKey: "f")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "f")
|
||||
}
|
||||
if let quality = self.quality {
|
||||
encoder.encodeInt32(quality, forKey: "q")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "q")
|
||||
}
|
||||
}
|
||||
|
||||
public var id: MediaResourceId {
|
||||
@ -249,7 +286,25 @@ public class PhotoLibraryMediaResource: TelegramMediaResource {
|
||||
|
||||
public func isEqual(to: MediaResource) -> Bool {
|
||||
if let to = to as? PhotoLibraryMediaResource {
|
||||
return self.localIdentifier == to.localIdentifier && self.uniqueId == to.uniqueId
|
||||
if self.localIdentifier != to.localIdentifier {
|
||||
return false
|
||||
}
|
||||
if self.uniqueId != to.uniqueId {
|
||||
return false
|
||||
}
|
||||
if self.width != to.width {
|
||||
return false
|
||||
}
|
||||
if self.height != to.height {
|
||||
return false
|
||||
}
|
||||
if self.format != to.format {
|
||||
return false
|
||||
}
|
||||
if self.quality != to.quality {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ objc_library(
|
||||
enable_modules = True,
|
||||
srcs = glob([
|
||||
"Sources/**/*.m",
|
||||
"Sources/**/*.mm",
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
hdrs = glob([
|
||||
@ -15,6 +16,7 @@ objc_library(
|
||||
],
|
||||
deps = [
|
||||
"//third-party/mozjpeg:mozjpeg",
|
||||
"//third-party/libjxl:jxl",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -1,6 +1,17 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage);
|
||||
NSArray<NSNumber *> * _Nonnull extractJPEGDataScans(NSData * _Nonnull data);
|
||||
NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, CGSize size);
|
||||
UIImage * _Nullable decompressImage(NSData * _Nonnull sourceData);
|
||||
|
||||
NSData * _Nullable compressJPEGXLData(UIImage * _Nonnull sourceImage, int quality);
|
||||
UIImage * _Nullable decompressJPEGXLData(NSData * _Nonnull data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,335 +0,0 @@
|
||||
#import <MozjpegBinding/MozjpegBinding.h>
|
||||
|
||||
#import <mozjpeg/turbojpeg.h>
|
||||
#import <mozjpeg/jpeglib.h>
|
||||
#import <Accelerate/Accelerate.h>
|
||||
|
||||
static NSData *getHeaderPattern() {
|
||||
static NSData *value = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
value = [[NSData alloc] initWithBase64EncodedString:@"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACgcHiMeGSgjISMtKygwPGRBPDc3PHtYXUlkkYCZlo+AjIqgtObDoKrarYqMyP/L2u71////m8H////6/+b9//j/2wBDASstLTw1PHZBQXb4pYyl+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj/wAARCAAAAAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwA=" options:0];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
static NSData *getFooterPattern() {
|
||||
static NSData *value = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
value = [[NSData alloc] initWithBase64EncodedString:@"/9k=" options:0];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
NSArray<NSNumber *> * _Nonnull extractJPEGDataScans(NSData * _Nonnull data) {
|
||||
NSMutableArray<NSNumber *> *result = [[NSMutableArray alloc] init];
|
||||
|
||||
const uint8_t *dataBytes = data.bytes;
|
||||
int offset = 0;
|
||||
while (offset < data.length) {
|
||||
bool found = false;
|
||||
for (int i = offset + 2; i < data.length - 1; i++) {
|
||||
if (dataBytes[i] == 0xffU && dataBytes[i + 1] == 0xdaU) {
|
||||
if (offset != 0) {
|
||||
[result addObject:@(i)];
|
||||
}
|
||||
offset = i;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
static NSString *sessionPrefix = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sessionPrefix = [NSString stringWithFormat:@"%u", arc4random()];
|
||||
});
|
||||
|
||||
NSString *randomId = [NSString stringWithFormat:@"%u", arc4random()];
|
||||
NSString *dirPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:sessionPrefix] stringByAppendingPathComponent:randomId];
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:nil error:nil];
|
||||
for (int i = 0; i < result.count + 1; i++) {
|
||||
NSString *filePath = [dirPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.jpg", i]];
|
||||
if (i == result.count) {
|
||||
[data writeToFile:filePath atomically:true];
|
||||
} else {
|
||||
[[data subdataWithRange:NSMakeRange(0, [result[i] intValue])] writeToFile:filePath atomically:true];
|
||||
}
|
||||
}
|
||||
NSLog(@"Path: %@", dirPath);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage) {
|
||||
int width = (int)(sourceImage.size.width * sourceImage.scale);
|
||||
int height = (int)(sourceImage.size.height * sourceImage.scale);
|
||||
|
||||
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
|
||||
uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height));
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
|
||||
|
||||
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
||||
|
||||
UIGraphicsPushContext(targetContext);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), sourceImage.CGImage);
|
||||
|
||||
UIGraphicsPopContext();
|
||||
|
||||
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
|
||||
uint8_t *buffer = malloc(bufferBytesPerRow * height);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
|
||||
|
||||
uint32_t r = ((*color >> 16) & 0xff);
|
||||
uint32_t g = ((*color >> 8) & 0xff);
|
||||
uint32_t b = (*color & 0xff);
|
||||
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
CGContextRelease(targetContext);
|
||||
|
||||
free(targetMemory);
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
uint8_t *outBuffer = NULL;
|
||||
unsigned long outSize = 0;
|
||||
jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
|
||||
|
||||
cinfo.image_width = (uint32_t)width;
|
||||
cinfo.image_height = (uint32_t)height;
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
|
||||
jpeg_set_defaults(&cinfo);
|
||||
cinfo.arith_code = FALSE;
|
||||
cinfo.dct_method = JDCT_ISLOW;
|
||||
cinfo.optimize_coding = TRUE;
|
||||
jpeg_set_quality(&cinfo, 72, 1);
|
||||
jpeg_simple_progression(&cinfo);
|
||||
jpeg_start_compress(&cinfo, 1);
|
||||
|
||||
JSAMPROW rowPointer[1];
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
rowPointer[0] = (JSAMPROW)(buffer + cinfo.next_scanline * bufferBytesPerRow);
|
||||
jpeg_write_scanlines(&cinfo, rowPointer, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
NSData *result = [[NSData alloc] initWithBytes:outBuffer length:outSize];
|
||||
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, CGSize size) {
|
||||
CGSize fittedSize = image.size;
|
||||
if (fittedSize.width > size.width) {
|
||||
fittedSize = CGSizeMake(size.width, (int)((fittedSize.height * size.width / MAX(fittedSize.width, 1.0f))));
|
||||
}
|
||||
if (fittedSize.height > size.height) {
|
||||
fittedSize = CGSizeMake((int)((fittedSize.width * size.height / MAX(fittedSize.height, 1.0f))), size.height);
|
||||
}
|
||||
|
||||
int width = (int)fittedSize.width;
|
||||
int height = (int)fittedSize.height;
|
||||
|
||||
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
|
||||
uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height));
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
|
||||
|
||||
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
||||
|
||||
UIGraphicsPushContext(targetContext);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), image.CGImage);
|
||||
|
||||
UIGraphicsPopContext();
|
||||
|
||||
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
|
||||
uint8_t *buffer = malloc(bufferBytesPerRow * height);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
|
||||
|
||||
uint32_t r = ((*color >> 16) & 0xff);
|
||||
uint32_t g = ((*color >> 8) & 0xff);
|
||||
uint32_t b = (*color & 0xff);
|
||||
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
CGContextRelease(targetContext);
|
||||
|
||||
free(targetMemory);
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
uint8_t *outBuffer = NULL;
|
||||
unsigned long outSize = 0;
|
||||
jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
|
||||
|
||||
cinfo.image_width = (uint32_t)width;
|
||||
cinfo.image_height = (uint32_t)height;
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
|
||||
jpeg_set_defaults(&cinfo);
|
||||
cinfo.arith_code = FALSE;
|
||||
cinfo.dct_method = JDCT_ISLOW;
|
||||
cinfo.optimize_coding = FALSE;
|
||||
jpeg_set_quality(&cinfo, 20, 1);
|
||||
jpeg_start_compress(&cinfo, 1);
|
||||
|
||||
JSAMPROW rowPointer[1];
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
rowPointer[0] = (JSAMPROW)(buffer + cinfo.next_scanline * bufferBytesPerRow);
|
||||
jpeg_write_scanlines(&cinfo, rowPointer, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
NSMutableData *serializedData = nil;
|
||||
|
||||
NSData *headerPattern = getHeaderPattern();
|
||||
NSData *footerPattern = getFooterPattern();
|
||||
if (outBuffer[164] == height && outBuffer[166] == width && headerPattern != nil && footerPattern != nil) {
|
||||
outBuffer[164] = 0;
|
||||
outBuffer[166] = 0;
|
||||
|
||||
if (memcmp(headerPattern.bytes, outBuffer, headerPattern.length) == 0) {
|
||||
if (memcmp(footerPattern.bytes, outBuffer + outSize - footerPattern.length, footerPattern.length) == 0) {
|
||||
serializedData = [[NSMutableData alloc] init];
|
||||
uint8_t version = 1;
|
||||
[serializedData appendBytes:&version length:1];
|
||||
uint8_t outWidth = (uint8_t)width;
|
||||
uint8_t outHeight = (uint8_t)height;
|
||||
[serializedData appendBytes:&outHeight length:1];
|
||||
[serializedData appendBytes:&outWidth length:1];
|
||||
unsigned long contentSize = outSize - headerPattern.length - footerPattern.length;
|
||||
[serializedData appendBytes:outBuffer + headerPattern.length length:contentSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return serializedData;
|
||||
}
|
||||
|
||||
UIImage * _Nullable decompressImage(NSData * _Nonnull sourceData) {
|
||||
long unsigned int jpegSize = sourceData.length;
|
||||
unsigned char *_compressedImage = (unsigned char *)sourceData.bytes;
|
||||
|
||||
int jpegSubsamp, width, height;
|
||||
|
||||
tjhandle _jpegDecompressor = tjInitDecompress();
|
||||
|
||||
if (tjDecompressHeader2(_jpegDecompressor, _compressedImage, jpegSize, &width, &height, &jpegSubsamp) != 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
int sourceBytesPerRow = (3 * width + 31) & ~0x1F;
|
||||
int targetBytesPerRow = (4 * width + 31) & ~0x1F;
|
||||
|
||||
unsigned char *buffer = malloc(sourceBytesPerRow * height);
|
||||
|
||||
tjDecompress2(_jpegDecompressor, _compressedImage, jpegSize, buffer, width, sourceBytesPerRow, height, TJPF_RGB, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE);
|
||||
|
||||
tjDestroy(_jpegDecompressor);
|
||||
|
||||
vImage_Buffer source;
|
||||
source.width = width;
|
||||
source.height = height;
|
||||
source.rowBytes = sourceBytesPerRow;
|
||||
source.data = buffer;
|
||||
|
||||
vImage_Buffer target;
|
||||
target.width = width;
|
||||
target.height = height;
|
||||
target.rowBytes = targetBytesPerRow;
|
||||
|
||||
unsigned char *targetBuffer = malloc(targetBytesPerRow * height);
|
||||
target.data = targetBuffer;
|
||||
|
||||
vImageConvert_RGB888toARGB8888(&source, nil, 0xff, &target, false, kvImageDoNotTile);
|
||||
|
||||
free(buffer);
|
||||
|
||||
vImage_Buffer permuteTarget;
|
||||
permuteTarget.width = width;
|
||||
permuteTarget.height = height;
|
||||
permuteTarget.rowBytes = targetBytesPerRow;
|
||||
|
||||
unsigned char *permuteTargetBuffer = malloc(targetBytesPerRow * height);
|
||||
permuteTarget.data = permuteTargetBuffer;
|
||||
|
||||
const uint8_t permuteMap[4] = {3,2,1,0};
|
||||
vImagePermuteChannels_ARGB8888(&target, &permuteTarget, permuteMap, kvImageDoNotTile);
|
||||
|
||||
free(targetBuffer);
|
||||
|
||||
NSData *resultData = [[NSData alloc] initWithBytesNoCopy:permuteTargetBuffer length:targetBytesPerRow * height deallocator:^(void * _Nonnull bytes, __unused NSUInteger length) {
|
||||
free(bytes);
|
||||
}];
|
||||
|
||||
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)resultData);
|
||||
|
||||
static CGColorSpaceRef imageColorSpace;
|
||||
static CGBitmapInfo bitmapInfo;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0);
|
||||
UIImage *refImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
imageColorSpace = CGColorSpaceRetain(CGImageGetColorSpace(refImage.CGImage));
|
||||
bitmapInfo = CGImageGetBitmapInfo(refImage.CGImage);
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
|
||||
CGImageRef cgImg = CGImageCreate(width, height, 8, 32, targetBytesPerRow, imageColorSpace, bitmapInfo, dataProvider, NULL, true, kCGRenderingIntentDefault);
|
||||
|
||||
CGDataProviderRelease(dataProvider);
|
||||
|
||||
UIImage *resultImage = [[UIImage alloc] initWithCGImage:cgImg];
|
||||
CGImageRelease(cgImg);
|
||||
|
||||
return resultImage;
|
||||
}
|
780
submodules/MozjpegBinding/Sources/MozjpegBinding.mm
Normal file
780
submodules/MozjpegBinding/Sources/MozjpegBinding.mm
Normal file
@ -0,0 +1,780 @@
|
||||
#import <MozjpegBinding/MozjpegBinding.h>
|
||||
|
||||
#define USE_JPEGLI false
|
||||
|
||||
#import <mozjpeg/turbojpeg.h>
|
||||
#import <mozjpeg/jpeglib.h>
|
||||
|
||||
#import <Accelerate/Accelerate.h>
|
||||
|
||||
#include <jxl/encode.h>
|
||||
#include <jxl/encode_cxx.h>
|
||||
#include <jxl/decode.h>
|
||||
#include <jxl/decode_cxx.h>
|
||||
//#include <jxl/thread_parallel_runner.h>
|
||||
//#include <jxl/thread_parallel_runner_cxx.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
static inline float JXLGetDistance(int32_t quality) {
|
||||
if (quality == 0) {
|
||||
return 1.0f;
|
||||
} else if (quality >= 30) {
|
||||
return 0.1f + (float)(100 - MIN(100, quality)) * 0.09f;
|
||||
} else {
|
||||
return 6.24f + (float)pow(2.5f, (30.0 - quality) / 5.0) / 6.25f;
|
||||
}
|
||||
}
|
||||
|
||||
NSData * _Nullable compressJPEGXLData(UIImage * _Nonnull sourceImage, int quality) {
|
||||
int width = (int)(sourceImage.size.width * sourceImage.scale);
|
||||
int height = (int)(sourceImage.size.height * sourceImage.scale);
|
||||
|
||||
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
|
||||
uint8_t *targetMemory = (uint8_t *)malloc((int)(targetBytesPerRow * height));
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
|
||||
|
||||
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
||||
|
||||
UIGraphicsPushContext(targetContext);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), sourceImage.CGImage);
|
||||
|
||||
UIGraphicsPopContext();
|
||||
|
||||
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
|
||||
int bufferSize = bufferBytesPerRow * height;
|
||||
uint8_t *buffer = (uint8_t *)malloc(bufferBytesPerRow * height);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
|
||||
|
||||
uint32_t r = ((*color >> 16) & 0xff);
|
||||
uint32_t g = ((*color >> 8) & 0xff);
|
||||
uint32_t b = (*color & 0xff);
|
||||
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
CGContextRelease(targetContext);
|
||||
|
||||
free(targetMemory);
|
||||
|
||||
auto enc = JxlEncoderMake(nullptr);
|
||||
|
||||
JxlPixelFormat pixel_format = {3, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 16};
|
||||
|
||||
JxlBasicInfo basic_info;
|
||||
JxlEncoderInitBasicInfo(&basic_info);
|
||||
basic_info.xsize = width;
|
||||
basic_info.ysize = height;
|
||||
basic_info.bits_per_sample = 32;
|
||||
basic_info.exponent_bits_per_sample = 8;
|
||||
basic_info.uses_original_profile = JXL_FALSE;
|
||||
if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) {
|
||||
free(buffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
JxlColorEncoding color_encoding = {};
|
||||
JxlColorEncodingSetToSRGB(&color_encoding,
|
||||
/*is_gray=*/pixel_format.num_channels < 3);
|
||||
if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) {
|
||||
free(buffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
JxlEncoderFrameSettings* frame_settings =
|
||||
JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
|
||||
|
||||
JxlEncoderSetFrameDistance(frame_settings, JXLGetDistance(quality));
|
||||
JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 8);
|
||||
|
||||
if (JXL_ENC_SUCCESS != JxlEncoderAddImageFrame(frame_settings, &pixel_format, buffer, bufferSize)) {
|
||||
free(buffer);
|
||||
return nil;
|
||||
}
|
||||
JxlEncoderCloseInput(enc.get());
|
||||
|
||||
NSMutableData *result = [[NSMutableData alloc] initWithLength:64];
|
||||
uint8_t *next_out = (uint8_t *)result.mutableBytes;
|
||||
size_t avail_out = result.length - (next_out - ((uint8_t *)result.mutableBytes));
|
||||
|
||||
JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT;
|
||||
while (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
|
||||
process_result = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out);
|
||||
if (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
|
||||
size_t offset = next_out - ((uint8_t *)result.mutableBytes);
|
||||
[result setLength:result.length * 2];
|
||||
next_out = ((uint8_t *)result.mutableBytes) + offset;
|
||||
avail_out = result.length - offset;
|
||||
}
|
||||
}
|
||||
[result setLength:next_out - ((uint8_t *)result.mutableBytes)];
|
||||
if (JXL_ENC_SUCCESS != process_result) {
|
||||
free(buffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
return result;
|
||||
|
||||
/*auto runner = JxlThreadParallelRunnerMake(
|
||||
nullptr,
|
||||
8);
|
||||
if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(),
|
||||
JxlThreadParallelRunner,
|
||||
runner.get())) {
|
||||
fprintf(stderr, "JxlEncoderSetParallelRunner failed\n");
|
||||
return false;
|
||||
}*/
|
||||
}
|
||||
|
||||
UIImage * _Nullable decompressJPEGXLData(NSData * _Nonnull data) {
|
||||
//const uint8_t* jxl, size_t size, std::vector<float>* pixels, size_t* xsize, size_t* ysize, std::vector<uint8_t>* icc_profile
|
||||
|
||||
auto dec = JxlDecoderMake(nullptr);
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(), JxlResizableParallelRunner, runner.get())) {
|
||||
fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
|
||||
return false;
|
||||
}*/
|
||||
|
||||
JxlBasicInfo info;
|
||||
JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
|
||||
|
||||
JxlDecoderSetInput(dec.get(), (uint8_t const *)data.bytes, data.length);
|
||||
JxlDecoderCloseInput(dec.get());
|
||||
|
||||
int xsize = 0;
|
||||
int ysize = 0;
|
||||
std::vector<uint8_t> icc_profile;
|
||||
|
||||
std::vector<uint8_t> pixels;
|
||||
|
||||
while (true) {
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
|
||||
|
||||
if (status == JXL_DEC_ERROR) {
|
||||
return nil;
|
||||
} else if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
return nil;
|
||||
} else if (status == JXL_DEC_BASIC_INFO) {
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
|
||||
return nil;
|
||||
}
|
||||
xsize = info.xsize;
|
||||
ysize = info.ysize;
|
||||
//JxlResizableParallelRunnerSetThreads(runner.get(), JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
|
||||
} else if (status == JXL_DEC_COLOR_ENCODING) {
|
||||
// Get the ICC color profile of the pixel data
|
||||
size_t icc_size;
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetICCProfileSize(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA, &icc_size)) {
|
||||
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
|
||||
return nil;
|
||||
}
|
||||
icc_profile.resize(icc_size);
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA, icc_profile.data(), icc_profile.size())) {
|
||||
return nil;
|
||||
}
|
||||
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
|
||||
size_t buffer_size;
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
|
||||
return nil;
|
||||
}
|
||||
if (buffer_size != xsize * ysize * 16) {
|
||||
return nil;
|
||||
}
|
||||
pixels.resize(xsize * ysize * 4);
|
||||
void* pixels_buffer = (void*)pixels.data();
|
||||
size_t pixels_buffer_size = pixels.size() * sizeof(float);
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, pixels_buffer, pixels_buffer_size)) {
|
||||
return nil;
|
||||
}
|
||||
} else if (status == JXL_DEC_FULL_IMAGE) {
|
||||
// Nothing to do. Do not yet return. If the image is an animation, more
|
||||
// full frames may be decoded. This example only keeps the last one.
|
||||
} else if (status == JXL_DEC_SUCCESS) {
|
||||
// All decoding successfully finished.
|
||||
// It's not required to call JxlDecoderReleaseInput(dec.get()) here since
|
||||
// the decoder will be destroyed.
|
||||
|
||||
int targetBytesPerRow = xsize * 4;
|
||||
uint8_t *permuteTargetBuffer = (uint8_t *)malloc(targetBytesPerRow * ysize);
|
||||
memcpy(permuteTargetBuffer, pixels.data(), pixels.size());
|
||||
|
||||
NSData *resultData = [[NSData alloc] initWithBytesNoCopy:permuteTargetBuffer length:targetBytesPerRow * ysize deallocator:^(void * _Nonnull bytes, __unused NSUInteger length) {
|
||||
free(bytes);
|
||||
}];
|
||||
|
||||
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)resultData);
|
||||
|
||||
static CGColorSpaceRef imageColorSpace;
|
||||
static CGBitmapInfo bitmapInfo;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0);
|
||||
UIImage *refImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
imageColorSpace = CGColorSpaceRetain(CGImageGetColorSpace(refImage.CGImage));
|
||||
bitmapInfo = CGImageGetBitmapInfo(refImage.CGImage);
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
|
||||
CGImageRef cgImg = CGImageCreate(xsize, ysize, 8, 32, targetBytesPerRow, imageColorSpace, bitmapInfo, dataProvider, NULL, true, kCGRenderingIntentDefault);
|
||||
|
||||
CGDataProviderRelease(dataProvider);
|
||||
|
||||
UIImage *resultImage = [[UIImage alloc] initWithCGImage:cgImg];
|
||||
CGImageRelease(cgImg);
|
||||
|
||||
return resultImage;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSData *getHeaderPattern() {
|
||||
static NSData *value = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
value = [[NSData alloc] initWithBase64EncodedString:@"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACgcHiMeGSgjISMtKygwPGRBPDc3PHtYXUlkkYCZlo+AjIqgtObDoKrarYqMyP/L2u71////m8H////6/+b9//j/2wBDASstLTw1PHZBQXb4pYyl+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj/wAARCAAAAAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwA=" options:0];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
static NSData *getFooterPattern() {
|
||||
static NSData *value = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
value = [[NSData alloc] initWithBase64EncodedString:@"/9k=" options:0];
|
||||
});
|
||||
return value;
|
||||
}
|
||||
|
||||
NSArray<NSNumber *> * _Nonnull extractJPEGDataScans(NSData * _Nonnull data) {
|
||||
NSMutableArray<NSNumber *> *result = [[NSMutableArray alloc] init];
|
||||
|
||||
const uint8_t *dataBytes = (const uint8_t *)data.bytes;
|
||||
int offset = 0;
|
||||
while (offset < data.length) {
|
||||
bool found = false;
|
||||
for (int i = offset + 2; i < data.length - 1; i++) {
|
||||
if (dataBytes[i] == 0xffU && dataBytes[i + 1] == 0xdaU) {
|
||||
if (offset != 0) {
|
||||
[result addObject:@(i)];
|
||||
}
|
||||
offset = i;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
static NSString *sessionPrefix = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sessionPrefix = [NSString stringWithFormat:@"%u", arc4random()];
|
||||
});
|
||||
|
||||
NSString *randomId = [NSString stringWithFormat:@"%u", arc4random()];
|
||||
NSString *dirPath = [[NSTemporaryDirectory() stringByAppendingPathComponent:sessionPrefix] stringByAppendingPathComponent:randomId];
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:nil error:nil];
|
||||
for (int i = 0; i < result.count + 1; i++) {
|
||||
NSString *filePath = [dirPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%d.jpg", i]];
|
||||
if (i == result.count) {
|
||||
[data writeToFile:filePath atomically:true];
|
||||
} else {
|
||||
[[data subdataWithRange:NSMakeRange(0, [result[i] intValue])] writeToFile:filePath atomically:true];
|
||||
}
|
||||
}
|
||||
NSLog(@"Path: %@", dirPath);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if USE_JPEGLI
|
||||
NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage) {
|
||||
int width = (int)(sourceImage.size.width * sourceImage.scale);
|
||||
int height = (int)(sourceImage.size.height * sourceImage.scale);
|
||||
|
||||
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
|
||||
uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height));
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
|
||||
|
||||
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
||||
|
||||
UIGraphicsPushContext(targetContext);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), sourceImage.CGImage);
|
||||
|
||||
UIGraphicsPopContext();
|
||||
|
||||
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
|
||||
uint8_t *buffer = malloc(bufferBytesPerRow * height);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
|
||||
|
||||
uint32_t r = ((*color >> 16) & 0xff);
|
||||
uint32_t g = ((*color >> 8) & 0xff);
|
||||
uint32_t b = (*color & 0xff);
|
||||
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
CGContextRelease(targetContext);
|
||||
|
||||
free(targetMemory);
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
uint8_t *outBuffer = NULL;
|
||||
unsigned long outSize = 0;
|
||||
jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
|
||||
|
||||
cinfo.image_width = (uint32_t)width;
|
||||
cinfo.image_height = (uint32_t)height;
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
//jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
|
||||
jpeg_set_defaults(&cinfo);
|
||||
cinfo.arith_code = FALSE;
|
||||
cinfo.dct_method = JDCT_ISLOW;
|
||||
cinfo.optimize_coding = TRUE;
|
||||
jpeg_set_quality(&cinfo, 72, 1);
|
||||
jpeg_simple_progression(&cinfo);
|
||||
jpeg_start_compress(&cinfo, 1);
|
||||
|
||||
JSAMPROW rowPointer[1];
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
rowPointer[0] = (JSAMPROW)(buffer + cinfo.next_scanline * bufferBytesPerRow);
|
||||
jpeg_write_scanlines(&cinfo, rowPointer, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
NSData *result = [[NSData alloc] initWithBytes:outBuffer length:outSize];
|
||||
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage) {
|
||||
int width = (int)(sourceImage.size.width * sourceImage.scale);
|
||||
int height = (int)(sourceImage.size.height * sourceImage.scale);
|
||||
|
||||
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
|
||||
uint8_t *targetMemory = (uint8_t *)malloc((int)(targetBytesPerRow * height));
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
|
||||
|
||||
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
||||
|
||||
UIGraphicsPushContext(targetContext);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), sourceImage.CGImage);
|
||||
|
||||
UIGraphicsPopContext();
|
||||
|
||||
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
|
||||
uint8_t *buffer = (uint8_t *)malloc(bufferBytesPerRow * height);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
|
||||
|
||||
uint32_t r = ((*color >> 16) & 0xff);
|
||||
uint32_t g = ((*color >> 8) & 0xff);
|
||||
uint32_t b = (*color & 0xff);
|
||||
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
CGContextRelease(targetContext);
|
||||
|
||||
free(targetMemory);
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
uint8_t *outBuffer = NULL;
|
||||
unsigned long outSize = 0;
|
||||
jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
|
||||
|
||||
cinfo.image_width = (uint32_t)width;
|
||||
cinfo.image_height = (uint32_t)height;
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
|
||||
jpeg_set_defaults(&cinfo);
|
||||
cinfo.arith_code = FALSE;
|
||||
cinfo.dct_method = JDCT_ISLOW;
|
||||
cinfo.optimize_coding = TRUE;
|
||||
jpeg_set_quality(&cinfo, 72, 1);
|
||||
jpeg_simple_progression(&cinfo);
|
||||
jpeg_start_compress(&cinfo, 1);
|
||||
|
||||
JSAMPROW rowPointer[1];
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
rowPointer[0] = (JSAMPROW)(buffer + cinfo.next_scanline * bufferBytesPerRow);
|
||||
jpeg_write_scanlines(&cinfo, rowPointer, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
NSData *result = [[NSData alloc] initWithBytes:outBuffer length:outSize];
|
||||
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_JPEGLI
|
||||
NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, CGSize size) {
|
||||
CGSize fittedSize = image.size;
|
||||
if (fittedSize.width > size.width) {
|
||||
fittedSize = CGSizeMake(size.width, (int)((fittedSize.height * size.width / MAX(fittedSize.width, 1.0f))));
|
||||
}
|
||||
if (fittedSize.height > size.height) {
|
||||
fittedSize = CGSizeMake((int)((fittedSize.width * size.height / MAX(fittedSize.height, 1.0f))), size.height);
|
||||
}
|
||||
|
||||
int width = (int)fittedSize.width;
|
||||
int height = (int)fittedSize.height;
|
||||
|
||||
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
|
||||
uint8_t *targetMemory = malloc((int)(targetBytesPerRow * height));
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
|
||||
|
||||
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
||||
|
||||
UIGraphicsPushContext(targetContext);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), image.CGImage);
|
||||
|
||||
UIGraphicsPopContext();
|
||||
|
||||
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
|
||||
uint8_t *buffer = malloc(bufferBytesPerRow * height);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
|
||||
|
||||
uint32_t r = ((*color >> 16) & 0xff);
|
||||
uint32_t g = ((*color >> 8) & 0xff);
|
||||
uint32_t b = (*color & 0xff);
|
||||
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
CGContextRelease(targetContext);
|
||||
|
||||
free(targetMemory);
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
uint8_t *outBuffer = NULL;
|
||||
unsigned long outSize = 0;
|
||||
jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
|
||||
|
||||
cinfo.image_width = (uint32_t)width;
|
||||
cinfo.image_height = (uint32_t)height;
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
//jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
|
||||
jpeg_set_defaults(&cinfo);
|
||||
cinfo.arith_code = FALSE;
|
||||
cinfo.dct_method = JDCT_ISLOW;
|
||||
cinfo.optimize_coding = FALSE;
|
||||
jpeg_set_quality(&cinfo, 20, 1);
|
||||
jpeg_start_compress(&cinfo, 1);
|
||||
|
||||
JSAMPROW rowPointer[1];
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
rowPointer[0] = (JSAMPROW)(buffer + cinfo.next_scanline * bufferBytesPerRow);
|
||||
jpeg_write_scanlines(&cinfo, rowPointer, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
NSMutableData *serializedData = nil;
|
||||
|
||||
NSData *headerPattern = getHeaderPattern();
|
||||
NSData *footerPattern = getFooterPattern();
|
||||
if (outBuffer[164] == height && outBuffer[166] == width && headerPattern != nil && footerPattern != nil) {
|
||||
outBuffer[164] = 0;
|
||||
outBuffer[166] = 0;
|
||||
|
||||
if (memcmp(headerPattern.bytes, outBuffer, headerPattern.length) == 0) {
|
||||
if (memcmp(footerPattern.bytes, outBuffer + outSize - footerPattern.length, footerPattern.length) == 0) {
|
||||
serializedData = [[NSMutableData alloc] init];
|
||||
uint8_t version = 1;
|
||||
[serializedData appendBytes:&version length:1];
|
||||
uint8_t outWidth = (uint8_t)width;
|
||||
uint8_t outHeight = (uint8_t)height;
|
||||
[serializedData appendBytes:&outHeight length:1];
|
||||
[serializedData appendBytes:&outWidth length:1];
|
||||
unsigned long contentSize = outSize - headerPattern.length - footerPattern.length;
|
||||
[serializedData appendBytes:outBuffer + headerPattern.length length:contentSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return serializedData;
|
||||
}
|
||||
#else
|
||||
NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, CGSize size) {
|
||||
CGSize fittedSize = image.size;
|
||||
if (fittedSize.width > size.width) {
|
||||
fittedSize = CGSizeMake(size.width, (int)((fittedSize.height * size.width / MAX(fittedSize.width, 1.0f))));
|
||||
}
|
||||
if (fittedSize.height > size.height) {
|
||||
fittedSize = CGSizeMake((int)((fittedSize.width * size.height / MAX(fittedSize.height, 1.0f))), size.height);
|
||||
}
|
||||
|
||||
int width = (int)fittedSize.width;
|
||||
int height = (int)fittedSize.height;
|
||||
|
||||
int targetBytesPerRow = ((4 * (int)width) + 31) & (~31);
|
||||
uint8_t *targetMemory = (uint8_t *)malloc((int)(targetBytesPerRow * height));
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
|
||||
|
||||
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, width, height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
|
||||
|
||||
UIGraphicsPushContext(targetContext);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGContextDrawImage(targetContext, CGRectMake(0, 0, width, height), image.CGImage);
|
||||
|
||||
UIGraphicsPopContext();
|
||||
|
||||
int bufferBytesPerRow = ((3 * (int)width) + 31) & (~31);
|
||||
uint8_t *buffer = (uint8_t *)malloc(bufferBytesPerRow * height);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
|
||||
|
||||
uint32_t r = ((*color >> 16) & 0xff);
|
||||
uint32_t g = ((*color >> 8) & 0xff);
|
||||
uint32_t b = (*color & 0xff);
|
||||
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 0] = r;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 1] = g;
|
||||
buffer[y * bufferBytesPerRow + x * 3 + 2] = b;
|
||||
}
|
||||
}
|
||||
|
||||
CGContextRelease(targetContext);
|
||||
|
||||
free(targetMemory);
|
||||
|
||||
struct jpeg_compress_struct cinfo;
|
||||
struct jpeg_error_mgr jerr;
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
jpeg_create_compress(&cinfo);
|
||||
|
||||
uint8_t *outBuffer = NULL;
|
||||
unsigned long outSize = 0;
|
||||
jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
|
||||
|
||||
cinfo.image_width = (uint32_t)width;
|
||||
cinfo.image_height = (uint32_t)height;
|
||||
cinfo.input_components = 3;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
jpeg_c_set_int_param(&cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST);
|
||||
jpeg_set_defaults(&cinfo);
|
||||
cinfo.arith_code = FALSE;
|
||||
cinfo.dct_method = JDCT_ISLOW;
|
||||
cinfo.optimize_coding = FALSE;
|
||||
jpeg_set_quality(&cinfo, 20, 1);
|
||||
jpeg_start_compress(&cinfo, 1);
|
||||
|
||||
JSAMPROW rowPointer[1];
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
rowPointer[0] = (JSAMPROW)(buffer + cinfo.next_scanline * bufferBytesPerRow);
|
||||
jpeg_write_scanlines(&cinfo, rowPointer, 1);
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&cinfo);
|
||||
|
||||
NSMutableData *serializedData = nil;
|
||||
|
||||
NSData *headerPattern = getHeaderPattern();
|
||||
NSData *footerPattern = getFooterPattern();
|
||||
if (outBuffer[164] == height && outBuffer[166] == width && headerPattern != nil && footerPattern != nil) {
|
||||
outBuffer[164] = 0;
|
||||
outBuffer[166] = 0;
|
||||
|
||||
if (memcmp(headerPattern.bytes, outBuffer, headerPattern.length) == 0) {
|
||||
if (memcmp(footerPattern.bytes, outBuffer + outSize - footerPattern.length, footerPattern.length) == 0) {
|
||||
serializedData = [[NSMutableData alloc] init];
|
||||
uint8_t version = 1;
|
||||
[serializedData appendBytes:&version length:1];
|
||||
uint8_t outWidth = (uint8_t)width;
|
||||
uint8_t outHeight = (uint8_t)height;
|
||||
[serializedData appendBytes:&outHeight length:1];
|
||||
[serializedData appendBytes:&outWidth length:1];
|
||||
unsigned long contentSize = outSize - headerPattern.length - footerPattern.length;
|
||||
[serializedData appendBytes:outBuffer + headerPattern.length length:contentSize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_destroy_compress(&cinfo);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return serializedData;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_JPEGLI
|
||||
UIImage * _Nullable decompressImage(NSData * _Nonnull sourceData) {
|
||||
return [UIImage imageWithData:sourceData];
|
||||
}
|
||||
#else
|
||||
UIImage * _Nullable decompressImage(NSData * _Nonnull sourceData) {
|
||||
long unsigned int jpegSize = sourceData.length;
|
||||
unsigned char *_compressedImage = (unsigned char *)sourceData.bytes;
|
||||
|
||||
int jpegSubsamp, width, height;
|
||||
|
||||
tjhandle _jpegDecompressor = tjInitDecompress();
|
||||
|
||||
if (tjDecompressHeader2(_jpegDecompressor, _compressedImage, jpegSize, &width, &height, &jpegSubsamp) != 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
int sourceBytesPerRow = (3 * width + 31) & ~0x1F;
|
||||
int targetBytesPerRow = (4 * width + 31) & ~0x1F;
|
||||
|
||||
unsigned char *buffer = (uint8_t *)malloc(sourceBytesPerRow * height);
|
||||
|
||||
tjDecompress2(_jpegDecompressor, _compressedImage, jpegSize, buffer, width, sourceBytesPerRow, height, TJPF_RGB, TJFLAG_FASTDCT | TJFLAG_FASTUPSAMPLE);
|
||||
|
||||
tjDestroy(_jpegDecompressor);
|
||||
|
||||
vImage_Buffer source;
|
||||
source.width = width;
|
||||
source.height = height;
|
||||
source.rowBytes = sourceBytesPerRow;
|
||||
source.data = buffer;
|
||||
|
||||
vImage_Buffer target;
|
||||
target.width = width;
|
||||
target.height = height;
|
||||
target.rowBytes = targetBytesPerRow;
|
||||
|
||||
unsigned char *targetBuffer = (uint8_t *)malloc(targetBytesPerRow * height);
|
||||
target.data = targetBuffer;
|
||||
|
||||
vImageConvert_RGB888toARGB8888(&source, nil, 0xff, &target, false, kvImageDoNotTile);
|
||||
|
||||
free(buffer);
|
||||
|
||||
vImage_Buffer permuteTarget;
|
||||
permuteTarget.width = width;
|
||||
permuteTarget.height = height;
|
||||
permuteTarget.rowBytes = targetBytesPerRow;
|
||||
|
||||
unsigned char *permuteTargetBuffer = (uint8_t *)malloc(targetBytesPerRow * height);
|
||||
permuteTarget.data = permuteTargetBuffer;
|
||||
|
||||
const uint8_t permuteMap[4] = {3,2,1,0};
|
||||
vImagePermuteChannels_ARGB8888(&target, &permuteTarget, permuteMap, kvImageDoNotTile);
|
||||
|
||||
free(targetBuffer);
|
||||
|
||||
NSData *resultData = [[NSData alloc] initWithBytesNoCopy:permuteTargetBuffer length:targetBytesPerRow * height deallocator:^(void * _Nonnull bytes, __unused NSUInteger length) {
|
||||
free(bytes);
|
||||
}];
|
||||
|
||||
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)resultData);
|
||||
|
||||
static CGColorSpaceRef imageColorSpace;
|
||||
static CGBitmapInfo bitmapInfo;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 1), YES, 0);
|
||||
UIImage *refImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
imageColorSpace = CGColorSpaceRetain(CGImageGetColorSpace(refImage.CGImage));
|
||||
bitmapInfo = CGImageGetBitmapInfo(refImage.CGImage);
|
||||
UIGraphicsEndImageContext();
|
||||
});
|
||||
|
||||
CGImageRef cgImg = CGImageCreate(width, height, 8, 32, targetBytesPerRow, imageColorSpace, bitmapInfo, dataProvider, NULL, true, kCGRenderingIntentDefault);
|
||||
|
||||
CGDataProviderRelease(dataProvider);
|
||||
|
||||
UIImage *resultImage = [[UIImage alloc] initWithCGImage:cgImg];
|
||||
CGImageRelease(cgImg);
|
||||
|
||||
return resultImage;
|
||||
}
|
||||
#endif
|
@ -51,6 +51,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var disableBackgroundAnimation: Bool
|
||||
public var logLanguageRecognition: Bool
|
||||
public var storiesExperiment: Bool
|
||||
public var storiesJpegExperiment: Bool
|
||||
|
||||
public static var defaultSettings: ExperimentalUISettings {
|
||||
return ExperimentalUISettings(
|
||||
@ -79,7 +80,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
disableImageContentAnalysis: false,
|
||||
disableBackgroundAnimation: false,
|
||||
logLanguageRecognition: false,
|
||||
storiesExperiment: false
|
||||
storiesExperiment: false,
|
||||
storiesJpegExperiment: false
|
||||
)
|
||||
}
|
||||
|
||||
@ -109,7 +111,8 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
disableImageContentAnalysis: Bool,
|
||||
disableBackgroundAnimation: Bool,
|
||||
logLanguageRecognition: Bool,
|
||||
storiesExperiment: Bool
|
||||
storiesExperiment: Bool,
|
||||
storiesJpegExperiment: Bool
|
||||
) {
|
||||
self.keepChatNavigationStack = keepChatNavigationStack
|
||||
self.skipReadHistory = skipReadHistory
|
||||
@ -137,6 +140,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.disableBackgroundAnimation = disableBackgroundAnimation
|
||||
self.logLanguageRecognition = logLanguageRecognition
|
||||
self.storiesExperiment = storiesExperiment
|
||||
self.storiesJpegExperiment = storiesJpegExperiment
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@ -168,6 +172,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.disableBackgroundAnimation = try container.decodeIfPresent(Bool.self, forKey: "disableBackgroundAnimation") ?? false
|
||||
self.logLanguageRecognition = try container.decodeIfPresent(Bool.self, forKey: "logLanguageRecognition") ?? false
|
||||
self.storiesExperiment = try container.decodeIfPresent(Bool.self, forKey: "storiesExperiment") ?? false
|
||||
self.storiesJpegExperiment = try container.decodeIfPresent(Bool.self, forKey: "storiesJpegExperiment") ?? false
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
@ -199,6 +204,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encode(self.disableBackgroundAnimation, forKey: "disableBackgroundAnimation")
|
||||
try container.encode(self.logLanguageRecognition, forKey: "logLanguageRecognition")
|
||||
try container.encode(self.storiesExperiment, forKey: "storiesExperiment")
|
||||
try container.encode(self.storiesJpegExperiment, forKey: "storiesJpegExperiment")
|
||||
}
|
||||
}
|
||||
|
||||
|
129
third-party/libjxl/BUILD
vendored
Normal file
129
third-party/libjxl/BUILD
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
|
||||
headers = [
|
||||
"jxl/codestream_header.h",
|
||||
"jxl/cms_interface.h",
|
||||
"jxl/color_encoding.h",
|
||||
"jxl/decode_cxx.h",
|
||||
"jxl/decode.h",
|
||||
"jxl/encode_cxx.h",
|
||||
"jxl/encode.h",
|
||||
"jxl/jxl_export.h",
|
||||
"jxl/jxl_threads_export.h",
|
||||
"jxl/memory_manager.h",
|
||||
"jxl/parallel_runner.h",
|
||||
"jxl/stats.h",
|
||||
"jxl/types.h",
|
||||
"jxl/version.h",
|
||||
]
|
||||
|
||||
libs = [
|
||||
"jxl",
|
||||
]
|
||||
|
||||
|
||||
brotli_libs = [
|
||||
"libbrotlicommon",
|
||||
"libbrotlidec",
|
||||
"libbrotlienc",
|
||||
]
|
||||
|
||||
highway_libs = [
|
||||
"libhwy"
|
||||
]
|
||||
|
||||
filegroup(
|
||||
name = "libjxl_sources",
|
||||
srcs = glob([
|
||||
"libjxl/**/*"
|
||||
]),
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "libjxl_build",
|
||||
srcs = [
|
||||
"build-libjxl-bazel.sh",
|
||||
":libjxl_sources",
|
||||
"@cmake_tar_gz//file",
|
||||
],
|
||||
cmd_bash =
|
||||
"""
|
||||
set -ex
|
||||
|
||||
if [ "$(TARGET_CPU)" == "ios_armv7" ]; then
|
||||
BUILD_ARCH="armv7"
|
||||
elif [ "$(TARGET_CPU)" == "ios_arm64" ]; then
|
||||
BUILD_ARCH="arm64"
|
||||
elif [ "$(TARGET_CPU)" == "ios_sim_arm64" ]; then
|
||||
BUILD_ARCH="sim_arm64"
|
||||
elif [ "$(TARGET_CPU)" == "ios_x86_64" ]; then
|
||||
BUILD_ARCH="x86_64"
|
||||
else
|
||||
echo "Unsupported architecture $(TARGET_CPU)"
|
||||
fi
|
||||
|
||||
BUILD_DIR="$(RULEDIR)/build_$${BUILD_ARCH}"
|
||||
rm -rf "$$BUILD_DIR"
|
||||
mkdir -p "$$BUILD_DIR"
|
||||
|
||||
CMAKE_DIR="$$(pwd)/$$BUILD_DIR/cmake"
|
||||
rm -rf "$$CMAKE_DIR"
|
||||
mkdir -p "$$CMAKE_DIR"
|
||||
tar -xzf "$(location @cmake_tar_gz//file)" -C "$$CMAKE_DIR"
|
||||
|
||||
cp $(location :build-libjxl-bazel.sh) "$$BUILD_DIR/"
|
||||
|
||||
SOURCE_PATH="third-party/libjxl/libjxl"
|
||||
|
||||
cp -R "$$SOURCE_PATH" "$$BUILD_DIR/"
|
||||
|
||||
mkdir -p "$$BUILD_DIR/Public/jxl"
|
||||
|
||||
PATH="$$PATH:$$CMAKE_DIR/cmake-3.23.1-macos-universal/CMake.app/Contents/bin" sh $$BUILD_DIR/build-libjxl-bazel.sh $$BUILD_ARCH "$$BUILD_DIR/libjxl" "$$BUILD_DIR"
|
||||
""" +
|
||||
"\n".join([
|
||||
"cp -f \"$$BUILD_DIR/build/lib/include/{}\" \"$(location Public/{})\"".format(header, header) for header in headers
|
||||
]) +
|
||||
"\n" +
|
||||
"\n".join([
|
||||
"cp -f \"$$BUILD_DIR/build/lib/lib{}.a\" \"$(location Public/jxl/lib/lib{}.a)\"".format(lib, lib) for lib in libs
|
||||
]) +
|
||||
"\n" +
|
||||
"\n".join([
|
||||
"cp -f \"$$BUILD_DIR/build/third_party/brotli/{}.a\" \"$(location Public/jxl/lib/{}.a)\"".format(lib, lib) for lib in brotli_libs
|
||||
]) +
|
||||
"\n" +
|
||||
"\n".join([
|
||||
"cp -f \"$$BUILD_DIR/build/third_party/highway/{}.a\" \"$(location Public/jxl/lib/{}.a)\"".format(lib, lib) for lib in highway_libs
|
||||
]),
|
||||
outs = ["Public/" + x for x in headers] +
|
||||
["Public/jxl/lib/lib{}.a".format(x) for x in libs] +
|
||||
["Public/jxl/lib/{}.a".format(x) for x in brotli_libs] +
|
||||
["Public/jxl/lib/{}.a".format(x) for x in highway_libs],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
]
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "jxl_lib",
|
||||
srcs = [":Public/jxl/lib/lib" + x + ".a" for x in libs] +
|
||||
[":Public/jxl/lib/" + x + ".a" for x in brotli_libs] +
|
||||
[":Public/jxl/lib/" + x + ".a" for x in highway_libs],
|
||||
)
|
||||
|
||||
objc_library(
|
||||
name = "jxl",
|
||||
module_name = "jxl",
|
||||
enable_modules = True,
|
||||
hdrs = [":Public/" + x for x in headers],
|
||||
includes = [
|
||||
"Public",
|
||||
"Public/jxl",
|
||||
],
|
||||
deps = [
|
||||
":jxl_lib",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
65
third-party/libjxl/build-libjxl-bazel.sh
vendored
Executable file
65
third-party/libjxl/build-libjxl-bazel.sh
vendored
Executable file
@ -0,0 +1,65 @@
|
||||
#! /bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
ARCH="$1"
|
||||
|
||||
SOURCE_DIR="$2"
|
||||
BUILD_DIR=$(echo "$(cd "$(dirname "$3")"; pwd -P)/$(basename "$3")")
|
||||
|
||||
RSSS="9"
|
||||
|
||||
CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF -DJPEGXL_ENABLE_BENCHMARK=0 -DJPEGXL_ENABLE_FUZZERS=0 -DJPEGXL_ENABLE_TOOLS=0 -DJPEGXL_ENABLE_JPEGLI=0 -DJPEGXL_ENABLE_DOXYGEN=0 -DJPEGXL_ENABLE_MANPAGES=0 -DJPEGXL_ENABLE_BENCHMARK=0 -DJPEGXL_ENABLE_EXAMPLES=0 -DJPEGXL_BUNDLE_LIBPNG=0 -DJPEGXL_ENABLE_JNI=0 -DJPEGXL_ENABLE_SJPEG=0 -DJPEGXL_ENABLE_OPENEXR=0 -DJPEGXL_ENABLE_TRANSCODE_JPEG=0 -DJPEGXL_STATIC=1 -DJPEGXL_ENABLE_BOXES=0"
|
||||
|
||||
if [ "$ARCH" = "arm64" ]; then
|
||||
IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneOS.platform"
|
||||
IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneOS*.sdk)
|
||||
export CFLAGS="-Wall -arch arm64 -miphoneos-version-min=11.0 -funwind-tables"
|
||||
|
||||
cd "$BUILD_DIR"
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
touch toolchain.cmake
|
||||
echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
|
||||
echo "set(CMAKE_SYSTEM_PROCESSOR aarch64)" >> toolchain.cmake
|
||||
echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
|
||||
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} $CMAKE_OPTIONS ../libjxl
|
||||
make
|
||||
elif [ "$ARCH" = "sim_arm64" ]; then
|
||||
IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneSimulator.platform"
|
||||
IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneSimulator*.sdk)
|
||||
export CFLAGS="-Wall -arch arm64 --target=arm64-apple-ios11.0-simulator -miphonesimulator-version-min=11.0 -funwind-tables"
|
||||
|
||||
cd "$BUILD_DIR"
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
touch toolchain.cmake
|
||||
echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
|
||||
echo "set(CMAKE_SYSTEM_PROCESSOR aarch64)" >> toolchain.cmake
|
||||
echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
|
||||
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} $CMAKE_OPTIONS ../libjxl
|
||||
make
|
||||
elif [ "$ARCH" = "x86_64" ]; then
|
||||
IOS_PLATFORMDIR="$(xcode-select -p)/Platforms/iPhoneSimulator.platform"
|
||||
IOS_SYSROOT=($IOS_PLATFORMDIR/Developer/SDKs/iPhoneSimulator*.sdk)
|
||||
export CFLAGS="-Wall -arch x86_64 -miphoneos-version-min=11.0 -funwind-tables"
|
||||
|
||||
cd "$BUILD_DIR"
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
touch toolchain.cmake
|
||||
echo "set(CMAKE_SYSTEM_NAME Darwin)" >> toolchain.cmake
|
||||
echo "set(CMAKE_SYSTEM_PROCESSOR AMD64)" >> toolchain.cmake
|
||||
echo "set(CMAKE_C_COMPILER $(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang)" >> toolchain.cmake
|
||||
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DCMAKE_OSX_SYSROOT=${IOS_SYSROOT[0]} $CMAKE_OPTIONS ../libjxl
|
||||
make
|
||||
else
|
||||
echo "Unsupported architecture $ARCH"
|
||||
exit 1
|
||||
fi
|
73
third-party/libjxl/libjxl/AUTHORS
vendored
Normal file
73
third-party/libjxl/libjxl/AUTHORS
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
# List of the project authors for copyright purposes. When contributing to the
|
||||
# project add your name or your organization's name to this list. See
|
||||
# CONTRIBUTING.md for details.
|
||||
#
|
||||
# For organizations:
|
||||
# Organization <email pattern: *@domain>
|
||||
#
|
||||
# For individuals:
|
||||
# Name <email address>
|
||||
#
|
||||
# Please keep each list sorted. If you wish to change your email address please
|
||||
# send a pull request.
|
||||
|
||||
# Organizations:
|
||||
Cloudinary Ltd. <*@cloudinary.com>
|
||||
Google LLC <*@google.com>
|
||||
|
||||
# Individuals:
|
||||
a-shvedov
|
||||
Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
|
||||
Alexander Sago <cagelight@gmail.com>
|
||||
Alistair Barrow
|
||||
Andrius Lukas Narbutas <andrius4669@gmail.com>
|
||||
Aous Naman <aous@unsw.edu.au>
|
||||
Artem Selishchev
|
||||
Biswapriyo Nath <nathbappai@gmail.com>
|
||||
CanadianBaconBoi <beamconnor@gmail.com>
|
||||
Damiano Albani <damiano.albani@gmail.com>
|
||||
Daniel Novomeský <dnovomesky@gmail.com>
|
||||
David Burnett <vargolsoft@gmail.com>
|
||||
dependabot[bot]
|
||||
Diego Pino <dpino@igalia.com>
|
||||
Dirk Lemstra <dirk@lemstra.org>
|
||||
Don Olmstead <don.j.olmstead@gmail.com>
|
||||
Dong Xu <xdong181@gmail.com>
|
||||
Even Rouault <even.rouault@spatialys.com>
|
||||
Fred Brennan <copypaste@kittens.ph>
|
||||
gi-man
|
||||
Gilles Devillers (GilDev) <gildev@gmail.com>
|
||||
Heiko Becker <heirecka@exherbo.org>
|
||||
Jim Robinson <jimbo2150@gmail.com>
|
||||
Jon Sneyers <jon@cloudinary.com>
|
||||
Jonathan Brown (Jonnyawsom3) <jonathanbr30@gmail.com>
|
||||
Joshua Root <jmr@macports.org>
|
||||
Kai Hollberg <Schweinepriester@users.noreply.github.com>
|
||||
Kleis Auke Wolthuizen <github@kleisauke.nl>
|
||||
L. E. Segovia
|
||||
Leo Izen <leo.izen@gmail.com>
|
||||
Lovell Fuller
|
||||
Maarten DB <anonymous.maarten@gmail.com>
|
||||
Marcin Konicki <ahwayakchih@gmail.com>
|
||||
Martin Strunz
|
||||
Mathieu Malaterre <mathieu.malaterre@gmail.com>
|
||||
Mikk Leini <mikk.leini@krakul.eu>
|
||||
Misaki Kasumi <misakikasumi@outlook.com>
|
||||
Moonchild Straver <moonchild@palemoon.org>
|
||||
Nicholas Hayes <0xC0000054@users.noreply.github.com>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
Petr Diblík
|
||||
Pieter Wuille
|
||||
roland-rollo
|
||||
Samuel Leong <wvvwvvvvwvvw@gmail.com>
|
||||
Sandro <sandro.jaeckel@gmail.com>
|
||||
Sergey Fedorov <vital.had@gmail.com>
|
||||
Stephan T. Lavavej <stl@nuwen.net>
|
||||
Sylvestre Ledru <sylvestre@debian.org>
|
||||
Thomas Bonfort <thomas.bonfort@airbus.com>
|
||||
tmkk <tmkkmac@gmail.com>
|
||||
Vincent Torri <vincent.torri@gmail.com>
|
||||
xiota
|
||||
Yonatan Nebenzhal <yonatan.nebenzhl@gmail.com>
|
||||
Ziemowit Zabawa <ziemek.zabawa@outlook.com>
|
||||
源文雨 <41315874+fumiama@users.noreply.github.com>
|
22
third-party/libjxl/libjxl/BUILD.bazel
vendored
Normal file
22
third-party/libjxl/libjxl/BUILD.bazel
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package(default_visibility = ["//:__subpackages__"])
|
||||
|
||||
filegroup(
|
||||
name = "testdata",
|
||||
srcs = glob([
|
||||
"testdata/**/*.icc",
|
||||
"testdata/**/*.pam",
|
||||
"testdata/**/*.pfm",
|
||||
"testdata/**/*.pgm",
|
||||
"testdata/**/*.pnm",
|
||||
"testdata/**/*.ppm",
|
||||
"testdata/**/*.png",
|
||||
"testdata/**/*.jpg",
|
||||
"testdata/**/*.jxl",
|
||||
"testdata/**/*.gif",
|
||||
"testdata/**/*.y4m",
|
||||
"testdata/**/*.jxl",
|
||||
"testdata/**/*.png",
|
||||
"testdata/**/*.jpg",
|
||||
"testdata/position_encoding/*.txt",
|
||||
]),
|
||||
)
|
85
third-party/libjxl/libjxl/BUILDING.md
vendored
Normal file
85
third-party/libjxl/libjxl/BUILDING.md
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
# Compilation
|
||||
|
||||
For more details and other workflows see the "Advanced guide" below.
|
||||
|
||||
## Checking out the code
|
||||
|
||||
```bash
|
||||
git clone https://github.com/libjxl/libjxl.git --recursive --shallow-submodules
|
||||
```
|
||||
|
||||
This repository uses git submodules to handle some third party dependencies
|
||||
under `third_party`, that's why it is important to pass `--recursive`. If you
|
||||
didn't check out with `--recursive`, or any submodule has changed, run:
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive --depth 1 --recommend-shallow
|
||||
```
|
||||
|
||||
The `--shallow-submodules` and `--depth 1 --recommend-shallow` options create
|
||||
shallow clones which only downloads the commits requested, and is all that is
|
||||
needed to build `libjxl`. Should full clones be necessary, you could always run:
|
||||
|
||||
```bash
|
||||
git submodule foreach git fetch --unshallow
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
which pulls the rest of the commits in the submodules.
|
||||
|
||||
Important: If you downloaded a zip file or tarball from the web interface you
|
||||
won't get the needed submodules and the code will not compile. You can download
|
||||
these external dependencies from source running `./deps.sh`. The git workflow
|
||||
described above is recommended instead.
|
||||
|
||||
## Installing dependencies
|
||||
|
||||
Required dependencies for compiling the code, in a Debian/Ubuntu based
|
||||
distribution run:
|
||||
|
||||
```bash
|
||||
sudo apt install cmake pkg-config libbrotli-dev
|
||||
```
|
||||
|
||||
Optional dependencies for supporting other formats in the `cjxl`/`djxl` tools,
|
||||
in a Debian/Ubuntu based distribution run:
|
||||
|
||||
```bash
|
||||
sudo apt install libgif-dev libjpeg-dev libopenexr-dev libpng-dev libwebp-dev
|
||||
```
|
||||
|
||||
We recommend using a recent Clang compiler (version 7 or newer), for that
|
||||
install clang and set `CC` and `CXX` variables.
|
||||
|
||||
```bash
|
||||
sudo apt install clang
|
||||
export CC=clang CXX=clang++
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
cd libjxl
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF ..
|
||||
cmake --build . -- -j$(nproc)
|
||||
```
|
||||
|
||||
The encoder/decoder tools will be available in the `build/tools` directory.
|
||||
|
||||
## <a name="installing"></a> Installing
|
||||
|
||||
```bash
|
||||
sudo cmake --install .
|
||||
```
|
||||
|
||||
|
||||
## Building JPEG XL for developers
|
||||
|
||||
For experienced developers, we provide build instructions for several other environments:
|
||||
|
||||
* [Building on Debian](doc/developing_in_debian.md)
|
||||
* Building on Windows with [vcpkg](doc/developing_in_windows_vcpkg.md) (Visual Studio 2019)
|
||||
* Building on Windows with [MSYS2](doc/developing_in_windows_msys.md)
|
||||
* [Cross Compiling for Windows with Crossroad](doc/developing_with_crossroad.md)
|
20
third-party/libjxl/libjxl/BUILDING_Haiku.md
vendored
Normal file
20
third-party/libjxl/libjxl/BUILDING_Haiku.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
## Disclaimer
|
||||
|
||||
Haiku builds are not officially supported, i.e. the build might not work at all,
|
||||
some tests may fail and some sub-projects are excluded from build.
|
||||
|
||||
This manual outlines Haiku-specific setup. For general building and testing
|
||||
instructions see "[BUILDING](BUILDING.md)" and
|
||||
"[Building and Testing changes](doc/building_and_testing.md)".
|
||||
|
||||
## Dependencies
|
||||
|
||||
```shell
|
||||
pkgman install llvm9_clang ninja cmake doxygen libjpeg_turbo_devel giflib_devel
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
```shell
|
||||
TEST_STACK_LIMIT=none CMAKE_FLAGS="-I/boot/system/develop/tools/lib/gcc/x86_64-unknown-haiku/8.3.0/include/c++ -I/boot/system/develop/tools/lib/gcc/x86_64-unknown-haiku/8.3.0/include/c++/x86_64-unknown-haiku" CMAKE_SHARED_LINKER_FLAGS="-shared -Xlinker -soname=libjpegxl.so -lpthread" ./ci.sh opt
|
||||
```
|
41
third-party/libjxl/libjxl/BUILDING_OSX.md
vendored
Normal file
41
third-party/libjxl/libjxl/BUILDING_OSX.md
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
## Disclaimer
|
||||
|
||||
OSX builds have "best effort" support, i.e. build might not work at all, some
|
||||
tests may fail and some sub-projects are excluded from build.
|
||||
|
||||
This manual outlines OSX specific setup. For general building and testing
|
||||
instructions see "[BUILDING](BUILDING.md)" and
|
||||
"[Building and Testing changes](doc/building_and_testing.md)".
|
||||
|
||||
[Homebrew](https://brew.sh/) is a popular package manager. JPEG XL library and
|
||||
binaries could be installed using it:
|
||||
|
||||
```bash
|
||||
brew install jpeg-xl
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
Make sure that `brew doctor` does not report serious problems and up-to-date
|
||||
version of XCode is installed.
|
||||
|
||||
Installing (actually, building) `clang` might take a couple hours.
|
||||
|
||||
```bash
|
||||
brew install llvm
|
||||
```
|
||||
|
||||
```bash
|
||||
brew install coreutils cmake giflib jpeg-turbo libpng ninja zlib
|
||||
```
|
||||
|
||||
Before building the project check that `which clang` is
|
||||
`/usr/local/opt/llvm/bin/clang`, not the one provided by XCode. If not, update
|
||||
`PATH` environment variable.
|
||||
|
||||
Also, setting `CMAKE_PREFIX_PATH` might be necessary for correct include paths
|
||||
resolving, e.g.:
|
||||
|
||||
```bash
|
||||
export CMAKE_PREFIX_PATH=`brew --prefix giflib`:`brew --prefix jpeg-turbo`:`brew --prefix libpng`:`brew --prefix zlib`
|
||||
```
|
320
third-party/libjxl/libjxl/CHANGELOG.md
vendored
Normal file
320
third-party/libjxl/libjxl/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,320 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
- encoder API: add `JxlEncoderSetExtraChannelDistance` to adjust the quality
|
||||
of extra channels (like alpha) separately.
|
||||
- encoder API: new api functions for streaming encoding:
|
||||
- `JxlEncoderSetOutputCallback`,
|
||||
- `JxlEncoderChunkedImageFrameStart`,
|
||||
- `JxlEncoderChunkedImageFrameAddPart` and new
|
||||
- `JXL_ENC_FRAME_SETTING_BUFFERING` enum value.
|
||||
- encoder API: new options for more fine-grained control over metadata
|
||||
preservation when using `JxlEncoderAddJPEGFrame`:
|
||||
- `JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF`
|
||||
- `JXL_ENC_FRAME_SETTING_JPEG_KEEP_XMP`
|
||||
- `JXL_ENC_FRAME_SETTING_JPEG_KEEP_JUMBF`
|
||||
- encoder API: new function `JxlEncoderSetUpsamplingMode` to change the upsampling
|
||||
method, e.g. to use nearest-neighbor upsampling for pixel art
|
||||
- cjxl can now be used to explicitly add/update/strip Exif/XMP/JUMBF metadata using
|
||||
the decoder-hints syntax, e.g. `cjxl input.ppm -x exif=input.exif output.jxl`
|
||||
- djxl can now be used to extract Exif/XMP/JUMBF metadata
|
||||
|
||||
### Removed
|
||||
- API: the Butteraugli API (`jxl/butteraugli.h`) was removed.
|
||||
- encoder and decoder API: all deprecated functions were removed:
|
||||
`JxlDecoderDefaultPixelFormat`, `JxlEncoderOptionsSetLossless`,
|
||||
`JxlEncoderOptionsSetEffort`, `JxlEncoderOptionsSetDecodingSpeed`,
|
||||
`JxlEncoderOptionsSetDistance`, `JxlEncoderOptionsCreate`, as well as
|
||||
the deprecated enumerator values `JXL_DEC_EXTENSIONS`, `JXL_ENC_NOT_SUPPORTED`,
|
||||
`JXL_TYPE_BOOLEAN`, `JXL_TYPE_UINT32`, and deprecated type `JxlEncoderOptions`.
|
||||
- decoder API: the signature of `JxlDecoderGetColorAsEncodedProfile`,
|
||||
`JxlDecoderGetICCProfileSize`, and `JxlDecoderGetColorAsICCProfile`
|
||||
changed: a deprecated unused argument was removed.
|
||||
|
||||
### Changed
|
||||
- changed the name of the cjxl flag `photon_noise` to `photon_noise_iso`
|
||||
|
||||
## [0.8.0] - 2023-01-18
|
||||
|
||||
### Added
|
||||
- decoder API: new function `JxlDecoderSetImageBitDepth` to set the bit depth
|
||||
of the output buffer.
|
||||
- decoder API proposal: add `JxlDecoderSetOutputColorProfile` and
|
||||
`JxlDecoderSetCms` to enable decoding to desired colorspace; NB: not
|
||||
implemented yet.
|
||||
- encoder API: new function `JxlEncoderSetFrameBitDepth` to set the bit depth
|
||||
of the input buffer.
|
||||
- encoder API: add an effort 10 option for lossless compression; using this
|
||||
setting requires calling `JxlEncoderAllowExpertOptions`.
|
||||
- encoder API: new `JXL_ENC_FRAME_SETTING_JPEG_COMPRESS_BOXES` enum value to
|
||||
allow explicit control of metadata compression
|
||||
|
||||
### Removed
|
||||
- common API: removed `JxlIntrinsicSizeHeader`
|
||||
- decoder API: removed deprecated `JXL_DEC_NEED_DC_OUT_BUFFER` and
|
||||
`JXL_DEC_DC_IMAGE` events, `JxlDecoderDCOutBufferSize` and
|
||||
`JxlDecoderSetDCOutBuffer` functions
|
||||
|
||||
### Changed / clarified
|
||||
- encoder API: `JxlEncoderProcessOutput` requires at least 32 bytes of output
|
||||
space to proceed and guarantees that at least one byte will be written
|
||||
|
||||
## [0.7] - 2022-07-21
|
||||
|
||||
### Added
|
||||
- Export version information in headers.
|
||||
- decoder API: Ability to decode the content of metadata boxes:
|
||||
`JXL_DEC_BOX`, `JXL_DEC_BOX_NEED_MORE_OUTPUT`, `JxlDecoderSetBoxBuffer`,
|
||||
`JxlDecoderGetBoxType`, `JxlDecoderGetBoxSizeRaw` and
|
||||
`JxlDecoderSetDecompressBoxes`.
|
||||
- decoder API: ability to mark the input is finished: `JxlDecoderCloseInput`.
|
||||
- decoder API: ability to request updates on different progressive events using
|
||||
`JxlDecoderSetProgressiveDetail`; currently supported events are
|
||||
`kDC`, `kLastPasses` and `kPasses`.
|
||||
- decoder API: ability to specify desired intensity target using
|
||||
`JxlDecoderSetDesiredIntensityTarget`
|
||||
- decoder API: new function `JxlDecoderSetCoalesced` to allow decoding
|
||||
non-coalesced (unblended) frames, e.g. layers of a composite still image
|
||||
or the cropped frames of a recompressed GIF/APNG.
|
||||
- decoder API: new function `JxlDecoderSetUnpremultiplyAlpha` to set
|
||||
preference for getting an associated alpha channel with premultiplied or
|
||||
unpremultiplied colors.
|
||||
- decoder API: field added to `JxlFrameHeader`: a `JxlLayerInfo` struct
|
||||
that contains crop dimensions and offsets and blending information for
|
||||
the non-coalesced case.
|
||||
- decoder API: new function `JxlDecoderGetExtraChannelBlendInfo` to get
|
||||
the blending information for extra channels in the non-coalesced case.
|
||||
- decoder API: new function `JxlDecoderSetMultithreadedImageOutCallback`,
|
||||
allowing output callbacks to receive more information about the number of
|
||||
threads on which they are running.
|
||||
- decoder API: new function `JxlDecoderSkipCurrentFrame` to skip processing
|
||||
the current frame after a progressive detail is reached.
|
||||
- decoder API: new function `JxlDecoderGetIntendedDownsamplingRatio` to get
|
||||
the intended downsampling ratio of progressive steps, based on the
|
||||
information in the frame header.
|
||||
- decoder API: new function `JxlDecoderSetRenderSpotcolors` to allow disabling
|
||||
rendering of spot colors.
|
||||
- decoder/encoder API: add two fields to `JXLBasicInfo`: `intrinsic_xsize`
|
||||
and `intrinsic_ysize` to signal the intrinsic size.
|
||||
- encoder API: ability to add metadata boxes, added new functions
|
||||
`JxlEncoderAddBox`, `JxlEncoderUseBoxes`, `JxlEncoderCloseBoxes` and
|
||||
`JxlEncoderCloseFrames`.
|
||||
- encoder API: added ability to set several encoder options / extra fields to
|
||||
frames using `JxlEncoderSetFrameName`, `JxlEncoderFrameSettingsSetOption`,
|
||||
`JxlEncoderFrameSettingsSetFloatOption`.
|
||||
- encoder API: added ability to check required codestream compatibility level
|
||||
and force specified using `JxlEncoderGetRequiredCodestreamLevel` and
|
||||
`JxlEncoderSetCodestreamLevel`.
|
||||
- encoder API: added ability to force emitting box-based container format
|
||||
using `JxlEncoderUseContainer`.
|
||||
- encoder API: added ability to store JPEG metadata for lossless reconstruction
|
||||
using `JxlEncoderStoreJPEGMetadata`
|
||||
- encoder API: new functions `JxlEncoderSetFrameHeader` and
|
||||
`JxlEncoderSetExtraChannelBlendInfo` to set animation
|
||||
and blending parameters of the frame, and `JxlEncoderInitFrameHeader` and
|
||||
`JxlEncoderInitBlendInfo` to initialize the structs to set.
|
||||
- encoder API: ability to encode arbitrary extra channels:
|
||||
`JxlEncoderInitExtraChannelInfo`, `JxlEncoderSetExtraChannelInfo`,
|
||||
`JxlEncoderSetExtraChannelName` and `JxlEncoderSetExtraChannelBuffer`.
|
||||
- encoder API: ability to plug custom CMS implementation using
|
||||
`JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms)`
|
||||
- encoder API: added `JxlEncoderGetError` to retrieve last encoder error.
|
||||
|
||||
### Changed
|
||||
- decoder API: using `JxlDecoderCloseInput` at the end of all input is required
|
||||
when using JXL_DEC_BOX, and is now also encouraged in other cases, but not
|
||||
required in those other cases for backwards compatibility.
|
||||
- encoder API: `JxlEncoderCloseInput` now closes both frames and boxes input.
|
||||
- CLI: `cjxl` and `djxl` have been reimplemented on the base of public decoder
|
||||
and encoder API; dropped dependency on `gflags` for argument parsing.
|
||||
|
||||
### Deprecated
|
||||
- decoder API: `JXL_DEC_EXTENSIONS` event: use `JXL_DEC_BASIC_INFO`
|
||||
- decoder / encoder API: pixel types `JXL_TYPE_BOOLEAN` and `JXL_TYPE_UINT32`:
|
||||
consider using `JXL_TYPE_UINT8` and `JXL_TYPE_FLOAT` correspondingly.
|
||||
- decoder API: pixel format parameter for `JxlDecoderGetColorAsEncodedProfile`
|
||||
and `JxlDecoderGetICCProfileSize`: pass `NULL`.
|
||||
- decoder API: `JxlDecoderDefaultPixelFormat`
|
||||
- encoder API: `JxlEncoderOptions`: use `JxlEncoderFrameSettings` instead.
|
||||
- encoder API: `JxlEncoderOptionsCreate`: use `JxlEncoderFrameSettingsCreate`
|
||||
instead.
|
||||
- encoder API: `JxlEncoderOptionsSetDistance`: use `JxlEncoderSetFrameDistance`
|
||||
instead.
|
||||
- encoder API: `JxlEncoderOptionsSetLossless`: use `JxlEncoderSetFrameLossless`
|
||||
instead.
|
||||
- encoder API: `JxlEncoderOptionsSetEffort`: use
|
||||
`JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, effort)`
|
||||
instead.
|
||||
- encoder API: `JxlEncoderOptionsSetDecodingSpeed`: use
|
||||
`JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, tier)`
|
||||
instead.
|
||||
- encoder API: deprecated `JXL_ENC_NOT_SUPPORTED`, the encoder returns
|
||||
`JXL_ENC_ERROR` instead and there is no need to handle
|
||||
`JXL_ENC_NOT_SUPPORTED`.
|
||||
|
||||
## [0.6.1] - 2021-10-29
|
||||
### Changed
|
||||
- Security: Fix OOB read in splines rendering (#735 -
|
||||
[CVE-2021-22563](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22563))
|
||||
- Security: Fix OOB copy (read/write) in out-of-order/multi-threaded decoding
|
||||
(#708 - [CVE-2021-22564](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22564))
|
||||
- Fix segfault in `djxl` tool with `--allow_partial_files` flag (#781).
|
||||
- Fix border in extra channels when using upsampling (#796)
|
||||
|
||||
## [0.6] - 2021-10-04
|
||||
### Added
|
||||
- API: New functions to decode extra channels:
|
||||
`JxlDecoderExtraChannelBufferSize` and `JxlDecoderSetExtraChannelBuffer`.
|
||||
- API: New function `JxlEncoderInitBasicInfo` to initialize `JxlBasicInfo`
|
||||
(only needed when encoding). NOTE: it is now required to call this function
|
||||
when using the encoder. Padding was added to the struct for forward
|
||||
compatibility.
|
||||
- API: Support for encoding oriented images.
|
||||
- API: FLOAT16 support in the encoder API.
|
||||
- Rewrite of the GDK pixbuf loader plugin. Added proper color management and
|
||||
animation support.
|
||||
- Rewrite of GIMP plugin. Added compression parameters dialog and switched to
|
||||
using the public C API.
|
||||
- Debian packages for GDK pixbuf loader (`libjxl-gdk-pixbuf`) and GIMP
|
||||
(`libjxl-gimp-plugin`) plugins.
|
||||
- `cjxl`/`djxl` support for `stdin` and `stdout`.
|
||||
|
||||
### Changed
|
||||
- API: Renamed the field `alpha_associated` in `JxlExtraChannelInfo` to
|
||||
`alpha_premultiplied`, to match the corresponding name in `JxlBasicInfo`.
|
||||
- Improved the 2x2 downscaling method in the encoder for the optional color
|
||||
channel resampling for low bit rates.
|
||||
- Fixed: the combination of floating point original data, XYB color encoding,
|
||||
and Modular mode was broken (in both encoder and decoder). It now works.
|
||||
NOTE: this can cause the current encoder to write jxl bitstreams that do
|
||||
not decode with the old decoder. In particular this will happen when using
|
||||
cjxl with PFM, EXR, or floating point PSD input, and a combination of XYB
|
||||
and modular mode is used (which caused an encoder error before), e.g.
|
||||
using options like `-m -q 80` (lossy modular), `-d 4.5` or `--progressive_dc=1`
|
||||
(modular DC frame), or default lossy encoding on an image where patches
|
||||
end up being used. There is no problem when using cjxl with PNG, JPEG, GIF,
|
||||
APNG, PPM, PGM, PGX, or integer (8-bit or 16-bit) PSD input.
|
||||
- `libjxl` static library now bundles skcms, fixing static linking in
|
||||
downstream projects when skcms is used.
|
||||
- Spline rendering performance improvements.
|
||||
- Butteraugli changes for less visual masking.
|
||||
|
||||
## [0.5] - 2021-08-02
|
||||
### Added
|
||||
- API: New function to decode the image using a callback outputting a part of a
|
||||
row per call.
|
||||
- API: 16-bit float output support.
|
||||
- API: `JxlDecoderRewind` and `JxlDecoderSkipFrames` functions to skip more
|
||||
efficiently to earlier animation frames.
|
||||
- API: `JxlDecoderSetPreferredColorProfile` function to choose color profile in
|
||||
certain circumstances.
|
||||
- encoder: Adding `center_x` and `center_y` flags for more control of the tile
|
||||
order.
|
||||
- New encoder speeds `lightning` (1) and `thunder` (2).
|
||||
|
||||
### Changed
|
||||
- Re-licensed the project under a BSD 3-Clause license. See the
|
||||
[LICENSE](LICENSE) and [PATENTS](PATENTS) files for details.
|
||||
- Full JPEG XL part 1 specification support: Implemented all the spec required
|
||||
to decode files to pixels, including cases that are not used by the encoder
|
||||
yet. Part 2 of the spec (container format) is final but not fully implemented
|
||||
here.
|
||||
- Butteraugli metric improvements. Exact numbers are different from previous
|
||||
versions.
|
||||
- Memory reductions during decoding.
|
||||
- Reduce the size of the jxl_dec library by removing dependencies.
|
||||
- A few encoding speedups.
|
||||
- Clarify the security policy.
|
||||
- Significant encoding improvements (~5 %) and less ringing.
|
||||
- Butteraugli metric to have some less masking.
|
||||
- `cjxl` flag `--speed` is deprecated and replaced by the `--effort` synonym.
|
||||
|
||||
### Removed
|
||||
- API for returning a downsampled DC was deprecated
|
||||
(`JxlDecoderDCOutBufferSize` and `JxlDecoderSetDCOutBuffer`) and will be
|
||||
removed in the next release.
|
||||
|
||||
## [0.3.7] - 2021-03-29
|
||||
### Changed
|
||||
- Fix a rounding issue in 8-bit decoding.
|
||||
|
||||
## [0.3.6] - 2021-03-25
|
||||
### Changed
|
||||
- Fix a bug that could result in the generation of invalid codestreams as
|
||||
well as failure to decode valid streams.
|
||||
|
||||
## [0.3.5] - 2021-03-23
|
||||
### Added
|
||||
- New encode-time options for faster decoding at the cost of quality.
|
||||
- Man pages for cjxl and djxl.
|
||||
|
||||
### Changed
|
||||
- Memory usage improvements.
|
||||
- Faster decoding to 8-bit output with the C API.
|
||||
- GIMP plugin: avoid the sRGB conversion dialog for sRGB images, do not show
|
||||
a console window on Windows.
|
||||
- Various bug fixes.
|
||||
|
||||
## [0.3.4] - 2021-03-16
|
||||
### Changed
|
||||
- Improved box parsing.
|
||||
- Improved metadata handling.
|
||||
- Performance and memory usage improvements.
|
||||
|
||||
## [0.3.3] - 2021-03-05
|
||||
### Changed
|
||||
- Performance improvements for small images.
|
||||
- Add a (flag-protected) non-high-precision mode with better speed.
|
||||
- Significantly speed up the PQ EOTF.
|
||||
- Allow optional HDR tone mapping in djxl (--tone_map, --display_nits).
|
||||
- Change the behavior of djxl -j to make it consistent with cjxl (#153).
|
||||
- Improve image quality.
|
||||
- Improve EXIF handling.
|
||||
|
||||
## [0.3.2] - 2021-02-12
|
||||
### Changed
|
||||
- Fix embedded ICC encoding regression
|
||||
[#149](https://gitlab.com/wg1/jpeg-xl/-/issues/149).
|
||||
|
||||
## [0.3.1] - 2021-02-10
|
||||
### Changed
|
||||
- New experimental Butteraugli API (`jxl/butteraugli.h`).
|
||||
- Encoder improvements to low quality settings.
|
||||
- Bug fixes, including fuzzer-found potential security bug fixes.
|
||||
- Fixed `-q 100` and `-d 0` not triggering lossless modes.
|
||||
|
||||
## [0.3] - 2021-01-29
|
||||
### Changed
|
||||
- Minor change to the Decoder C API to accommodate future work for other ways
|
||||
to provide input.
|
||||
- Future decoder C API changes will be backwards compatible.
|
||||
- Lots of bug fixes since the previous version.
|
||||
|
||||
## [0.2] - 2020-12-24
|
||||
### Added
|
||||
- JPEG XL bitstream format is frozen. Files encoded with 0.2 will be supported
|
||||
by future versions.
|
||||
|
||||
### Changed
|
||||
- Files encoded with previous versions are not supported.
|
||||
|
||||
## [0.1.1] - 2020-12-01
|
||||
|
||||
## [0.1] - 2020-11-14
|
||||
### Added
|
||||
- Initial release of an encoder (`cjxl`) and decoder (`djxl`) that work
|
||||
together as well as a benchmark tool for comparison with other codecs
|
||||
(`benchmark_xl`).
|
||||
- Note: JPEG XL format is in the final stages of standardization, minor changes
|
||||
to the codestream format are still possible but we are not expecting any
|
||||
changes beyond what is required by bug fixing.
|
||||
- API: new decoder API in C, check the `examples/` directory for its example
|
||||
usage. The C API is a work in progress and likely to change both in API and
|
||||
ABI in future releases.
|
527
third-party/libjxl/libjxl/CMakeLists.txt
vendored
Normal file
527
third-party/libjxl/libjxl/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,527 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# Ubuntu bionic ships with cmake 3.10.
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
# Honor VISIBILITY_INLINES_HIDDEN on all types of targets.
|
||||
if(POLICY CMP0063)
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
endif()
|
||||
# Pass CMAKE_EXE_LINKER_FLAGS to CC and CXX compilers when testing if they work.
|
||||
if(POLICY CMP0065)
|
||||
cmake_policy(SET CMP0065 NEW)
|
||||
endif()
|
||||
|
||||
# Set PIE flags for POSITION_INDEPENDENT_CODE targets, added in 3.14.
|
||||
if(POLICY CMP0083)
|
||||
cmake_policy(SET CMP0083 NEW)
|
||||
endif()
|
||||
|
||||
project(LIBJXL LANGUAGES C CXX)
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles(
|
||||
"int main() {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
static_assert(false, \"__EMSCRIPTEN__ is not defined\");
|
||||
#endif
|
||||
return 0;
|
||||
}"
|
||||
JPEGXL_EMSCRIPTEN
|
||||
)
|
||||
|
||||
message(STATUS "CMAKE_SYSTEM_PROCESSOR is ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag("-fsanitize=fuzzer-no-link" CXX_FUZZERS_SUPPORTED)
|
||||
check_cxx_compiler_flag("-Xclang -mconstructor-aliases" CXX_CONSTRUCTOR_ALIASES_SUPPORTED)
|
||||
check_cxx_compiler_flag("-fmacro-prefix-map=OLD=NEW" CXX_MACRO_PREFIX_MAP)
|
||||
check_cxx_compiler_flag("-fno-rtti" CXX_NO_RTTI_SUPPORTED)
|
||||
|
||||
# Enabled PIE binaries by default if supported.
|
||||
include(CheckPIESupported OPTIONAL RESULT_VARIABLE CHECK_PIE_SUPPORTED)
|
||||
if(CHECK_PIE_SUPPORTED)
|
||||
check_pie_supported(LANGUAGES CXX)
|
||||
if(CMAKE_CXX_LINK_PIE_SUPPORTED)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(PROVISION_DEPENDENCIES)
|
||||
# Run script to provision dependencies.
|
||||
find_program (BASH_PROGRAM bash)
|
||||
if(BASH_PROGRAM)
|
||||
execute_process(
|
||||
COMMAND ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/deps.sh
|
||||
RESULT_VARIABLE PROVISION_DEPENDENCIES_RESULT)
|
||||
endif()
|
||||
if(NOT PROVISION_DEPENDENCIES_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "${CMAKE_CURRENT_SOURCE_DIR}/deps.sh failed with ${PROVISION_DEPENDENCIES_RESULT}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
### Project build options:
|
||||
if(CXX_FUZZERS_SUPPORTED)
|
||||
# Enabled by default except on arm64, Windows and Apple builds.
|
||||
set(ENABLE_FUZZERS_DEFAULT true)
|
||||
endif()
|
||||
find_package(PkgConfig)
|
||||
if(NOT APPLE AND NOT WIN32 AND NOT HAIKU AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
pkg_check_modules(TCMallocMinimalVersionCheck QUIET IMPORTED_TARGET
|
||||
libtcmalloc_minimal)
|
||||
if(TCMallocMinimalVersionCheck_FOUND AND
|
||||
NOT TCMallocMinimalVersionCheck_VERSION VERSION_EQUAL 2.8.0)
|
||||
# Enabled by default except on Windows and Apple builds for
|
||||
# tcmalloc != 2.8.0. tcmalloc 2.8.1 already has a fix for this issue.
|
||||
set(ENABLE_TCMALLOC_DEFAULT true)
|
||||
else()
|
||||
message(STATUS
|
||||
"tcmalloc version ${TCMallocMinimalVersionCheck_VERSION} -- "
|
||||
"tcmalloc 2.8.0 disabled due to "
|
||||
"https://github.com/gperftools/gperftools/issues/1204")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"int main() {
|
||||
#if !defined(HWY_DISABLED_TARGETS)
|
||||
static_assert(false, \"HWY_DISABLED_TARGETS is not defined\");
|
||||
#endif
|
||||
return 0;
|
||||
}"
|
||||
JXL_HWY_DISABLED_TARGETS_FORCED
|
||||
)
|
||||
|
||||
set(WARNINGS_AS_ERRORS_DEFAULT false)
|
||||
|
||||
if((SANITIZER STREQUAL "msan") OR JPEGXL_EMSCRIPTEN)
|
||||
set(BUNDLE_LIBPNG_DEFAULT YES)
|
||||
else()
|
||||
set(BUNDLE_LIBPNG_DEFAULT NO)
|
||||
endif()
|
||||
|
||||
# Standard cmake naming for building shared libraries.
|
||||
get_property(SHARED_LIBS_SUPPORTED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones" ${SHARED_LIBS_SUPPORTED})
|
||||
|
||||
set(JPEGXL_ENABLE_FUZZERS ${ENABLE_FUZZERS_DEFAULT} CACHE BOOL
|
||||
"Build JPEGXL fuzzer targets.")
|
||||
set(JPEGXL_ENABLE_DEVTOOLS false CACHE BOOL
|
||||
"Build JPEGXL developer tools.")
|
||||
set(JPEGXL_ENABLE_TOOLS true CACHE BOOL
|
||||
"Build JPEGXL user tools: cjxl and djxl.")
|
||||
set(JPEGXL_ENABLE_JPEGLI true CACHE BOOL
|
||||
"Build jpegli library.")
|
||||
set(JPEGXL_ENABLE_JPEGLI_LIBJPEG true CACHE BOOL
|
||||
"Build libjpeg.so shared library based on jpegli.")
|
||||
set(JPEGXL_INSTALL_JPEGLI_LIBJPEG false CACHE BOOL
|
||||
"Install jpegli version of libjpeg.so system-wide.")
|
||||
set(JPEGLI_LIBJPEG_LIBRARY_VERSION "62.3.0" CACHE STRING
|
||||
"Library version of the libjpeg.so shared library that we build.")
|
||||
set(JPEGLI_LIBJPEG_LIBRARY_SOVERSION "62" CACHE STRING
|
||||
"Library so-version of the libjpeg.so shared library that we build.")
|
||||
set(JPEGXL_ENABLE_DOXYGEN true CACHE BOOL
|
||||
"Generate C API documentation using Doxygen.")
|
||||
set(JPEGXL_ENABLE_MANPAGES true CACHE BOOL
|
||||
"Build and install man pages for the command-line tools.")
|
||||
set(JPEGXL_ENABLE_BENCHMARK true CACHE BOOL
|
||||
"Build JPEGXL benchmark tools.")
|
||||
set(JPEGXL_ENABLE_EXAMPLES true CACHE BOOL
|
||||
"Build JPEGXL library usage examples.")
|
||||
set(JPEGXL_BUNDLE_LIBPNG ${BUNDLE_LIBPNG_DEFAULT} CACHE BOOL
|
||||
"Build libpng from source and link it statically.")
|
||||
set(JPEGXL_ENABLE_JNI true CACHE BOOL
|
||||
"Build JPEGXL JNI Java wrapper, if Java dependencies are installed.")
|
||||
set(JPEGXL_ENABLE_SJPEG true CACHE BOOL
|
||||
"Build JPEGXL with support for encoding with sjpeg.")
|
||||
set(JPEGXL_ENABLE_OPENEXR true CACHE BOOL
|
||||
"Build JPEGXL with support for OpenEXR if available.")
|
||||
set(JPEGXL_ENABLE_SKCMS true CACHE BOOL
|
||||
"Build with skcms instead of lcms2.")
|
||||
set(JPEGXL_BUNDLE_SKCMS true CACHE BOOL
|
||||
"When building with skcms, bundle it into libjxl.a.")
|
||||
set(JPEGXL_ENABLE_VIEWERS false CACHE BOOL
|
||||
"Build JPEGXL viewer tools for evaluation.")
|
||||
set(JPEGXL_ENABLE_TCMALLOC ${ENABLE_TCMALLOC_DEFAULT} CACHE BOOL
|
||||
"Build JPEGXL using gperftools (tcmalloc) allocator.")
|
||||
set(JPEGXL_ENABLE_PLUGINS false CACHE BOOL
|
||||
"Build third-party plugins to support JPEG XL in other applications.")
|
||||
set(JPEGXL_ENABLE_COVERAGE false CACHE BOOL
|
||||
"Enable code coverage tracking for libjxl. This also enables debug and disables optimizations.")
|
||||
set(JPEGXL_ENABLE_SIZELESS_VECTORS false CACHE BOOL
|
||||
"Builds in support for SVE/RVV vectorization")
|
||||
set(JPEGXL_ENABLE_TRANSCODE_JPEG true CACHE BOOL
|
||||
"Builds in support for decoding transcoded JXL files back to JPEG,\
|
||||
disabling it makes the decoder reject JXL_DEC_JPEG_RECONSTRUCTION events,\
|
||||
(default enabled)")
|
||||
set(JPEGXL_ENABLE_BOXES true CACHE BOOL
|
||||
"Builds in support for decoding boxes in JXL files,\
|
||||
disabling it makes the decoder reject JXL_DEC_BOX events,\
|
||||
(default enabled)")
|
||||
set(JPEGXL_STATIC false CACHE BOOL
|
||||
"Build tools as static binaries.")
|
||||
set(JPEGXL_WARNINGS_AS_ERRORS ${WARNINGS_AS_ERRORS_DEFAULT} CACHE BOOL
|
||||
"Treat warnings as errors during compilation.")
|
||||
set(JPEGXL_DEP_LICENSE_DIR "" CACHE STRING
|
||||
"Directory where to search for system dependencies \"copyright\" files.")
|
||||
set(JPEGXL_FORCE_NEON false CACHE BOOL
|
||||
"Set flags to enable NEON in arm if not enabled by your toolchain.")
|
||||
set(JPEGXL_TEST_TOOLS false CACHE BOOL
|
||||
"Run scripts that test the encoding / decoding tools.")
|
||||
set(JPEGXL_ENABLE_AVX512 false CACHE BOOL
|
||||
"Build with AVX512 support (faster on CPUs that support it, but larger binary size).")
|
||||
set(JPEGXL_ENABLE_AVX512_ZEN4 false CACHE BOOL
|
||||
"Build with Zen4-optimized AVX512 support (faster on CPUs that support it, but larger binary size).")
|
||||
|
||||
# Force system dependencies.
|
||||
set(JPEGXL_FORCE_SYSTEM_BROTLI false CACHE BOOL
|
||||
"Force using system installed brotli instead of third_party/brotli source.")
|
||||
set(JPEGXL_FORCE_SYSTEM_GTEST false CACHE BOOL
|
||||
"Force using system installed googletest (gtest/gmock) instead of third_party/googletest source.")
|
||||
set(JPEGXL_FORCE_SYSTEM_LCMS2 false CACHE BOOL
|
||||
"Force using system installed lcms2 instead of third_party/lcms source.")
|
||||
set(JPEGXL_FORCE_SYSTEM_HWY false CACHE BOOL
|
||||
"Force using system installed highway (libhwy-dev) instead of third_party/highway source.")
|
||||
|
||||
# Check minimum compiler versions. Older compilers are not supported and fail
|
||||
# with hard to understand errors.
|
||||
if (NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
|
||||
message(FATAL_ERROR "Different C/C++ compilers set: "
|
||||
"${CMAKE_C_COMPILER_ID} vs ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# Android NDK's toolchain.cmake fakes the clang version in
|
||||
# CMAKE_CXX_COMPILER_VERSION with an incorrect number, so ignore this.
|
||||
if (NOT CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION MATCHES "clang"
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
|
||||
message(FATAL_ERROR
|
||||
"Minimum Clang version required is Clang 5, please update.")
|
||||
endif()
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7)
|
||||
message(FATAL_ERROR
|
||||
"Minimum GCC version required is 7, please update.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS
|
||||
"Compiled IDs C:${CMAKE_C_COMPILER_ID}, C++:${CMAKE_CXX_COMPILER_ID}")
|
||||
|
||||
# Always disable SSSE3 since it is rare to have SSSE3 but not SSE4
|
||||
set(HWY_DISABLED_TARGETS "HWY_SSSE3")
|
||||
if (NOT JPEGXL_ENABLE_AVX512)
|
||||
message(STATUS "Disabled AVX512 (set JPEGXL_ENABLE_AVX512 to enable it)")
|
||||
set(HWY_DISABLED_TARGETS "${HWY_DISABLED_TARGETS}|HWY_AVX3")
|
||||
add_definitions(-DFJXL_ENABLE_AVX512=0)
|
||||
endif()
|
||||
if (NOT JPEGXL_ENABLE_AVX512_ZEN4)
|
||||
message(STATUS "Disabled AVX512_ZEN4 (set JPEGXL_ENABLE_AVX512_ZEN4 to enable it)")
|
||||
set(HWY_DISABLED_TARGETS "${HWY_DISABLED_TARGETS}|HWY_AVX3_ZEN4")
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
# CMAKE_EXPORT_COMPILE_COMMANDS is used to generate the compilation database
|
||||
# used by clang-tidy.
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if(JPEGXL_STATIC)
|
||||
set(BUILD_SHARED_LIBS 0)
|
||||
# Clang developers say that in case to use "static" we have to build stdlib
|
||||
# ourselves; for real use case we don't care about stdlib, as it is "granted",
|
||||
# so just linking all other libraries is fine.
|
||||
if (NOT MSVC AND NOT APPLE)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
|
||||
set(CMAKE_EXE_LINKER_FLAGS
|
||||
"${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++")
|
||||
endif()
|
||||
endif() # JPEGXL_STATIC
|
||||
|
||||
# Threads
|
||||
set(THREADS_PREFER_PTHREAD_FLAG YES)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# These settings are important to drive check_cxx_source_compiles
|
||||
# See CMP0067 (min cmake version is 3.10 anyway)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
|
||||
# Atomics
|
||||
find_package(Atomics REQUIRED)
|
||||
|
||||
if(JPEGXL_STATIC)
|
||||
if (MINGW)
|
||||
# In MINGW libstdc++ uses pthreads directly. When building statically a
|
||||
# program (regardless of whether the source code uses pthread or not) the
|
||||
# toolchain will add stdc++ and pthread to the linking step but stdc++ will
|
||||
# be linked statically while pthread will be linked dynamically.
|
||||
# To avoid this and have pthread statically linked with need to pass it in
|
||||
# the command line with "-Wl,-Bstatic -lpthread -Wl,-Bdynamic" but the
|
||||
# linker will discard it if not used by anything else up to that point in
|
||||
# the linker command line. If the program or any dependency don't use
|
||||
# pthread directly -lpthread is discarded and libstdc++ (added by the
|
||||
# toolchain later) will then use the dynamic version. For this we also need
|
||||
# to pass -lstdc++ explicitly before -lpthread. For pure C programs -lstdc++
|
||||
# will be discarded anyway.
|
||||
# This adds these flags as dependencies for *all* targets. Adding this to
|
||||
# CMAKE_EXE_LINKER_FLAGS instead would cause them to be included before any
|
||||
# object files and therefore discarded. This should be set in the
|
||||
# INTERFACE_LINK_LIBRARIES of Threads::Threads but some third_part targets
|
||||
# don't depend on it.
|
||||
link_libraries(-Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic)
|
||||
elseif(CMAKE_USE_PTHREADS_INIT)
|
||||
# "whole-archive" is not supported on OSX.
|
||||
if (NOT APPLE)
|
||||
# Set pthreads as a whole-archive, otherwise weak symbols in the static
|
||||
# libraries will discard pthreads symbols leading to segmentation fault at
|
||||
# runtime.
|
||||
message(STATUS "Using -lpthread as --whole-archive")
|
||||
set_target_properties(Threads::Threads PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"-Wl,--whole-archive;-lpthread;-Wl,--no-whole-archive")
|
||||
endif()
|
||||
endif()
|
||||
endif() # JPEGXL_STATIC
|
||||
|
||||
if (JPEGXL_EMSCRIPTEN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||
endif()
|
||||
|
||||
if (CXX_MACRO_PREFIX_MAP)
|
||||
add_compile_options(-fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
|
||||
endif()
|
||||
|
||||
if (CXX_NO_RTTI_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
endif()
|
||||
|
||||
# Internal flags for coverage builds:
|
||||
set(JPEGXL_COVERAGE_FLAGS)
|
||||
set(JPEGXL_COVERAGE_LINK_FLAGS)
|
||||
|
||||
if (MSVC)
|
||||
# TODO(janwas): add flags
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
else ()
|
||||
# Global compiler flags for all targets here and in subdirectories.
|
||||
add_definitions(
|
||||
# Avoid changing the binary based on the current time and date.
|
||||
-D__DATE__="redacted"
|
||||
-D__TIMESTAMP__="redacted"
|
||||
-D__TIME__="redacted"
|
||||
)
|
||||
|
||||
# TODO(eustas): JXL currently compiles, but does not pass tests...
|
||||
if (NOT JXL_HWY_DISABLED_TARGETS_FORCED)
|
||||
if (NOT JPEGXL_ENABLE_SIZELESS_VECTORS)
|
||||
set(HWY_DISABLED_TARGETS "${HWY_DISABLED_TARGETS}|HWY_SVE|HWY_SVE2|HWY_SVE_256|HWY_SVE2_128|HWY_RVV")
|
||||
endif()
|
||||
add_definitions(-DHWY_DISABLED_TARGETS=\(${HWY_DISABLED_TARGETS}\))
|
||||
endif()
|
||||
|
||||
# In CMake before 3.12 it is problematic to pass repeated flags like -Xclang.
|
||||
# For this reason we place them in CMAKE_CXX_FLAGS instead.
|
||||
# See https://gitlab.kitware.com/cmake/cmake/issues/15826
|
||||
|
||||
# Machine flags.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funwind-tables")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -mrelax-all")
|
||||
endif()
|
||||
if (CXX_CONSTRUCTOR_ALIASES_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -mconstructor-aliases")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
# Not supported by clang-cl, but frame pointers are default on Windows
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
|
||||
# CPU flags - remove once we have NEON dynamic dispatch
|
||||
|
||||
# TODO(janwas): this also matches M1, but only ARMv7 is intended/needed.
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
|
||||
if(JPEGXL_FORCE_NEON)
|
||||
# GCC requires these flags, otherwise __ARM_NEON is undefined.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
|
||||
-mfpu=neon-vfpv4 -mfloat-abi=hard")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Force build with optimizations in release mode.
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
|
||||
|
||||
add_compile_options(
|
||||
# Ignore this to allow redefining __DATE__ and others.
|
||||
-Wno-builtin-macro-redefined
|
||||
|
||||
# Global warning settings.
|
||||
-Wall
|
||||
)
|
||||
|
||||
if (JPEGXL_WARNINGS_AS_ERRORS)
|
||||
add_compile_options(-Werror)
|
||||
endif ()
|
||||
|
||||
if(JPEGXL_ENABLE_COVERAGE)
|
||||
set(JPEGXL_COVERAGE_FLAGS
|
||||
-g -O0 -fprofile-arcs -ftest-coverage
|
||||
-DJXL_ENABLE_ASSERT=0 -DJXL_ENABLE_CHECK=0
|
||||
)
|
||||
set(JPEGXL_COVERAGE_LINK_FLAGS
|
||||
--coverage
|
||||
)
|
||||
endif() # JPEGXL_ENABLE_COVERAGE
|
||||
endif () # !MSVC
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Separately build/configure testing frameworks and other third_party libraries
|
||||
# to allow disabling tests in those libraries.
|
||||
include(third_party/testing.cmake)
|
||||
add_subdirectory(third_party)
|
||||
# Copy the JXL license file to the output build directory.
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/LICENSE"
|
||||
${PROJECT_BINARY_DIR}/LICENSE.jpeg-xl COPYONLY)
|
||||
|
||||
# Enable tests regardless of where they are defined.
|
||||
enable_testing()
|
||||
include(CTest)
|
||||
# Specify default location of `testdata`:
|
||||
if(NOT DEFINED JPEGXL_TEST_DATA_PATH)
|
||||
set(JPEGXL_TEST_DATA_PATH "${PROJECT_SOURCE_DIR}/testdata")
|
||||
endif()
|
||||
|
||||
# Libraries.
|
||||
add_subdirectory(lib)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
# Script to run tests over the source code in bash.
|
||||
find_program (BASH_PROGRAM bash)
|
||||
if(BASH_PROGRAM)
|
||||
add_test(
|
||||
NAME bash_test
|
||||
COMMAND ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/bash_test.sh)
|
||||
endif()
|
||||
endif() # BUILD_TESTING
|
||||
|
||||
# Documentation generated by Doxygen
|
||||
if(JPEGXL_ENABLE_DOXYGEN)
|
||||
find_package(Doxygen)
|
||||
if(DOXYGEN_FOUND)
|
||||
set(DOXYGEN_GENERATE_HTML "YES")
|
||||
set(DOXYGEN_GENERATE_XML "YES")
|
||||
set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/include")
|
||||
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "README.md")
|
||||
if(JPEGXL_WARNINGS_AS_ERRORS)
|
||||
set(DOXYGEN_WARN_AS_ERROR "YES")
|
||||
endif()
|
||||
set(DOXYGEN_QUIET "YES")
|
||||
doxygen_add_docs(doc
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/lib/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/doc/api.txt"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
COMMENT "Generating C API documentation")
|
||||
|
||||
# Add sphinx doc build step for readthedocs.io (requires doxygen too).
|
||||
find_program(SPHINX_BUILD_PROGRAM sphinx-build)
|
||||
if(SPHINX_BUILD_PROGRAM)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent"
|
||||
COMMENT "Generating readthedocs.io output on ${CMAKE_CURRENT_BINARY_DIR}/rtd"
|
||||
COMMAND ${SPHINX_BUILD_PROGRAM} -q -W -b html -j auto
|
||||
${CMAKE_SOURCE_DIR}/doc/sphinx
|
||||
${CMAKE_CURRENT_BINARY_DIR}/rtd
|
||||
DEPENDS doc
|
||||
)
|
||||
# This command runs the documentation generation every time since the output
|
||||
# target file doesn't exist.
|
||||
add_custom_target(rtd-html
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent
|
||||
)
|
||||
else() # SPHINX_BUILD_PROGRAM\
|
||||
message(WARNING "sphinx-build not found, skipping rtd documentation")
|
||||
endif() # SPHINX_BUILD_PROGRAM
|
||||
|
||||
else()
|
||||
# Create a "doc" target for compatibility since "doc" is not otherwise added to
|
||||
# the build when doxygen is not installed.
|
||||
add_custom_target(doc false
|
||||
COMMENT "Error: Can't generate doc since Doxygen not installed.")
|
||||
endif() # DOXYGEN_FOUND
|
||||
endif() # JPEGXL_ENABLE_DOXYGEN
|
||||
|
||||
if(JPEGXL_ENABLE_MANPAGES)
|
||||
find_program(ASCIIDOC a2x)
|
||||
if(ASCIIDOC)
|
||||
file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1)
|
||||
if(ASCIIDOC_SHEBANG MATCHES "/sh|/bash" OR MINGW)
|
||||
set(ASCIIDOC_PY_FOUND ON)
|
||||
# Run the program directly and set ASCIIDOC as empty.
|
||||
set(ASCIIDOC_PY "${ASCIIDOC}")
|
||||
set(ASCIIDOC "")
|
||||
elseif(ASCIIDOC_SHEBANG MATCHES "python2")
|
||||
find_package(Python2 COMPONENTS Interpreter)
|
||||
set(ASCIIDOC_PY_FOUND "${Python2_Interpreter_FOUND}")
|
||||
set(ASCIIDOC_PY Python2::Interpreter)
|
||||
elseif(ASCIIDOC_SHEBANG MATCHES "python3")
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
set(ASCIIDOC_PY_FOUND "${Python3_Interpreter_FOUND}")
|
||||
set(ASCIIDOC_PY Python3::Interpreter)
|
||||
else()
|
||||
find_package(Python COMPONENTS Interpreter QUIET)
|
||||
if(NOT Python_Interpreter_FOUND)
|
||||
find_program(ASCIIDOC_PY python)
|
||||
if(ASCIIDOC_PY)
|
||||
set(ASCIIDOC_PY_FOUND ON)
|
||||
endif()
|
||||
else()
|
||||
set(ASCIIDOC_PY_FOUND "${Python_Interpreter_FOUND}")
|
||||
set(ASCIIDOC_PY Python::Interpreter)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ASCIIDOC_PY_FOUND)
|
||||
set(MANPAGE_FILES "")
|
||||
set(MANPAGES "")
|
||||
foreach(PAGE IN ITEMS cjxl djxl)
|
||||
# Invoking the Python interpreter ourselves instead of running the a2x binary
|
||||
# directly is necessary on MSYS2, otherwise it is run through cmd.exe which
|
||||
# does not recognize it.
|
||||
add_custom_command(
|
||||
OUTPUT "${PAGE}.1"
|
||||
COMMAND "${ASCIIDOC_PY}"
|
||||
ARGS ${ASCIIDOC}
|
||||
--format manpage --destination-dir="${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt"
|
||||
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt")
|
||||
list(APPEND MANPAGE_FILES "${CMAKE_CURRENT_BINARY_DIR}/${PAGE}.1")
|
||||
list(APPEND MANPAGES "${PAGE}.1")
|
||||
endforeach()
|
||||
add_custom_target(manpages ALL DEPENDS ${MANPAGES})
|
||||
install(FILES ${MANPAGE_FILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||
endif() # ASCIIDOC_PY_FOUND
|
||||
else()
|
||||
message(WARNING "asciidoc was not found, the man pages will not be installed.")
|
||||
endif() # ASCIIDOC
|
||||
endif() # JPEGXL_ENABLE_MANPAGES
|
||||
|
||||
# Example usage code.
|
||||
if (JPEGXL_ENABLE_EXAMPLES)
|
||||
include(examples/examples.cmake)
|
||||
endif ()
|
||||
|
||||
# Plugins for third-party software
|
||||
if (JPEGXL_ENABLE_PLUGINS)
|
||||
add_subdirectory(plugins)
|
||||
endif ()
|
||||
|
||||
# Binary tools
|
||||
add_subdirectory(tools)
|
93
third-party/libjxl/libjxl/CODE_OF_CONDUCT.md
vendored
Normal file
93
third-party/libjxl/libjxl/CODE_OF_CONDUCT.md
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
# Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of
|
||||
experience, education, socio-economic status, nationality, personal appearance,
|
||||
race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, or to ban temporarily or permanently any
|
||||
contributor for other behaviors that they deem inappropriate, threatening,
|
||||
offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
This Code of Conduct also applies outside the project spaces when the Project
|
||||
Steward has a reasonable belief that an individual's behavior may have a
|
||||
negative impact on the project or its community.
|
||||
|
||||
## Conflict Resolution
|
||||
|
||||
We do not believe that all conflict is bad; healthy debate and disagreement
|
||||
often yield positive results. However, it is never okay to be disrespectful or
|
||||
to engage in behavior that violates the project’s code of conduct.
|
||||
|
||||
If you see someone violating the code of conduct, you are encouraged to address
|
||||
the behavior directly with those involved. Many issues can be resolved quickly
|
||||
and easily, and this gives people more control over the outcome of their
|
||||
dispute. If you are unable to resolve the matter for any reason, or if the
|
||||
behavior is threatening or harassing, report it. We are dedicated to providing
|
||||
an environment where participants feel welcome and safe.
|
||||
|
||||
Reports should be directed to Jyrki Alakuijala <jyrki@google.com>, the
|
||||
Project Steward(s) for JPEG XL. It is the Project Steward’s duty to
|
||||
receive and address reported violations of the code of conduct. They will then
|
||||
work with a committee consisting of representatives from the Open Source
|
||||
Programs Office and the Google Open Source Strategy team. If for any reason you
|
||||
are uncomfortable reaching out to the Project Steward, please email
|
||||
opensource@google.com.
|
||||
|
||||
We will investigate every complaint, but you may not receive a direct response.
|
||||
We will use our discretion in determining when and how to follow up on reported
|
||||
incidents, which may range from not taking action to permanent expulsion from
|
||||
the project and project-sponsored spaces. We will notify the accused of the
|
||||
report and provide them an opportunity to discuss it before any action is taken.
|
||||
The identity of the reporter will be omitted from the details of the report
|
||||
supplied to the accused. In potentially harmful situations, such as ongoing
|
||||
harassment or threats to anyone's safety, we may take action without notice.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
|
||||
available at
|
||||
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
132
third-party/libjxl/libjxl/CONTRIBUTING.md
vendored
Normal file
132
third-party/libjxl/libjxl/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
# Contributing to libjxl
|
||||
|
||||
## Contributing with bug reports
|
||||
|
||||
For security-related issues please see [SECURITY.md](SECURITY.md).
|
||||
|
||||
We welcome suggestions, feature requests and bug reports. Before opening a new
|
||||
issue please take a look if there is already an existing one in the following
|
||||
link:
|
||||
|
||||
* https://github.com/libjxl/libjxl/issues
|
||||
|
||||
## Contributing with patches and Pull Requests
|
||||
|
||||
We'd love to accept your contributions to the JPEG XL Project. Please read
|
||||
through this section before sending a Pull Request.
|
||||
|
||||
### Contributor License Agreements
|
||||
|
||||
Our project is open source under the terms outlined in the [LICENSE](LICENSE)
|
||||
and [PATENTS](PATENTS) files. Before we can accept your contributions, even for
|
||||
small changes, there are just a few small guidelines you need to follow:
|
||||
|
||||
Please fill out either the individual or corporate Contributor License Agreement
|
||||
(CLA) with Google. JPEG XL Project is an an effort by multiple individuals and
|
||||
companies, including the initial contributors Cloudinary and Google, but Google
|
||||
is the legal entity in charge of receiving these CLA and relicensing this
|
||||
software:
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an [individual
|
||||
CLA](https://code.google.com/legal/individual-cla-v1.0.html).
|
||||
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a [corporate
|
||||
CLA](https://code.google.com/legal/corporate-cla-v1.0.html).
|
||||
|
||||
Follow either of the two links above to access the appropriate CLA and
|
||||
instructions for how to sign and return it. Once we receive it, we'll be able
|
||||
to accept your pull requests.
|
||||
|
||||
***NOTE***: Only original source code from you and other people that have signed
|
||||
the CLA can be accepted into the main repository.
|
||||
|
||||
### License
|
||||
|
||||
Contributions are licensed under the project's [LICENSE](LICENSE). Each new
|
||||
file must include the following header when possible, with comment style adapted
|
||||
to the language as needed:
|
||||
|
||||
```
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
```
|
||||
|
||||
### Code Reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. Consult
|
||||
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
|
||||
information on using pull requests.
|
||||
|
||||
### Contribution philosophy
|
||||
|
||||
* Prefer small changes, even if they don't implement a complete feature. Small
|
||||
changes are easier to review and can be submitted faster. Think about what's
|
||||
the smallest unit you can send that makes sense to review and submit in
|
||||
isolation. For example, new modules that are not yet used by the tools but
|
||||
have their own unittests are ok. If you have unrelated changes that
|
||||
you discovered while working on something else, please send them in a
|
||||
different Pull Request. If your are refactoring code and changing
|
||||
functionality try to send the refactor first without any change in
|
||||
functionality. Reviewers may ask you to split a Pull Request and it is
|
||||
easier to create a smaller change from the beginning.
|
||||
|
||||
* Describe your commits. Add a meaningful description to your commit message, explain what you are changing if it is not trivially obvious, but more importantly explain *why* you are making those changes. For example "Fix
|
||||
build" is not a good commit message, describe what build and if it makes sense
|
||||
why is this fixing it or why was it failing without this. It is very likely
|
||||
that people far in the future without any context you have right now will be
|
||||
looking at your commit trying to figure out why was the change introduced. If
|
||||
related to an issue in this or another repository include a link to it.
|
||||
|
||||
* Code Style: We follow the [Google C++ Coding
|
||||
Style](https://google.github.io/styleguide/cppguide.html). A
|
||||
[clang-format](https://clang.llvm.org/docs/ClangFormat.html) configuration
|
||||
file is available to automatically format your code, you can invoke it with
|
||||
the `./ci.sh lint` helper tool.
|
||||
|
||||
* Testing: Test your change and explain in the commit message *how* your
|
||||
commit was tested. For example adding unittests or in some cases just testing
|
||||
with the existing ones is enough. In any case, mention what testing was
|
||||
performed so reviewers can evaluate whether that's enough testing. In many
|
||||
cases, testing that the Continuous Integration workflow passes is enough.
|
||||
|
||||
* Make one commit per Pull Request / review, unless there's a good reason not
|
||||
to. If you have multiple changes send multiple Pull Requests and each one can
|
||||
have its own review.
|
||||
|
||||
* When addressing comments from reviewers prefer to squash or fixup your
|
||||
edits and force-push your commit. When merging changes into the repository we
|
||||
don't want to include the history of code review back and forth changes or
|
||||
typos. Reviewers can click on the "force-pushed" automatic comment on a Pull
|
||||
Request to see the changes between versions. We use "Rebase and merge" policy
|
||||
to keep a linear git history which is easier to reason about.
|
||||
|
||||
* Your change must pass the build and test workflows. There's a `ci.sh` script
|
||||
to help building and testing these configurations. See [building and
|
||||
testing](doc/building_and_testing.md) for more details.
|
||||
|
||||
### Contributing checklist.
|
||||
|
||||
* Sign the CLA (only needed once per user, see above).
|
||||
|
||||
* AUTHORS: If this is your first contribution, add your name or your
|
||||
company name to the [AUTHORS](AUTHORS) file for copyright tracking purposes.
|
||||
|
||||
* Style guide. Check `./ci.sh lint`.
|
||||
|
||||
* Meaningful commit description: What and *why*, links to issues, testing
|
||||
procedure.
|
||||
|
||||
* Squashed multiple edits into a single commit.
|
||||
|
||||
* Upload your changes to your fork and [create a Pull
|
||||
Request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request).
|
||||
|
||||
# Community Guidelines
|
||||
|
||||
This project follows [Google's Open Source Community
|
||||
Guidelines](https://opensource.google.com/conduct/).
|
23
third-party/libjxl/libjxl/CONTRIBUTORS
vendored
Normal file
23
third-party/libjxl/libjxl/CONTRIBUTORS
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# This files lists individuals who made significant contributions to the JPEG XL
|
||||
# code base, such as design, adding features, performing experiments, ...
|
||||
# Small changes such as a small bugfix or fixing spelling errors are not
|
||||
# included. If you'd like to be included in this file thanks to a significant
|
||||
# contribution, feel free to send a pull request changing this file.
|
||||
Alex Deymo
|
||||
Alexander Rhatushnyak
|
||||
Evgenii Kliuchnikov
|
||||
Iulia-Maria Comșa
|
||||
Jan Wassenberg
|
||||
Jon Sneyers
|
||||
Jyrki Alakuijala
|
||||
Krzysztof Potempa
|
||||
Lode Vandevenne
|
||||
Luca Versari
|
||||
Martin Bruse
|
||||
Moritz Firsching
|
||||
Renata Khasanova
|
||||
Robert Obryk
|
||||
Sami Boukortt
|
||||
Sebastian Gomez-Gonzalez
|
||||
Thomas Fischbacher
|
||||
Zoltan Szabadka
|
27
third-party/libjxl/libjxl/LICENSE
vendored
Normal file
27
third-party/libjxl/libjxl/LICENSE
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) the JPEG XL Project Authors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
third-party/libjxl/libjxl/PATENTS
vendored
Normal file
22
third-party/libjxl/libjxl/PATENTS
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the JPEG XL project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of JPEG XL, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of JPEG XL. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of JPEG XL or any code incorporated within this
|
||||
implementation of JPEG XL constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of JPEG XL
|
||||
shall terminate as of the date such litigation is filed.
|
133
third-party/libjxl/libjxl/README.md
vendored
Normal file
133
third-party/libjxl/libjxl/README.md
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
# JPEG XL reference implementation
|
||||
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/build_test.yml)
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/build_test_cross.yml)
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/conformance.yml)
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/fuzz.yml)
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/release.yaml)
|
||||
[](
|
||||
https://libjxl.readthedocs.io/en/latest/?badge=latest)
|
||||
[](
|
||||
https://codecov.io/gh/libjxl/libjxl)
|
||||
|
||||
<img src="doc/jxl.svg" width="100" align="right" alt="JXL logo">
|
||||
|
||||
This repository contains a reference implementation of JPEG XL (encoder and
|
||||
decoder), called `libjxl`. This software library is
|
||||
[used by many applications that support JPEG XL](doc/software_support.md).
|
||||
|
||||
JPEG XL was standardized in 2022 as [ISO/IEC 18181](https://jpeg.org/jpegxl/workplan.html).
|
||||
The [core codestream](doc/format_overview.md#codestream-features) is specified in 18181-1,
|
||||
the [file format](doc/format_overview.md#file-format-features) in 18181-2.
|
||||
[Decoder conformance](https://github.com/libjxl/conformance) is defined in 18181-3,
|
||||
and 18181-4 is the [reference software](https://github.com/libjxl/libjxl).
|
||||
|
||||
The library API, command line options, and tools in this repository are subject
|
||||
to change, however files encoded with `cjxl` conform to the JPEG XL specification
|
||||
and can be decoded with current and future `djxl` decoders or the `libjxl` decoding library.
|
||||
|
||||
## Installation
|
||||
|
||||
In most Linux distributions, installing `libjxl` is just a matter of using the package management system.
|
||||
For example in Debian-based distributions: `apt install libjxl-tools` will install `cjxl` and `djxl`
|
||||
and other tools like `benchmark_xl` are available in the package `libjxl-devtools`.
|
||||
On MacOS, you can use [Homebrew](https://brew.sh/): `brew install jpeg-xl`.
|
||||
|
||||
[](https://repology.org/project/libjxl/versions)
|
||||
|
||||
From the [releases page](https://github.com/libjxl/libjxl/releases/) the following can be downloaded:
|
||||
- Windows binaries
|
||||
- Debian and Ubuntu .deb packages
|
||||
|
||||
Of course you can also [build libjxl from sources](BUILDING.md).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
To encode a source image to JPEG XL with default settings:
|
||||
|
||||
```bash
|
||||
cjxl input.png output.jxl
|
||||
```
|
||||
|
||||
The desired visual fidelity can be selected using the `--distance` parameter
|
||||
(in units of just-noticeable difference, where 0 is lossless and the most useful lossy range is 0.5 .. 3.0),
|
||||
or using `--quality` (on a scale from 0 to 100, roughly matching libjpeg).
|
||||
The [encode effort](doc/encode_effort.md) can be selected using the `--effort` parameter.
|
||||
|
||||
For more settings run `cjxl --help` or for a full list of options
|
||||
run `cjxl -v -v --help`.
|
||||
|
||||
To decode a JPEG XL file run:
|
||||
|
||||
```bash
|
||||
djxl input.jxl output.png
|
||||
```
|
||||
|
||||
When possible `cjxl`/`djxl` are able to read/write the following
|
||||
image formats: .exr, .gif, .jpeg/.jpg, .pfm, .pgm/.ppm, .pgx, .png.
|
||||
Specifically for JPEG files, the default `cjxl` behavior is to apply lossless
|
||||
recompression and the default `djxl` behavior is to reconstruct the original
|
||||
JPEG file (when the extension of the output file is .jpg).
|
||||
|
||||
### Benchmarking
|
||||
|
||||
For speed benchmarks on single images in single or multi-threaded decoding
|
||||
`djxl` can print decoding speed information. See `djxl --help` for details
|
||||
on the decoding options and note that the output image is optional for
|
||||
benchmarking purposes.
|
||||
|
||||
For more comprehensive benchmarking options, see the
|
||||
[benchmarking guide](doc/benchmarking.md).
|
||||
|
||||
### Library API
|
||||
|
||||
Besides the `libjxl` library [API documentation](https://libjxl.readthedocs.io/en/latest/),
|
||||
there are [example applications](examples/) and [plugins](plugins/) that can be used as a reference or
|
||||
starting point for developers who wish to integrate `libjxl` in their project.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This software is available under a 3-clause BSD license which can be found in
|
||||
the [LICENSE](LICENSE) file, with an "Additional IP Rights Grant" as outlined in
|
||||
the [PATENTS](PATENTS) file.
|
||||
|
||||
Please note that the PATENTS file only mentions Google since Google is the legal
|
||||
entity receiving the Contributor License Agreements (CLA) from all contributors
|
||||
to the JPEG XL Project, including the initial main contributors to the JPEG XL
|
||||
format: Cloudinary and Google.
|
||||
|
||||
## Additional documentation
|
||||
|
||||
### Codec description
|
||||
|
||||
* [JPEG XL Format Overview](doc/format_overview.md)
|
||||
* [Introductory paper](https://www.spiedigitallibrary.org/proceedings/Download?fullDOI=10.1117%2F12.2529237) (open-access)
|
||||
* [XL Overview](doc/xl_overview.md) - a brief introduction to the source code modules
|
||||
* [JPEG XL white paper](https://ds.jpeg.org/whitepapers/jpeg-xl-whitepaper.pdf)
|
||||
* [JPEG XL official website](https://jpeg.org/jpegxl)
|
||||
* [JPEG XL community website](https://jpegxl.info)
|
||||
|
||||
### Development process
|
||||
|
||||
* [More information on testing/build options](doc/building_and_testing.md)
|
||||
* [Git guide for JPEG XL](doc/developing_in_github.md) - for developers
|
||||
* [Fuzzing](doc/fuzzing.md) - for developers
|
||||
* [Building Web Assembly artifacts](doc/building_wasm.md)
|
||||
* [Test coverage on Codecov.io](https://app.codecov.io/gh/libjxl/libjxl) - for
|
||||
developers
|
||||
* [libjxl documentation on readthedocs.io](https://libjxl.readthedocs.io/)
|
||||
|
||||
### Contact
|
||||
|
||||
If you encounter a bug or other issue with the software, please open an Issue here.
|
||||
|
||||
There is a [subreddit about JPEG XL](https://www.reddit.com/r/jpegxl/), and
|
||||
informal chatting with developers and early adopters of `libjxl` can be done on the
|
||||
[JPEG XL Discord server](https://discord.gg/DqkQgDRTFu).
|
73
third-party/libjxl/libjxl/SECURITY.md
vendored
Normal file
73
third-party/libjxl/libjxl/SECURITY.md
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
# Security and Vulnerability Policy for libjxl
|
||||
|
||||
## TL;DR:
|
||||
|
||||
CPE prefix: `cpe:2.3:a:libjxl_project:libjxl`
|
||||
|
||||
To report a security issue, please email libjxl-security@google.com.
|
||||
|
||||
Include in your email a description of the issue, the steps you took to create
|
||||
the issue, affected versions, and if known, mitigations for the issue. Our
|
||||
vulnerability management team will acknowledge receiving your email within 3
|
||||
working days.
|
||||
|
||||
This project follows a 90 day disclosure timeline.
|
||||
|
||||
For all other bugs, where there are no security implications about disclosing
|
||||
the unpatched bug, open a [new issue](https://github.com/libjxl/libjxl/issues)
|
||||
checking first for existing similar issues. If in doubt about the security
|
||||
impact of a bug you discovered, email first.
|
||||
|
||||
## Policy overview
|
||||
|
||||
libjxl's Security Policy is based on the [Google Open Source program
|
||||
guidelines](https://github.com/google/oss-vulnerability-guide) for coordinated
|
||||
vulnerability disclosure.
|
||||
|
||||
Early versions of `libjxl` had a different security policy that didn't provide
|
||||
security and vulnerability disclosure support. Versions up to and including
|
||||
0.3.7 are not covered and won't receive any security advisory.
|
||||
|
||||
Only released versions, starting from version 0.5, are covered by this policy.
|
||||
Development branches, arbitrary commits from `main` branch or even releases with
|
||||
backported features externally patched on top are not covered. Only those
|
||||
versions with a release tag in `libjxl`'s repository are covered, starting from
|
||||
version 0.5.
|
||||
|
||||
## What's a "Security bug"
|
||||
|
||||
A security bug is a bug that can potentially be exploited to let an attacker
|
||||
gain unauthorized access or privileges such as disclosing information or
|
||||
arbitrary code execution. Not all fuzzer-found bugs and not all assert()
|
||||
failures are considered security bugs in libjxl. For a detailed explanation and
|
||||
examples see our [Security Vulnerabilities Playbook](doc/vuln_playbook.md).
|
||||
|
||||
## What to expect
|
||||
|
||||
To report a security issue, please email libjxl-security@google.com with all the
|
||||
details about the bug you encountered.
|
||||
|
||||
* Include a description of the issue, steps to reproduce, etc. Compiler
|
||||
versions, flags, exact version used and even CPU are often relevant given our
|
||||
usage of SIMD and run-time dispatch of SIMD instructions.
|
||||
|
||||
* A member of our security team will reply to you within 3 business days. Note
|
||||
that business days are different in different countries.
|
||||
|
||||
* We will evaluate the issue and we may require more input from your side to
|
||||
reproduce it.
|
||||
|
||||
* If the issue fits in the description of a security bug, we will issue a
|
||||
CVE, publish a fix and make a new minor or patch release with it. There is
|
||||
a maximum of 90 day disclosure timeline, we ask you to not publish the
|
||||
details before the 90 day deadline or the release date (whichever comes
|
||||
first).
|
||||
|
||||
* In the case that we publish a CVE we will credit the external researcher who
|
||||
reported the issue. When reporting security issues please let us know if you
|
||||
need to include specific information while doing so, like for example a
|
||||
company affiliation.
|
||||
|
||||
Our security team follows the [Security Vulnerabilities
|
||||
Playbook](doc/vuln_playbook.md). For more details about the process and policies
|
||||
please take a look at it.
|
768
third-party/libjxl/libjxl/WORKSPACE
vendored
Normal file
768
third-party/libjxl/libjxl/WORKSPACE
vendored
Normal file
@ -0,0 +1,768 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository", "new_git_repository")
|
||||
|
||||
http_archive(
|
||||
name = "bazel_skylib",
|
||||
sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
|
||||
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
|
||||
|
||||
bazel_skylib_workspace()
|
||||
|
||||
local_repository(
|
||||
name = "highway",
|
||||
path = "third_party/highway",
|
||||
)
|
||||
|
||||
local_repository(
|
||||
name = "brotli",
|
||||
path = "third_party/brotli",
|
||||
)
|
||||
|
||||
new_local_repository(
|
||||
name = "googletest",
|
||||
build_file = "third_party/googletest/BUILD.bazel",
|
||||
path = "third_party/googletest",
|
||||
)
|
||||
|
||||
new_local_repository(
|
||||
name = "skcms",
|
||||
build_file_content = """
|
||||
cc_library(
|
||||
name = "skcms",
|
||||
srcs = [
|
||||
"skcms.cc",
|
||||
"skcms_internal.h",
|
||||
"src/Transform_inl.h",
|
||||
],
|
||||
hdrs = ["skcms.h"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
path = "third_party/skcms",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "zlib",
|
||||
build_file_content = """
|
||||
cc_library(
|
||||
name = "zlib",
|
||||
defines = ["HAVE_UNISTD_H"],
|
||||
srcs = [
|
||||
"adler32.c",
|
||||
"compress.c",
|
||||
"crc32.c",
|
||||
"crc32.h",
|
||||
"deflate.c",
|
||||
"deflate.h",
|
||||
"gzclose.c",
|
||||
"gzguts.h",
|
||||
"gzlib.c",
|
||||
"gzread.c",
|
||||
"gzwrite.c",
|
||||
"infback.c",
|
||||
"inffast.c",
|
||||
"inffast.h",
|
||||
"inffixed.h",
|
||||
"inflate.c",
|
||||
"inflate.h",
|
||||
"inftrees.c",
|
||||
"inftrees.h",
|
||||
"trees.c",
|
||||
"trees.h",
|
||||
"uncompr.c",
|
||||
"zconf.h",
|
||||
"zutil.c",
|
||||
"zutil.h",
|
||||
],
|
||||
hdrs = ["zlib.h"],
|
||||
includes = ["."],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
remote = "https://github.com/madler/zlib",
|
||||
tag = "v1.2.13",
|
||||
)
|
||||
|
||||
new_local_repository(
|
||||
name = "png",
|
||||
build_file_content = """
|
||||
genrule(
|
||||
name = "pnglibconf",
|
||||
srcs = ["scripts/pnglibconf.h.prebuilt"],
|
||||
outs = ["pnglibconf.h"],
|
||||
cmd = "cp -f $< $@",
|
||||
)
|
||||
cc_library(
|
||||
name = "png",
|
||||
srcs = [
|
||||
"png.c",
|
||||
"pngconf.h",
|
||||
"pngdebug.h",
|
||||
"pngerror.c",
|
||||
"pngget.c",
|
||||
"pnginfo.h",
|
||||
":pnglibconf",
|
||||
"pngmem.c",
|
||||
"pngpread.c",
|
||||
"pngpriv.h",
|
||||
"pngread.c",
|
||||
"pngrio.c",
|
||||
"pngrtran.c",
|
||||
"pngrutil.c",
|
||||
"pngset.c",
|
||||
"pngstruct.h",
|
||||
"pngtrans.c",
|
||||
"pngwio.c",
|
||||
"pngwrite.c",
|
||||
"pngwtran.c",
|
||||
"pngwutil.c",
|
||||
],
|
||||
hdrs = ["png.h"],
|
||||
includes = ["."],
|
||||
linkopts = ["-lm"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["@zlib//:zlib"],
|
||||
)
|
||||
""",
|
||||
path = "third_party/libpng",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "libjpeg_turbo",
|
||||
build_file_content = """
|
||||
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
|
||||
SUBSTITUTIONS = {
|
||||
"@BUILD@" : "20230208",
|
||||
"@CMAKE_PROJECT_NAME@" : "libjpeg-turbo",
|
||||
"@COPYRIGHT_YEAR@" : "2023",
|
||||
"@INLINE@" : "__inline__",
|
||||
"@JPEG_LIB_VERSION@" : "62",
|
||||
"@LIBJPEG_TURBO_VERSION_NUMBER@" : "2001091",
|
||||
"@SIZE_T@" : "8",
|
||||
"@THREAD_LOCAL@" : "__thread",
|
||||
"@VERSION@" : "2.1.91",
|
||||
}
|
||||
YES_DEFINES = [
|
||||
"C_ARITH_CODING_SUPPORTED", "D_ARITH_CODING_SUPPORTED",
|
||||
"HAVE_BUILTIN_CTZL", "MEM_SRCDST_SUPPORTED"
|
||||
]
|
||||
NO_DEFINES = [
|
||||
"WITH_SIMD", "RIGHT_SHIFT_IS_UNSIGNED", "HAVE_INTRIN_H"
|
||||
]
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine " + key : "#define " + key for key in YES_DEFINES
|
||||
})
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine " + key : "// #define " + key for key in NO_DEFINES
|
||||
})
|
||||
[
|
||||
expand_template(
|
||||
name = "expand_" + src,
|
||||
template = src + ".in",
|
||||
out = src,
|
||||
substitutions = SUBSTITUTIONS,
|
||||
visibility = ["//visibility:public"],
|
||||
) for src in ["jconfig.h", "jconfigint.h", "jversion.h"]
|
||||
]
|
||||
JPEG16_SOURCES = [
|
||||
"jccolor.c",
|
||||
"jcdiffct.c",
|
||||
"jclossls.c",
|
||||
"jcmainct.c",
|
||||
"jcprepct.c",
|
||||
"jcsample.c",
|
||||
"jdcolor.c",
|
||||
"jddiffct.c",
|
||||
"jdlossls.c",
|
||||
"jdmainct.c",
|
||||
"jdmerge.c",
|
||||
"jdpostct.c",
|
||||
"jdsample.c",
|
||||
"jquant1.c",
|
||||
"jquant2.c",
|
||||
"jutils.c",
|
||||
]
|
||||
JPEG12_SOURCES = JPEG16_SOURCES + [
|
||||
"jccoefct.c",
|
||||
"jcdctmgr.c",
|
||||
"jdcoefct.c",
|
||||
"jddctmgr.c",
|
||||
"jfdctfst.c",
|
||||
"jfdctint.c",
|
||||
"jidctflt.c",
|
||||
"jidctfst.c",
|
||||
"jidctint.c",
|
||||
"jidctred.c",
|
||||
]
|
||||
JPEG_SOURCES = JPEG12_SOURCES + [
|
||||
"jaricom.c",
|
||||
"jcapimin.c",
|
||||
"jcapistd.c",
|
||||
"jcarith.c",
|
||||
"jchuff.c",
|
||||
"jcicc.c",
|
||||
"jcinit.c",
|
||||
"jclhuff.c",
|
||||
"jcmarker.c",
|
||||
"jcmaster.c",
|
||||
"jcomapi.c",
|
||||
"jcparam.c",
|
||||
"jcphuff.c",
|
||||
"jdapimin.c",
|
||||
"jdapistd.c",
|
||||
"jdarith.c",
|
||||
"jdatadst.c",
|
||||
"jdatasrc.c",
|
||||
"jdhuff.c",
|
||||
"jdicc.c",
|
||||
"jdinput.c",
|
||||
"jdlhuff.c",
|
||||
"jdmarker.c",
|
||||
"jdmaster.c",
|
||||
"jdphuff.c",
|
||||
"jdtrans.c",
|
||||
"jerror.c",
|
||||
"jfdctflt.c",
|
||||
"jmemmgr.c",
|
||||
"jmemnobs.c",
|
||||
]
|
||||
JPEG_HEADERS = [
|
||||
"jccolext.c",
|
||||
"jchuff.h",
|
||||
"jcmaster.h",
|
||||
"jconfig.h",
|
||||
"jconfigint.h",
|
||||
"jdcoefct.h",
|
||||
"jdcol565.c",
|
||||
"jdcolext.c",
|
||||
"jdct.h",
|
||||
"jdhuff.h",
|
||||
"jdmainct.h",
|
||||
"jdmaster.h",
|
||||
"jdmerge.h",
|
||||
"jdmrg565.c",
|
||||
"jdmrgext.c",
|
||||
"jdsample.h",
|
||||
"jerror.h",
|
||||
"jinclude.h",
|
||||
"jlossls.h",
|
||||
"jmemsys.h",
|
||||
"jmorecfg.h",
|
||||
"jpeg_nbits_table.h",
|
||||
"jpegapicomp.h",
|
||||
"jpegint.h",
|
||||
"jpeglib.h",
|
||||
"jsamplecomp.h",
|
||||
"jsimd.h",
|
||||
"jsimddct.h",
|
||||
"jstdhuff.c",
|
||||
"jversion.h",
|
||||
]
|
||||
cc_library(
|
||||
name = "jpeg16",
|
||||
srcs = JPEG16_SOURCES,
|
||||
hdrs = JPEG_HEADERS,
|
||||
local_defines = ["BITS_IN_JSAMPLE=16"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
cc_library(
|
||||
name = "jpeg12",
|
||||
srcs = JPEG12_SOURCES,
|
||||
hdrs = JPEG_HEADERS,
|
||||
local_defines = ["BITS_IN_JSAMPLE=12"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
cc_library(
|
||||
name = "jpeg",
|
||||
srcs = JPEG_SOURCES,
|
||||
hdrs = JPEG_HEADERS,
|
||||
deps = [":jpeg16", ":jpeg12"],
|
||||
includes = ["."],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
exports_files([
|
||||
"jmorecfg.h",
|
||||
"jpeglib.h",
|
||||
])
|
||||
""",
|
||||
remote = "https://github.com/libjpeg-turbo/libjpeg-turbo.git",
|
||||
tag = "2.1.91",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "gif",
|
||||
build_file_content = """
|
||||
cc_library(
|
||||
name = "gif",
|
||||
srcs = [
|
||||
"dgif_lib.c", "egif_lib.c", "gifalloc.c", "gif_err.c", "gif_font.c",
|
||||
"gif_hash.c", "openbsd-reallocarray.c", "gif_hash.h",
|
||||
"gif_lib_private.h"
|
||||
],
|
||||
hdrs = ["gif_lib.h"],
|
||||
includes = ["."],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
sha256 = "31da5562f44c5f15d63340a09a4fd62b48c45620cd302f77a6d9acf0077879bd",
|
||||
strip_prefix = "giflib-5.2.1",
|
||||
url = "https://netcologne.dl.sourceforge.net/project/giflib/giflib-5.2.1.tar.gz",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "imath",
|
||||
build_file_content = """
|
||||
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
|
||||
SUBSTITUTIONS = {
|
||||
"@IMATH_INTERNAL_NAMESPACE@": "Imath_3_1",
|
||||
"@IMATH_LIB_VERSION@": "3.1.4",
|
||||
"@IMATH_NAMESPACE_CUSTOM@": "0",
|
||||
"@IMATH_NAMESPACE@": "Imath",
|
||||
"@IMATH_PACKAGE_NAME@": "Imath 3.1.4",
|
||||
"@IMATH_VERSION_MAJOR@": "3",
|
||||
"@IMATH_VERSION_MINOR@": "1",
|
||||
"@IMATH_VERSION_PATCH@": "4",
|
||||
"@IMATH_VERSION@": "3.1.4",
|
||||
}
|
||||
YES_DEFINES = [
|
||||
"IMATH_HALF_USE_LOOKUP_TABLE", "IMATH_ENABLE_API_VISIBILITY",
|
||||
]
|
||||
NO_DEFINES = [
|
||||
"IMATH_HAVE_LARGE_STACK",
|
||||
]
|
||||
ONE_DEFINES = [
|
||||
"IMATH_USE_NOEXCEPT",
|
||||
]
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine " + key : "#define " + key for key in YES_DEFINES
|
||||
})
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine " + key : "// #define " + key for key in NO_DEFINES
|
||||
})
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine01 " + key : "#define " + key + " 1" for key in ONE_DEFINES
|
||||
})
|
||||
expand_template(
|
||||
name = "expand_ImathConfig",
|
||||
template = "config/ImathConfig.h.in",
|
||||
out = "src/Imath/ImathConfig.h",
|
||||
substitutions = SUBSTITUTIONS,
|
||||
)
|
||||
cc_library(
|
||||
name = "Imath",
|
||||
srcs = [
|
||||
"src/Imath/ImathColorAlgo.cpp",
|
||||
":src/Imath/ImathConfig.h",
|
||||
"src/Imath/ImathFun.cpp",
|
||||
"src/Imath/ImathMatrixAlgo.cpp",
|
||||
"src/Imath/ImathRandom.cpp",
|
||||
"src/Imath/half.cpp",
|
||||
"src/Imath/toFloat.h",
|
||||
],
|
||||
hdrs = [
|
||||
"src/Imath/ImathBox.h",
|
||||
"src/Imath/ImathBoxAlgo.h",
|
||||
"src/Imath/ImathColor.h",
|
||||
"src/Imath/ImathColorAlgo.h",
|
||||
"src/Imath/ImathEuler.h",
|
||||
"src/Imath/ImathExport.h",
|
||||
"src/Imath/ImathForward.h",
|
||||
"src/Imath/ImathFrame.h",
|
||||
"src/Imath/ImathFrustum.h",
|
||||
"src/Imath/ImathFrustumTest.h",
|
||||
"src/Imath/ImathFun.h",
|
||||
"src/Imath/ImathGL.h",
|
||||
"src/Imath/ImathGLU.h",
|
||||
"src/Imath/ImathInt64.h",
|
||||
"src/Imath/ImathInterval.h",
|
||||
"src/Imath/ImathLine.h",
|
||||
"src/Imath/ImathLineAlgo.h",
|
||||
"src/Imath/ImathMath.h",
|
||||
"src/Imath/ImathMatrix.h",
|
||||
"src/Imath/ImathMatrixAlgo.h",
|
||||
"src/Imath/ImathNamespace.h",
|
||||
"src/Imath/ImathPlane.h",
|
||||
"src/Imath/ImathPlatform.h",
|
||||
"src/Imath/ImathQuat.h",
|
||||
"src/Imath/ImathRandom.h",
|
||||
"src/Imath/ImathRoots.h",
|
||||
"src/Imath/ImathShear.h",
|
||||
"src/Imath/ImathSphere.h",
|
||||
"src/Imath/ImathTypeTraits.h",
|
||||
"src/Imath/ImathVec.h",
|
||||
"src/Imath/ImathVecAlgo.h",
|
||||
"src/Imath/half.h",
|
||||
"src/Imath/halfFunction.h",
|
||||
"src/Imath/halfLimits.h",
|
||||
],
|
||||
includes = ["src/Imath"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
remote = "https://github.com/AcademySoftwareFoundation/imath",
|
||||
tag = "v3.1.5",
|
||||
)
|
||||
|
||||
new_git_repository(
|
||||
name = "openexr",
|
||||
build_file_content = """
|
||||
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
|
||||
SUBSTITUTIONS = {
|
||||
"@IEX_INTERNAL_NAMESPACE@": "Iex_3_0",
|
||||
"@IEX_NAMESPACE_CUSTOM@": "0",
|
||||
"@IEX_NAMESPACE@": "Iex",
|
||||
"@ILMTHREAD_INTERNAL_NAMESPACE@": "IlmThread_3_0",
|
||||
"@ILMTHREAD_NAMESPACE_CUSTOM@": "0",
|
||||
"@ILMTHREAD_NAMESPACE@": "IlmThread",
|
||||
"@OPENEXR_IMF_NAMESPACE@": "Imf",
|
||||
"@OPENEXR_INTERNAL_IMF_NAMESPACE@": "Imf_3_0",
|
||||
"@OPENEXR_LIB_VERSION@": "3.0.4",
|
||||
"@OPENEXR_NAMESPACE_CUSTOM@": "0",
|
||||
"@OPENEXR_PACKAGE_NAME@": "OpenEXR 3.0.4",
|
||||
"@OPENEXR_VERSION_EXTRA@": "",
|
||||
"@OPENEXR_VERSION_MAJOR@": "3",
|
||||
"@OPENEXR_VERSION_MINOR@": "0",
|
||||
"@OPENEXR_VERSION_PATCH@": "4",
|
||||
"@OPENEXR_VERSION@": "3.0.4",
|
||||
}
|
||||
YES_DEFINES = [
|
||||
"OPENEXR_ENABLE_API_VISIBILITY", "OPENEXR_IMF_HAVE_COMPLETE_IOMANIP",
|
||||
"OPENEXR_HAVE_LARGE_STACK",
|
||||
]
|
||||
NO_DEFINES = [
|
||||
"HAVE_UCONTEXT_H", "IEX_HAVE_CONTROL_REGISTER_SUPPORT",
|
||||
"IEX_HAVE_SIGCONTEXT_CONTROL_REGISTER_SUPPORT", "OPENEXR_IMF_HAVE_DARWIN",
|
||||
"OPENEXR_IMF_HAVE_GCC_INLINE_ASM_AVX", "OPENEXR_IMF_HAVE_LINUX_PROCFS",
|
||||
"OPENEXR_IMF_HAVE_SYSCONF_NPROCESSORS_ONLN",
|
||||
]
|
||||
ONE_DEFINES = [
|
||||
"ILMTHREAD_THREADING_ENABLED",
|
||||
]
|
||||
ZERO_DEFINES = [
|
||||
"ILMTHREAD_HAVE_POSIX_SEMAPHORES",
|
||||
]
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine " + key : "#define " + key for key in YES_DEFINES
|
||||
})
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine " + key : "// #define " + key for key in NO_DEFINES
|
||||
})
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine01 " + key : "#define " + key + " 1" for key in ONE_DEFINES
|
||||
})
|
||||
SUBSTITUTIONS.update({
|
||||
"#cmakedefine01 " + key : "#define " + key + " 0" for key in ZERO_DEFINES
|
||||
})
|
||||
[
|
||||
expand_template(
|
||||
name = "expand_" + item,
|
||||
template = "cmake/" + item + ".h.in",
|
||||
out = "src/lib/Iex/" + item + ".h",
|
||||
substitutions = SUBSTITUTIONS,
|
||||
) for item in ["IexConfig", "IexConfigInternal"]
|
||||
]
|
||||
[
|
||||
expand_template(
|
||||
name = "expand_" + item,
|
||||
template = "cmake/" + item + ".h.in",
|
||||
out = "src/lib/IlmThread/" + item + ".h",
|
||||
substitutions = SUBSTITUTIONS,
|
||||
) for item in ["IlmThreadConfig"]
|
||||
]
|
||||
[
|
||||
expand_template(
|
||||
name = "expand_" + item,
|
||||
template = "cmake/" + item + ".h.in",
|
||||
out = "src/lib/OpenEXR/" + item + ".h",
|
||||
substitutions = SUBSTITUTIONS,
|
||||
) for item in ["OpenEXRConfig", "OpenEXRConfigInternal"]
|
||||
]
|
||||
cc_library(
|
||||
name = "Iex",
|
||||
srcs = [
|
||||
"src/lib/Iex/IexBaseExc.cpp",
|
||||
"src/lib/Iex/IexMathFloatExc.cpp",
|
||||
"src/lib/Iex/IexMathFpu.cpp",
|
||||
"src/lib/Iex/IexThrowErrnoExc.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"src/lib/Iex/Iex.h",
|
||||
"src/lib/Iex/IexBaseExc.h",
|
||||
":src/lib/Iex/IexConfig.h",
|
||||
":src/lib/Iex/IexConfigInternal.h",
|
||||
"src/lib/Iex/IexErrnoExc.h",
|
||||
"src/lib/Iex/IexExport.h",
|
||||
"src/lib/Iex/IexForward.h",
|
||||
"src/lib/Iex/IexMacros.h",
|
||||
"src/lib/Iex/IexMathExc.h",
|
||||
"src/lib/Iex/IexMathFloatExc.h",
|
||||
"src/lib/Iex/IexMathFpu.h",
|
||||
"src/lib/Iex/IexMathIeeeExc.h",
|
||||
"src/lib/Iex/IexNamespace.h",
|
||||
"src/lib/Iex/IexThrowErrnoExc.h",
|
||||
":src/lib/OpenEXR/OpenEXRConfig.h",
|
||||
],
|
||||
includes = [
|
||||
"src/lib/Iex",
|
||||
"src/lib/OpenEXR",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "IlmThread",
|
||||
srcs = [
|
||||
"src/lib/IlmThread/IlmThread.cpp",
|
||||
"src/lib/IlmThread/IlmThreadPool.cpp",
|
||||
"src/lib/IlmThread/IlmThreadSemaphore.cpp",
|
||||
"src/lib/IlmThread/IlmThreadSemaphoreOSX.cpp",
|
||||
"src/lib/IlmThread/IlmThreadSemaphorePosix.cpp",
|
||||
"src/lib/IlmThread/IlmThreadSemaphorePosixCompat.cpp",
|
||||
"src/lib/IlmThread/IlmThreadSemaphoreWin32.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"src/lib/IlmThread/IlmThread.h",
|
||||
":src/lib/IlmThread/IlmThreadConfig.h",
|
||||
"src/lib/IlmThread/IlmThreadExport.h",
|
||||
"src/lib/IlmThread/IlmThreadForward.h",
|
||||
"src/lib/IlmThread/IlmThreadMutex.h",
|
||||
"src/lib/IlmThread/IlmThreadNamespace.h",
|
||||
"src/lib/IlmThread/IlmThreadPool.h",
|
||||
"src/lib/IlmThread/IlmThreadSemaphore.h",
|
||||
],
|
||||
includes = ["src/lib/IlmThread"],
|
||||
deps = [":Iex"],
|
||||
)
|
||||
cc_library(
|
||||
name = "OpenEXR",
|
||||
srcs = [
|
||||
"src/lib/OpenEXR/ImfAcesFile.cpp",
|
||||
"src/lib/OpenEXR/ImfAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfB44Compressor.cpp",
|
||||
"src/lib/OpenEXR/ImfBoxAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfCRgbaFile.cpp",
|
||||
"src/lib/OpenEXR/ImfChannelList.cpp",
|
||||
"src/lib/OpenEXR/ImfChannelListAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfChromaticities.cpp",
|
||||
"src/lib/OpenEXR/ImfChromaticitiesAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfCompositeDeepScanLine.cpp",
|
||||
"src/lib/OpenEXR/ImfCompressionAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfCompressor.cpp",
|
||||
"src/lib/OpenEXR/ImfConvert.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepCompositing.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepFrameBuffer.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepImageStateAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepScanLineInputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepScanLineInputPart.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepScanLineOutputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepScanLineOutputPart.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepTiledInputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepTiledInputPart.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepTiledOutputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfDeepTiledOutputPart.cpp",
|
||||
"src/lib/OpenEXR/ImfDoubleAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfDwaCompressor.cpp",
|
||||
"src/lib/OpenEXR/ImfEnvmap.cpp",
|
||||
"src/lib/OpenEXR/ImfEnvmapAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfFastHuf.cpp",
|
||||
"src/lib/OpenEXR/ImfFloatAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfFloatVectorAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfFrameBuffer.cpp",
|
||||
"src/lib/OpenEXR/ImfFramesPerSecond.cpp",
|
||||
"src/lib/OpenEXR/ImfGenericInputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfGenericOutputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfHeader.cpp",
|
||||
"src/lib/OpenEXR/ImfHuf.cpp",
|
||||
"src/lib/OpenEXR/ImfIDManifest.cpp",
|
||||
"src/lib/OpenEXR/ImfIDManifestAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfIO.cpp",
|
||||
"src/lib/OpenEXR/ImfInputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfInputPart.cpp",
|
||||
"src/lib/OpenEXR/ImfInputPartData.cpp",
|
||||
"src/lib/OpenEXR/ImfIntAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfKeyCode.cpp",
|
||||
"src/lib/OpenEXR/ImfKeyCodeAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfLineOrderAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfLut.cpp",
|
||||
"src/lib/OpenEXR/ImfMatrixAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfMisc.cpp",
|
||||
"src/lib/OpenEXR/ImfMultiPartInputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfMultiPartOutputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfMultiView.cpp",
|
||||
"src/lib/OpenEXR/ImfOpaqueAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfOutputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfOutputPart.cpp",
|
||||
"src/lib/OpenEXR/ImfOutputPartData.cpp",
|
||||
"src/lib/OpenEXR/ImfPartType.cpp",
|
||||
"src/lib/OpenEXR/ImfPizCompressor.cpp",
|
||||
"src/lib/OpenEXR/ImfPreviewImage.cpp",
|
||||
"src/lib/OpenEXR/ImfPreviewImageAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfPxr24Compressor.cpp",
|
||||
"src/lib/OpenEXR/ImfRational.cpp",
|
||||
"src/lib/OpenEXR/ImfRationalAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfRgbaFile.cpp",
|
||||
"src/lib/OpenEXR/ImfRgbaYca.cpp",
|
||||
"src/lib/OpenEXR/ImfRle.cpp",
|
||||
"src/lib/OpenEXR/ImfRleCompressor.cpp",
|
||||
"src/lib/OpenEXR/ImfScanLineInputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfStandardAttributes.cpp",
|
||||
"src/lib/OpenEXR/ImfStdIO.cpp",
|
||||
"src/lib/OpenEXR/ImfStringAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfStringVectorAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfSystemSpecific.cpp",
|
||||
"src/lib/OpenEXR/ImfTestFile.cpp",
|
||||
"src/lib/OpenEXR/ImfThreading.cpp",
|
||||
"src/lib/OpenEXR/ImfTileDescriptionAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfTileOffsets.cpp",
|
||||
"src/lib/OpenEXR/ImfTiledInputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfTiledInputPart.cpp",
|
||||
"src/lib/OpenEXR/ImfTiledMisc.cpp",
|
||||
"src/lib/OpenEXR/ImfTiledOutputFile.cpp",
|
||||
"src/lib/OpenEXR/ImfTiledOutputPart.cpp",
|
||||
"src/lib/OpenEXR/ImfTiledRgbaFile.cpp",
|
||||
"src/lib/OpenEXR/ImfTimeCode.cpp",
|
||||
"src/lib/OpenEXR/ImfTimeCodeAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfVecAttribute.cpp",
|
||||
"src/lib/OpenEXR/ImfVersion.cpp",
|
||||
"src/lib/OpenEXR/ImfWav.cpp",
|
||||
"src/lib/OpenEXR/ImfZip.cpp",
|
||||
"src/lib/OpenEXR/ImfZipCompressor.cpp",
|
||||
"src/lib/OpenEXR/b44ExpLogTable.h",
|
||||
"src/lib/OpenEXR/dwaLookups.h",
|
||||
],
|
||||
hdrs = [
|
||||
":src/lib/Iex/IexConfig.h",
|
||||
":src/lib/Iex/IexConfigInternal.h",
|
||||
":src/lib/IlmThread/IlmThreadConfig.h",
|
||||
"src/lib/OpenEXR/ImfAcesFile.h",
|
||||
"src/lib/OpenEXR/ImfArray.h",
|
||||
"src/lib/OpenEXR/ImfAttribute.h",
|
||||
"src/lib/OpenEXR/ImfAutoArray.h",
|
||||
"src/lib/OpenEXR/ImfB44Compressor.h",
|
||||
"src/lib/OpenEXR/ImfBoxAttribute.h",
|
||||
"src/lib/OpenEXR/ImfCRgbaFile.h",
|
||||
"src/lib/OpenEXR/ImfChannelList.h",
|
||||
"src/lib/OpenEXR/ImfChannelListAttribute.h",
|
||||
"src/lib/OpenEXR/ImfCheckedArithmetic.h",
|
||||
"src/lib/OpenEXR/ImfChromaticities.h",
|
||||
"src/lib/OpenEXR/ImfChromaticitiesAttribute.h",
|
||||
"src/lib/OpenEXR/ImfCompositeDeepScanLine.h",
|
||||
"src/lib/OpenEXR/ImfCompression.h",
|
||||
"src/lib/OpenEXR/ImfCompressionAttribute.h",
|
||||
"src/lib/OpenEXR/ImfCompressor.h",
|
||||
"src/lib/OpenEXR/ImfConvert.h",
|
||||
"src/lib/OpenEXR/ImfDeepCompositing.h",
|
||||
"src/lib/OpenEXR/ImfDeepFrameBuffer.h",
|
||||
"src/lib/OpenEXR/ImfDeepImageState.h",
|
||||
"src/lib/OpenEXR/ImfDeepImageStateAttribute.h",
|
||||
"src/lib/OpenEXR/ImfDeepScanLineInputFile.h",
|
||||
"src/lib/OpenEXR/ImfDeepScanLineInputPart.h",
|
||||
"src/lib/OpenEXR/ImfDeepScanLineOutputFile.h",
|
||||
"src/lib/OpenEXR/ImfDeepScanLineOutputPart.h",
|
||||
"src/lib/OpenEXR/ImfDeepTiledInputFile.h",
|
||||
"src/lib/OpenEXR/ImfDeepTiledInputPart.h",
|
||||
"src/lib/OpenEXR/ImfDeepTiledOutputFile.h",
|
||||
"src/lib/OpenEXR/ImfDeepTiledOutputPart.h",
|
||||
"src/lib/OpenEXR/ImfDoubleAttribute.h",
|
||||
"src/lib/OpenEXR/ImfDwaCompressor.h",
|
||||
"src/lib/OpenEXR/ImfDwaCompressorSimd.h",
|
||||
"src/lib/OpenEXR/ImfEnvmap.h",
|
||||
"src/lib/OpenEXR/ImfEnvmapAttribute.h",
|
||||
"src/lib/OpenEXR/ImfExport.h",
|
||||
"src/lib/OpenEXR/ImfFastHuf.h",
|
||||
"src/lib/OpenEXR/ImfFloatAttribute.h",
|
||||
"src/lib/OpenEXR/ImfFloatVectorAttribute.h",
|
||||
"src/lib/OpenEXR/ImfForward.h",
|
||||
"src/lib/OpenEXR/ImfFrameBuffer.h",
|
||||
"src/lib/OpenEXR/ImfFramesPerSecond.h",
|
||||
"src/lib/OpenEXR/ImfGenericInputFile.h",
|
||||
"src/lib/OpenEXR/ImfGenericOutputFile.h",
|
||||
"src/lib/OpenEXR/ImfHeader.h",
|
||||
"src/lib/OpenEXR/ImfHuf.h",
|
||||
"src/lib/OpenEXR/ImfIDManifest.h",
|
||||
"src/lib/OpenEXR/ImfIDManifestAttribute.h",
|
||||
"src/lib/OpenEXR/ImfIO.h",
|
||||
"src/lib/OpenEXR/ImfInputFile.h",
|
||||
"src/lib/OpenEXR/ImfInputPart.h",
|
||||
"src/lib/OpenEXR/ImfInputPartData.h",
|
||||
"src/lib/OpenEXR/ImfInputStreamMutex.h",
|
||||
"src/lib/OpenEXR/ImfInt64.h",
|
||||
"src/lib/OpenEXR/ImfIntAttribute.h",
|
||||
"src/lib/OpenEXR/ImfKeyCode.h",
|
||||
"src/lib/OpenEXR/ImfKeyCodeAttribute.h",
|
||||
"src/lib/OpenEXR/ImfLineOrder.h",
|
||||
"src/lib/OpenEXR/ImfLineOrderAttribute.h",
|
||||
"src/lib/OpenEXR/ImfLut.h",
|
||||
"src/lib/OpenEXR/ImfMatrixAttribute.h",
|
||||
"src/lib/OpenEXR/ImfMisc.h",
|
||||
"src/lib/OpenEXR/ImfMultiPartInputFile.h",
|
||||
"src/lib/OpenEXR/ImfMultiPartOutputFile.h",
|
||||
"src/lib/OpenEXR/ImfMultiView.h",
|
||||
"src/lib/OpenEXR/ImfName.h",
|
||||
"src/lib/OpenEXR/ImfNamespace.h",
|
||||
"src/lib/OpenEXR/ImfOpaqueAttribute.h",
|
||||
"src/lib/OpenEXR/ImfOptimizedPixelReading.h",
|
||||
"src/lib/OpenEXR/ImfOutputFile.h",
|
||||
"src/lib/OpenEXR/ImfOutputPart.h",
|
||||
"src/lib/OpenEXR/ImfOutputPartData.h",
|
||||
"src/lib/OpenEXR/ImfOutputStreamMutex.h",
|
||||
"src/lib/OpenEXR/ImfPartHelper.h",
|
||||
"src/lib/OpenEXR/ImfPartType.h",
|
||||
"src/lib/OpenEXR/ImfPixelType.h",
|
||||
"src/lib/OpenEXR/ImfPizCompressor.h",
|
||||
"src/lib/OpenEXR/ImfPreviewImage.h",
|
||||
"src/lib/OpenEXR/ImfPreviewImageAttribute.h",
|
||||
"src/lib/OpenEXR/ImfPxr24Compressor.h",
|
||||
"src/lib/OpenEXR/ImfRational.h",
|
||||
"src/lib/OpenEXR/ImfRationalAttribute.h",
|
||||
"src/lib/OpenEXR/ImfRgba.h",
|
||||
"src/lib/OpenEXR/ImfRgbaFile.h",
|
||||
"src/lib/OpenEXR/ImfRgbaYca.h",
|
||||
"src/lib/OpenEXR/ImfRle.h",
|
||||
"src/lib/OpenEXR/ImfRleCompressor.h",
|
||||
"src/lib/OpenEXR/ImfScanLineInputFile.h",
|
||||
"src/lib/OpenEXR/ImfSimd.h",
|
||||
"src/lib/OpenEXR/ImfStandardAttributes.h",
|
||||
"src/lib/OpenEXR/ImfStdIO.h",
|
||||
"src/lib/OpenEXR/ImfStringAttribute.h",
|
||||
"src/lib/OpenEXR/ImfStringVectorAttribute.h",
|
||||
"src/lib/OpenEXR/ImfSystemSpecific.h",
|
||||
"src/lib/OpenEXR/ImfTestFile.h",
|
||||
"src/lib/OpenEXR/ImfThreading.h",
|
||||
"src/lib/OpenEXR/ImfTileDescription.h",
|
||||
"src/lib/OpenEXR/ImfTileDescriptionAttribute.h",
|
||||
"src/lib/OpenEXR/ImfTileOffsets.h",
|
||||
"src/lib/OpenEXR/ImfTiledInputFile.h",
|
||||
"src/lib/OpenEXR/ImfTiledInputPart.h",
|
||||
"src/lib/OpenEXR/ImfTiledMisc.h",
|
||||
"src/lib/OpenEXR/ImfTiledOutputFile.h",
|
||||
"src/lib/OpenEXR/ImfTiledOutputPart.h",
|
||||
"src/lib/OpenEXR/ImfTiledRgbaFile.h",
|
||||
"src/lib/OpenEXR/ImfTimeCode.h",
|
||||
"src/lib/OpenEXR/ImfTimeCodeAttribute.h",
|
||||
"src/lib/OpenEXR/ImfVecAttribute.h",
|
||||
"src/lib/OpenEXR/ImfVersion.h",
|
||||
"src/lib/OpenEXR/ImfWav.h",
|
||||
"src/lib/OpenEXR/ImfXdr.h",
|
||||
"src/lib/OpenEXR/ImfZip.h",
|
||||
"src/lib/OpenEXR/ImfZipCompressor.h",
|
||||
":src/lib/OpenEXR/OpenEXRConfig.h",
|
||||
":src/lib/OpenEXR/OpenEXRConfigInternal.h",
|
||||
],
|
||||
includes = ["src/lib/OpenEXR"],
|
||||
deps = [
|
||||
":IlmThread",
|
||||
"@imath//:Imath",
|
||||
"@zlib//:zlib",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
remote = "https://github.com/AcademySoftwareFoundation/openexr",
|
||||
tag = "v3.1.5",
|
||||
)
|
317
third-party/libjxl/libjxl/bash_test.sh
vendored
Executable file
317
third-party/libjxl/libjxl/bash_test.sh
vendored
Executable file
@ -0,0 +1,317 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# Tests implemented in bash. These typically will run checks about the source
|
||||
# code rather than the compiled one.
|
||||
|
||||
MYDIR=$(dirname $(realpath "$0"))
|
||||
|
||||
set -u
|
||||
|
||||
test_includes() {
|
||||
local ret=0
|
||||
local f
|
||||
for f in $(git ls-files | grep -E '(\.cc|\.cpp|\.h)$'); do
|
||||
if [ ! -e "$f" ]; then
|
||||
continue
|
||||
fi
|
||||
# Check that the full paths to the public headers are not used, since users
|
||||
# of the library will include the library as: #include "jxl/foobar.h".
|
||||
if grep -i -H -n -E '#include\s*[<"]lib/include/jxl' "$f" >&2; then
|
||||
echo "Don't add \"include/\" to the include path of public headers." >&2
|
||||
ret=1
|
||||
fi
|
||||
|
||||
if [[ "${f#third_party/}" == "$f" ]]; then
|
||||
# $f is not in third_party/
|
||||
|
||||
# Check that local files don't use the full path to third_party/
|
||||
# directory since the installed versions will not have that path.
|
||||
# Add an exception for third_party/dirent.h.
|
||||
if grep -v -F 'third_party/dirent.h' "$f" | \
|
||||
grep -i -H -n -E '#include\s*[<"]third_party/' >&2 &&
|
||||
[[ $ret -eq 0 ]]; then
|
||||
cat >&2 <<EOF
|
||||
$f: Don't add third_party/ to the include path of third_party projects. This \
|
||||
makes it harder to use installed system libraries instead of the third_party/ \
|
||||
ones.
|
||||
EOF
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
|
||||
done
|
||||
return ${ret}
|
||||
}
|
||||
|
||||
test_include_collision() {
|
||||
local ret=0
|
||||
local f
|
||||
for f in $(git ls-files | grep -E '^lib/include/'); do
|
||||
if [ ! -e "$f" ]; then
|
||||
continue
|
||||
fi
|
||||
local base=${f#lib/include/}
|
||||
if [[ -e "lib/${base}" ]]; then
|
||||
echo "$f: Name collision, both $f and lib/${base} exist." >&2
|
||||
ret=1
|
||||
fi
|
||||
done
|
||||
return ${ret}
|
||||
}
|
||||
|
||||
test_copyright() {
|
||||
local ret=0
|
||||
local f
|
||||
for f in $(
|
||||
git ls-files | grep -E \
|
||||
'(Dockerfile.*|\.c|\.cc|\.cpp|\.gni|\.h|\.java|\.sh|\.m|\.py|\.ui|\.yml)$'); do
|
||||
if [ ! -e "$f" ]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "${f#third_party/}" == "$f" ]]; then
|
||||
# $f is not in third_party/
|
||||
if ! head -n 10 "$f" |
|
||||
grep -F 'Copyright (c) the JPEG XL Project Authors.' >/dev/null ; then
|
||||
echo "$f: Missing Copyright blob near the top of the file." >&2
|
||||
ret=1
|
||||
fi
|
||||
if ! head -n 10 "$f" |
|
||||
grep -F 'Use of this source code is governed by a BSD-style' \
|
||||
>/dev/null ; then
|
||||
echo "$f: Missing License blob near the top of the file." >&2
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return ${ret}
|
||||
}
|
||||
|
||||
# Check that we don't use "%zu" or "%zd" in format string for size_t.
|
||||
test_printf_size_t() {
|
||||
local ret=0
|
||||
if grep -n -E '%[0-9]*z[udx]' \
|
||||
$(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$'); then
|
||||
echo "Don't use '%zu' or '%zd' in a format string, instead use " \
|
||||
"'%\" PRIuS \"' or '%\" PRIdS \"'." >&2
|
||||
ret=1
|
||||
fi
|
||||
|
||||
if grep -n -E 'gtest\.h' \
|
||||
$(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /testing.h); then
|
||||
echo "Don't include gtest directly, instead include 'testing.h'. " >&2
|
||||
ret=1
|
||||
fi
|
||||
|
||||
if grep -n -E 'gmock\.h' \
|
||||
$(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /testing.h); then
|
||||
echo "Don't include gmock directly, instead include 'testing.h'. " >&2
|
||||
ret=1
|
||||
fi
|
||||
|
||||
local f
|
||||
for f in $(git ls-files | grep -E "\.cc$" | xargs grep 'PRI[udx]S' |
|
||||
cut -f 1 -d : | uniq); do
|
||||
if [ ! -e "$f" ]; then
|
||||
continue
|
||||
fi
|
||||
if ! grep -F printf_macros.h "$f" >/dev/null; then
|
||||
echo "$f: Add lib/jxl/base/printf_macros.h for PRI.S, or use other " \
|
||||
"types for code outside lib/jxl library." >&2
|
||||
ret=1
|
||||
fi
|
||||
done
|
||||
|
||||
for f in $(git ls-files | grep -E "\.h$" | grep -v -E '(printf_macros\.h|testing\.h)' |
|
||||
xargs grep -n 'PRI[udx]S'); do
|
||||
# Having PRIuS / PRIdS in a header file means that printf_macros.h may
|
||||
# be included before a system header, in particular before gtest headers.
|
||||
# those may re-define PRIuS unconditionally causing a compile error.
|
||||
echo "$f: Don't use PRI.S in header files. Sorry."
|
||||
ret=1
|
||||
done
|
||||
|
||||
return ${ret}
|
||||
}
|
||||
|
||||
# Check that "dec_" code doesn't depend on "enc_" headers.
|
||||
test_dec_enc_deps() {
|
||||
local ret=0
|
||||
local f
|
||||
for f in $(git ls-files | grep -E '/dec_'); do
|
||||
if [ ! -e "$f" ]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "${f#third_party/}" == "$f" ]]; then
|
||||
# $f is not in third_party/
|
||||
if grep -n -H -E "#include.*/enc_" "$f" >&2; then
|
||||
echo "$f: Don't include \"enc_*\" files from \"dec_*\" files." >&2
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return ${ret}
|
||||
}
|
||||
|
||||
# Check for git merge conflict markers.
|
||||
test_merge_conflict() {
|
||||
local ret=0
|
||||
TEXT_FILES='(\.cc|\.cpp|\.h|\.sh|\.m|\.py|\.md|\.txt|\.cmake)$'
|
||||
for f in $(git ls-files | grep -E "${TEXT_FILES}"); do
|
||||
if [ ! -e "$f" ]; then
|
||||
continue
|
||||
fi
|
||||
if grep -E '^<<<<<<< ' "$f"; then
|
||||
echo "$f: Found git merge conflict marker. Please resolve." >&2
|
||||
ret=1
|
||||
fi
|
||||
done
|
||||
return ${ret}
|
||||
}
|
||||
|
||||
# Check that the library and the package have the same version. This prevents
|
||||
# accidentally having them out of sync.
|
||||
get_version() {
|
||||
local varname=$1
|
||||
local line=$(grep -F "set(${varname} " lib/CMakeLists.txt | head -n 1)
|
||||
[[ -n "${line}" ]]
|
||||
line="${line#set(${varname} }"
|
||||
line="${line%)}"
|
||||
echo "${line}"
|
||||
}
|
||||
|
||||
test_version() {
|
||||
local major=$(get_version JPEGXL_MAJOR_VERSION)
|
||||
local minor=$(get_version JPEGXL_MINOR_VERSION)
|
||||
local patch=$(get_version JPEGXL_PATCH_VERSION)
|
||||
# Check that the version is not empty
|
||||
if [[ -z "${major}${minor}${patch}" ]]; then
|
||||
echo "Couldn't parse version from CMakeLists.txt" >&2
|
||||
return 1
|
||||
fi
|
||||
local pkg_version=$(head -n 1 debian/changelog)
|
||||
# Get only the part between the first "jpeg-xl (" and the following ")".
|
||||
pkg_version="${pkg_version#jpeg-xl (}"
|
||||
pkg_version="${pkg_version%%)*}"
|
||||
if [[ -z "${pkg_version}" ]]; then
|
||||
echo "Couldn't parse version from debian package" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local lib_version="${major}.${minor}.${patch}"
|
||||
lib_version="${lib_version%.0}"
|
||||
if [[ "${pkg_version}" != "${lib_version}"* ]]; then
|
||||
echo "Debian package version (${pkg_version}) doesn't match library" \
|
||||
"version (${lib_version})." >&2
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check that the SHA versions in deps.sh matches the git submodules.
|
||||
test_deps_version() {
|
||||
while IFS= read -r line; do
|
||||
if [[ "${line:0:10}" != "[submodule" ]]; then
|
||||
continue
|
||||
fi
|
||||
line="${line#[submodule \"}"
|
||||
line="${line%\"]}"
|
||||
local varname=$(tr '[:lower:]' '[:upper:]' <<< "${line}")
|
||||
varname="${varname/\//_}"
|
||||
if ! grep -F "${varname}=" deps.sh >/dev/null; then
|
||||
# Ignoring submodule not in deps.sh
|
||||
continue
|
||||
fi
|
||||
local deps_sha=$(grep -F "${varname}=" deps.sh | cut -f 2 -d '"')
|
||||
[[ -n "${deps_sha}" ]]
|
||||
local git_sha=$(git ls-tree -r HEAD "${line}" | cut -f 1 | cut -f 3 -d ' ')
|
||||
if [[ "${deps_sha}" != "${git_sha}" ]]; then
|
||||
cat >&2 <<EOF
|
||||
deps.sh: SHA for project ${line} is at ${deps_sha} but the git submodule is at
|
||||
${git_sha}. Please update deps.sh
|
||||
|
||||
If you did not intend to change the submodule's SHA value, it is possible that
|
||||
you accidentally included this change in your commit after a rebase or checkout
|
||||
without running "git submodule --init". To revert the submodule change run from
|
||||
the top checkout directory:
|
||||
|
||||
git -C ${line} checkout ${deps_sha}
|
||||
git commit --amend ${line}
|
||||
|
||||
EOF
|
||||
return 1
|
||||
fi
|
||||
done < .gitmodules
|
||||
}
|
||||
|
||||
# Make sure that all the Fields objects are fuzzed directly.
|
||||
test_fuzz_fields() {
|
||||
local ret=0
|
||||
# List all the classes of the form "ClassName : public Fields".
|
||||
# This doesn't catch class names that are too long to fit.
|
||||
local field_classes=$( git ls-files |
|
||||
grep -E '\.(cc|h)' | grep -v 'test\.cc$' |
|
||||
xargs grep -h -o -E '\b[^ ]+ : public Fields' | cut -f 1 -d ' ')
|
||||
local classname
|
||||
for classname in ${field_classes}; do
|
||||
if [ ! -e "$classname" ]; then
|
||||
continue
|
||||
fi
|
||||
if ! grep -E "\\b${classname}\\b" tools/fields_fuzzer.cc >/dev/null; then
|
||||
cat >&2 <<EOF
|
||||
tools/fields_fuzzer.cc: Class ${classname} not found in the fields_fuzzer.
|
||||
EOF
|
||||
ret=1
|
||||
fi
|
||||
done
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Test that we don't use %n in C++ code to avoid using it in printf and scanf.
|
||||
# This test is not very precise but in cases where "module n" is needed we would
|
||||
# normally have "% n" instead of "%n". Using %n is not allowed in Android 10+.
|
||||
test_percent_n() {
|
||||
local ret=0
|
||||
local f
|
||||
for f in $(git ls-files | grep -E '(\.cc|\.cpp|\.h)$'); do
|
||||
if [ ! -e "$f" ]; then
|
||||
continue
|
||||
fi
|
||||
if grep -i -H -n -E '%h*n' "$f" >&2; then
|
||||
echo "Don't use \"%n\"." >&2
|
||||
ret=1
|
||||
fi
|
||||
done
|
||||
return ${ret}
|
||||
}
|
||||
|
||||
main() {
|
||||
local ret=0
|
||||
cd "${MYDIR}"
|
||||
|
||||
if ! git rev-parse >/dev/null 2>/dev/null; then
|
||||
echo "Not a git checkout, skipping bash_test"
|
||||
return 0
|
||||
fi
|
||||
|
||||
IFS=$'\n'
|
||||
for f in $(declare -F); do
|
||||
local test_name=$(echo "$f" | cut -f 3 -d ' ')
|
||||
# Runs all the local bash functions that start with "test_".
|
||||
if [[ "${test_name}" == test_* ]]; then
|
||||
echo "Test ${test_name}: Start"
|
||||
if ${test_name}; then
|
||||
echo "Test ${test_name}: PASS"
|
||||
else
|
||||
echo "Test ${test_name}: FAIL"
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return ${ret}
|
||||
}
|
||||
|
||||
main "$@"
|
1552
third-party/libjxl/libjxl/ci.sh
vendored
Executable file
1552
third-party/libjxl/libjxl/ci.sh
vendored
Executable file
File diff suppressed because it is too large
Load Diff
53
third-party/libjxl/libjxl/cmake/FindAtomics.cmake
vendored
Normal file
53
third-party/libjxl/libjxl/cmake/FindAtomics.cmake
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
# Original issue:
|
||||
# * https://gitlab.kitware.com/cmake/cmake/-/issues/23021#note_1098733
|
||||
#
|
||||
# For reference:
|
||||
# * https://gcc.gnu.org/wiki/Atomic/GCCMM
|
||||
#
|
||||
# riscv64 specific:
|
||||
# * https://lists.debian.org/debian-riscv/2022/01/msg00009.html
|
||||
#
|
||||
# ATOMICS_FOUND - system has c++ atomics
|
||||
# ATOMICS_LIBRARIES - libraries needed to use c++ atomics
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
# RISC-V only has 32-bit and 64-bit atomic instructions. GCC is supposed
|
||||
# to convert smaller atomics to those larger ones via masking and
|
||||
# shifting like LLVM, but it’s a known bug that it does not. This means
|
||||
# anything that wants to use atomics on 1-byte or 2-byte types needs
|
||||
# -latomic, but not 4-byte or 8-byte (though it does no harm).
|
||||
set(atomic_code
|
||||
"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
std::atomic<uint8_t> n8 (0); // riscv64
|
||||
std::atomic<uint64_t> n64 (0); // armel, mipsel, powerpc
|
||||
int main() {
|
||||
++n8;
|
||||
++n64;
|
||||
return 0;
|
||||
}")
|
||||
|
||||
check_cxx_source_compiles("${atomic_code}" ATOMICS_LOCK_FREE_INSTRUCTIONS)
|
||||
|
||||
if(ATOMICS_LOCK_FREE_INSTRUCTIONS)
|
||||
set(ATOMICS_FOUND TRUE)
|
||||
set(ATOMICS_LIBRARIES)
|
||||
else()
|
||||
set(CMAKE_REQUIRED_LIBRARIES "-latomic")
|
||||
check_cxx_source_compiles("${atomic_code}" ATOMICS_IN_LIBRARY)
|
||||
set(CMAKE_REQUIRED_LIBRARIES)
|
||||
if(ATOMICS_IN_LIBRARY)
|
||||
set(ATOMICS_LIBRARY atomic)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Atomics DEFAULT_MSG ATOMICS_LIBRARY)
|
||||
set(ATOMICS_LIBRARIES ${ATOMICS_LIBRARY})
|
||||
unset(ATOMICS_LIBRARY)
|
||||
else()
|
||||
if(Atomics_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Neither lock free instructions nor -latomic found.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
unset(atomic_code)
|
75
third-party/libjxl/libjxl/cmake/FindBrotli.cmake
vendored
Normal file
75
third-party/libjxl/libjxl/cmake/FindBrotli.cmake
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
set(brlibs brotlicommon brotlienc brotlidec)
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PkgConfig_FOUND)
|
||||
foreach(brlib IN ITEMS ${brlibs})
|
||||
string(TOUPPER "${brlib}" BRPREFIX)
|
||||
pkg_check_modules("PC_${BRPREFIX}" lib${brlib})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
find_path(BROTLI_INCLUDE_DIR
|
||||
NAMES brotli/decode.h
|
||||
HINTS ${PC_BROTLICOMMON_INCLUDEDIR} ${PC_BROTLICOMMON_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
foreach(brlib IN ITEMS ${brlibs})
|
||||
string(TOUPPER "${brlib}" BRPREFIX)
|
||||
find_library(${BRPREFIX}_LIBRARY
|
||||
NAMES ${${BRPREFIX}_NAMES} ${brlib}
|
||||
HINTS ${PC_${BRPREFIX}_LIBDIR} ${PC_${BRPREFIX}_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if (${BRPREFIX}_LIBRARY AND NOT TARGET ${brlib})
|
||||
if(CMAKE_VERSION VERSION_LESS "3.13.5")
|
||||
add_library(${brlib} INTERFACE IMPORTED GLOBAL)
|
||||
set_property(TARGET ${brlib} PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${BROTLI_INCLUDE_DIR})
|
||||
target_link_libraries(${brlib} INTERFACE ${${BRPREFIX}_LIBRARY})
|
||||
set_property(TARGET ${brlib} PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_${BRPREFIX}_CFLAGS_OTHER})
|
||||
else()
|
||||
add_library(${brlib} INTERFACE IMPORTED GLOBAL)
|
||||
target_include_directories(${brlib}
|
||||
INTERFACE ${BROTLI_INCLUDE_DIR})
|
||||
target_link_libraries(${brlib}
|
||||
INTERFACE ${${BRPREFIX}_LIBRARY})
|
||||
target_link_options(${brlib}
|
||||
INTERFACE ${PC_${BRPREFIX}_LDFLAGS_OTHER})
|
||||
target_compile_options(${brlib}
|
||||
INTERFACE ${PC_${BRPREFIX}_CFLAGS_OTHER})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if (BROTLICOMMON_FOUND AND BROTLIENC_FOUND AND BROTLIDEC_FOUND)
|
||||
set(Brotli_FOUND ON)
|
||||
else ()
|
||||
set(Brotli_FOUND OFF)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Brotli
|
||||
FOUND_VAR Brotli_FOUND
|
||||
REQUIRED_VARS
|
||||
BROTLI_INCLUDE_DIR
|
||||
BROTLICOMMON_LIBRARY
|
||||
BROTLIENC_LIBRARY
|
||||
BROTLIDEC_LIBRARY
|
||||
VERSION_VAR Brotli_VERSION
|
||||
)
|
||||
|
||||
mark_as_advanced(
|
||||
BROTLI_INCLUDE_DIR
|
||||
BROTLICOMMON_LIBRARY
|
||||
BROTLIENC_LIBRARY
|
||||
BROTLIDEC_LIBRARY
|
||||
)
|
||||
|
||||
if (Brotli_FOUND)
|
||||
set(Brotli_LIBRARIES ${BROTLICOMMON_LIBRARY} ${BROTLIENC_LIBRARY} ${BROTLIDEC_LIBRARY})
|
||||
set(Brotli_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR})
|
||||
endif()
|
66
third-party/libjxl/libjxl/cmake/FindHWY.cmake
vendored
Normal file
66
third-party/libjxl/libjxl/cmake/FindHWY.cmake
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PkgConfig_FOUND)
|
||||
pkg_check_modules(PC_HWY QUIET libhwy)
|
||||
set(HWY_VERSION ${PC_HWY_VERSION})
|
||||
endif ()
|
||||
|
||||
find_path(HWY_INCLUDE_DIR
|
||||
NAMES hwy/highway.h
|
||||
HINTS ${PC_HWY_INCLUDEDIR} ${PC_HWY_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
find_library(HWY_LIBRARY
|
||||
NAMES ${HWY_NAMES} hwy
|
||||
HINTS ${PC_HWY_LIBDIR} ${PC_HWY_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if (HWY_INCLUDE_DIR AND NOT HWY_VERSION)
|
||||
if (EXISTS "${HWY_INCLUDE_DIR}/hwy/highway.h")
|
||||
file(READ "${HWY_INCLUDE_DIR}/hwy/highway.h" HWY_VERSION_CONTENT)
|
||||
|
||||
string(REGEX MATCH "#define HWY_MAJOR +([0-9]+)" _dummy "${HWY_VERSION_CONTENT}")
|
||||
set(HWY_VERSION_MAJOR "${CMAKE_MATCH_1}")
|
||||
|
||||
string(REGEX MATCH "#define +HWY_MINOR +([0-9]+)" _dummy "${HWY_VERSION_CONTENT}")
|
||||
set(HWY_VERSION_MINOR "${CMAKE_MATCH_1}")
|
||||
|
||||
string(REGEX MATCH "#define +HWY_PATCH +([0-9]+)" _dummy "${HWY_VERSION_CONTENT}")
|
||||
set(HWY_VERSION_PATCH "${CMAKE_MATCH_1}")
|
||||
|
||||
set(HWY_VERSION "${HWY_VERSION_MAJOR}.${HWY_VERSION_MINOR}.${HWY_VERSION_PATCH}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(HWY
|
||||
FOUND_VAR HWY_FOUND
|
||||
REQUIRED_VARS HWY_LIBRARY HWY_INCLUDE_DIR
|
||||
VERSION_VAR HWY_VERSION
|
||||
)
|
||||
|
||||
if (HWY_LIBRARY AND NOT TARGET hwy)
|
||||
add_library(hwy INTERFACE IMPORTED GLOBAL)
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.13.5")
|
||||
set_property(TARGET hwy PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${HWY_INCLUDE_DIR})
|
||||
target_link_libraries(hwy INTERFACE ${HWY_LIBRARY})
|
||||
set_property(TARGET hwy PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_HWY_CFLAGS_OTHER})
|
||||
else()
|
||||
target_include_directories(hwy INTERFACE ${HWY_INCLUDE_DIR})
|
||||
target_link_libraries(hwy INTERFACE ${HWY_LIBRARY})
|
||||
target_link_options(hwy INTERFACE ${PC_HWY_LDFLAGS_OTHER})
|
||||
target_compile_options(hwy INTERFACE ${PC_HWY_CFLAGS_OTHER})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(HWY_INCLUDE_DIR HWY_LIBRARY)
|
||||
|
||||
if (HWY_FOUND)
|
||||
set(HWY_LIBRARIES ${HWY_LIBRARY})
|
||||
set(HWY_INCLUDE_DIRS ${HWY_INCLUDE_DIR})
|
||||
endif ()
|
59
third-party/libjxl/libjxl/cmake/FindLCMS2.cmake
vendored
Normal file
59
third-party/libjxl/libjxl/cmake/FindLCMS2.cmake
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PkgConfig_FOUND)
|
||||
pkg_check_modules(PC_LCMS2 QUIET libLCMS2)
|
||||
set(LCMS2_VERSION ${PC_LCMS2_VERSION})
|
||||
endif ()
|
||||
|
||||
find_path(LCMS2_INCLUDE_DIR
|
||||
NAMES lcms2.h
|
||||
HINTS ${PC_LCMS2_INCLUDEDIR} ${PC_LCMS2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
find_library(LCMS2_LIBRARY
|
||||
NAMES ${LCMS2_NAMES} lcms2 liblcms2 lcms-2 liblcms-2
|
||||
HINTS ${PC_LCMS2_LIBDIR} ${PC_LCMS2_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
if (LCMS2_INCLUDE_DIR AND NOT LCMS_VERSION)
|
||||
file(READ ${LCMS2_INCLUDE_DIR}/lcms2.h LCMS2_VERSION_CONTENT)
|
||||
string(REGEX MATCH "#define[ \t]+LCMS_VERSION[ \t]+([0-9]+)[ \t]*\n" LCMS2_VERSION_MATCH ${LCMS2_VERSION_CONTENT})
|
||||
if (LCMS2_VERSION_MATCH)
|
||||
string(SUBSTRING ${CMAKE_MATCH_1} 0 1 LCMS2_VERSION_MAJOR)
|
||||
string(SUBSTRING ${CMAKE_MATCH_1} 1 2 LCMS2_VERSION_MINOR)
|
||||
set(LCMS2_VERSION "${LCMS2_VERSION_MAJOR}.${LCMS2_VERSION_MINOR}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LCMS2
|
||||
FOUND_VAR LCMS2_FOUND
|
||||
REQUIRED_VARS LCMS2_LIBRARY LCMS2_INCLUDE_DIR
|
||||
VERSION_VAR LCMS2_VERSION
|
||||
)
|
||||
|
||||
if (LCMS2_LIBRARY AND NOT TARGET lcms2)
|
||||
add_library(lcms2 INTERFACE IMPORTED GLOBAL)
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS "3.13.5")
|
||||
set_property(TARGET lcms2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LCMS2_INCLUDE_DIR})
|
||||
target_link_libraries(lcms2 INTERFACE ${LCMS2_LIBRARY})
|
||||
set_property(TARGET lcms2 PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_LCMS2_CFLAGS_OTHER})
|
||||
else()
|
||||
target_include_directories(lcms2 INTERFACE ${LCMS2_INCLUDE_DIR})
|
||||
target_link_libraries(lcms2 INTERFACE ${LCMS2_LIBRARY})
|
||||
target_link_options(lcms2 INTERFACE ${PC_LCMS2_LDFLAGS_OTHER})
|
||||
target_compile_options(lcms2 INTERFACE ${PC_LCMS2_CFLAGS_OTHER})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(LCMS2_INCLUDE_DIR LCMS2_LIBRARY)
|
||||
|
||||
if (LCMS2_FOUND)
|
||||
set(LCMS2_LIBRARIES ${LCMS2_LIBRARY})
|
||||
set(LCMS2_INCLUDE_DIRS ${LCMS2_INCLUDE_DIR})
|
||||
endif ()
|
95
third-party/libjxl/libjxl/debian/changelog
vendored
Normal file
95
third-party/libjxl/libjxl/debian/changelog
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
jpeg-xl (0.9.0) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.9.0.
|
||||
|
||||
-- JPEG XL Maintainers <jpegxl@google.com> Wed, 11 Jan 2023 16:12:35 +0000
|
||||
|
||||
jpeg-xl (0.8) unstable; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.8.
|
||||
|
||||
-- JPEG XL Maintainers <jpegxl@google.com> Wed, 11 Jan 2023 16:12:34 +0000
|
||||
|
||||
jpeg-xl (0.7) unstable; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.7.
|
||||
|
||||
-- JPEG XL Maintainers <jpegxl@google.com> Mon, 08 Aug 2022 14:43:58 +0000
|
||||
|
||||
jpeg-xl (0.6) unstable; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.6.
|
||||
|
||||
-- JPEG XL Maintainers <jpegxl@google.com> Fri, 10 Sep 2021 16:08:17 +0200
|
||||
|
||||
jpeg-xl (0.5.0) unstable; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.5.0.
|
||||
|
||||
-- JPEG XL Maintainers <jpegxl@google.com> Thu, 12 Aug 2021 23:49:40 +0200
|
||||
|
||||
jpeg-xl (0.3.7) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.3.7.
|
||||
|
||||
-- Sami Boukortt <sboukortt@google.com> Mon, 29 Mar 2021 12:14:20 +0200
|
||||
|
||||
jpeg-xl (0.3.6) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.3.6.
|
||||
|
||||
-- Sami Boukortt <sboukortt@google.com> Thu, 25 Mar 2021 17:40:58 +0100
|
||||
|
||||
jpeg-xl (0.3.5) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.3.5.
|
||||
|
||||
-- Sami Boukortt <sboukortt@google.com> Tue, 23 Mar 2021 15:20:44 +0100
|
||||
|
||||
jpeg-xl (0.3.4) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.3.4.
|
||||
|
||||
-- Sami Boukortt <sboukortt@google.com> Tue, 16 Mar 2021 12:13:59 +0100
|
||||
|
||||
jpeg-xl (0.3.3) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.3.3.
|
||||
|
||||
-- Sami Boukortt <sboukortt@google.com> Fri, 5 Mar 2021 19:15:26 +0100
|
||||
|
||||
jpeg-xl (0.3.2) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.3.2.
|
||||
|
||||
-- Alex Deymo <deymo@google.com> Fri, 12 Feb 2021 21:00:12 +0100
|
||||
|
||||
jpeg-xl (0.3.1) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.3.1.
|
||||
|
||||
-- Alex Deymo <deymo@google.com> Tue, 09 Feb 2021 09:48:43 +0100
|
||||
|
||||
jpeg-xl (0.3) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.3.
|
||||
|
||||
-- Alex Deymo <deymo@google.com> Wed, 27 Jan 2021 22:36:32 +0100
|
||||
|
||||
jpeg-xl (0.2) UNRELEASED; urgency=medium
|
||||
|
||||
* Bump JPEG XL version to 0.2.
|
||||
|
||||
-- Alex Deymo <deymo@google.com> Wed, 23 Nov 2020 20:42:10 +0100
|
||||
|
||||
jpeg-xl (0.1) UNRELEASED; urgency=medium
|
||||
|
||||
* JPEG XL format release candidate.
|
||||
|
||||
-- Alex Deymo <deymo@google.com> Fri, 13 Nov 2020 17:42:24 +0100
|
||||
|
||||
jpeg-xl (0.0.2-1) UNRELEASED; urgency=medium
|
||||
|
||||
* Initial debian package.
|
||||
|
||||
-- Alex Deymo <deymo@google.com> Tue, 27 Oct 2020 15:27:59 +0100
|
1
third-party/libjxl/libjxl/debian/compat
vendored
Normal file
1
third-party/libjxl/libjxl/debian/compat
vendored
Normal file
@ -0,0 +1 @@
|
||||
10
|
88
third-party/libjxl/libjxl/debian/control
vendored
Normal file
88
third-party/libjxl/libjxl/debian/control
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
Source: jpeg-xl
|
||||
Maintainer: JPEG XL Maintainers <jpegxl@google.com>
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Standards-Version: 3.9.8
|
||||
Build-Depends:
|
||||
asciidoc,
|
||||
cmake,
|
||||
debhelper (>= 9),
|
||||
libbrotli-dev,
|
||||
libgdk-pixbuf-2.0-dev | libgdk-pixbuf2.0-dev,
|
||||
libgif-dev,
|
||||
libgimp2.0-dev,
|
||||
libgmock-dev,
|
||||
libgoogle-perftools-dev,
|
||||
libgtest-dev,
|
||||
libhwy-dev (>= 1.0.0),
|
||||
libjpeg-dev,
|
||||
libopenexr-dev,
|
||||
libpng-dev,
|
||||
libwebp-dev,
|
||||
pkg-config,
|
||||
xdg-utils,
|
||||
xmlto,
|
||||
Homepage: https://github.com/libjxl/libjxl
|
||||
Rules-Requires-Root: no
|
||||
|
||||
Package: jxl
|
||||
Architecture: any
|
||||
Section: utils
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}
|
||||
Description: JPEG XL Image Coding System - "JXL" (command line utility)
|
||||
The JPEG XL Image Coding System (ISO/IEC 18181) is a lossy and
|
||||
lossless image compression format. It has a rich feature set and is
|
||||
particularly optimized for responsive web environments, so that
|
||||
content renders well on a wide range of devices. Moreover, it includes
|
||||
several features that help transition from the legacy JPEG format.
|
||||
.
|
||||
This package installs the command line utilities.
|
||||
|
||||
Package: libjxl-dev
|
||||
Architecture: any
|
||||
Section: libdevel
|
||||
Depends: libjxl (= ${binary:Version}), ${misc:Depends}
|
||||
libhwy-dev,
|
||||
Description: JPEG XL Image Coding System - "JXL" (development files)
|
||||
The JPEG XL Image Coding System (ISO/IEC 18181) is a lossy and
|
||||
lossless image compression format. It has a rich feature set and is
|
||||
particularly optimized for responsive web environments, so that
|
||||
content renders well on a wide range of devices. Moreover, it includes
|
||||
several features that help transition from the legacy JPEG format.
|
||||
.
|
||||
This package installs development files.
|
||||
|
||||
Package: libjxl
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libs
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Description: JPEG XL Image Coding System - "JXL" (shared libraries)
|
||||
The JPEG XL Image Coding System (ISO/IEC 18181) is a lossy and
|
||||
lossless image compression format. It has a rich feature set and is
|
||||
particularly optimized for responsive web environments, so that
|
||||
content renders well on a wide range of devices. Moreover, it includes
|
||||
several features that help transition from the legacy JPEG format.
|
||||
.
|
||||
This package installs shared libraries.
|
||||
|
||||
Package: libjxl-gdk-pixbuf
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libs
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Description: JPEG XL Plugin for gdk-pixbuf
|
||||
This package installs the required files for reading JPEG XL files in
|
||||
GTK applications.
|
||||
|
||||
Package: libjxl-gimp-plugin
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: graphics
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Enhances: gimp
|
||||
Description: JPEG XL Import and Export Plugin for GIMP
|
||||
This is a plugin for GIMP version 2.10.x to import and export JPEG XL images.
|
199
third-party/libjxl/libjxl/debian/copyright
vendored
Normal file
199
third-party/libjxl/libjxl/debian/copyright
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: jpeg-xl
|
||||
|
||||
Files: *
|
||||
Copyright: 2020 the JPEG XL Project
|
||||
License: BSD-3-clause
|
||||
|
||||
Files: third_party/libjpeg-turbo/*
|
||||
Copyright (C)2009-2023 D. R. Commander. All Rights Reserved.
|
||||
Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
|
||||
License: BSD-3-clause
|
||||
|
||||
Files: third_party/sjpeg/*
|
||||
Copyright: 2017 Google, Inc
|
||||
License: Apache-2.0
|
||||
|
||||
Files: third_party/skcms/*
|
||||
Copyright: 2018 Google Inc.
|
||||
License: BSD-3-clause
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
.
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Files: testdata/external/pngsuite/*
|
||||
Copyright: Willem van Schaik, 1996, 2011
|
||||
License: PngSuite License
|
||||
See http://www.schaik.com/pngsuite/ for details.
|
||||
.
|
||||
Permission to use, copy, modify and distribute these images for any
|
||||
purpose and without fee is hereby granted.
|
||||
|
||||
Files: testdata/external/raw.pixls/*
|
||||
Copyright: their respective owners listed in https://raw.pixls.us/
|
||||
License: CC0-1.0
|
||||
|
||||
Files: testdata/external/wesaturate/*
|
||||
Copyright: their respective owners listed in https://www.wesaturate.com/
|
||||
License: CC0-1.0
|
||||
|
||||
Files: testdata/external/wide-gamut-tests/
|
||||
Copyright: github.com/codelogic/wide-gamut-tests authors.
|
||||
License: Apache-2.0
|
||||
|
||||
License: Apache-2.0
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
.
|
||||
On Debian systems, the complete text of the Apache License, Version 2
|
||||
can be found in "/usr/share/common-licenses/Apache-2.0".
|
||||
|
||||
License: CC0
|
||||
Creative Commons Zero v1.0 Universal
|
||||
.
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL
|
||||
SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT
|
||||
RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS"
|
||||
BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS
|
||||
DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS
|
||||
LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE
|
||||
INFORMATION OR WORKS PROVIDED HEREUNDER.
|
||||
.
|
||||
Statement of Purpose
|
||||
.
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator and
|
||||
subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
.
|
||||
Certain owners wish to permanently relinquish those rights to a Work for the
|
||||
purpose of contributing to a commons of creative, cultural and scientific
|
||||
works ("Commons") that the public can reliably and without fear of later
|
||||
claims of infringement build upon, modify, incorporate in other works, reuse
|
||||
and redistribute as freely as possible in any form whatsoever and for any
|
||||
purposes, including without limitation commercial purposes. These owners may
|
||||
contribute to the Commons to promote the ideal of a free culture and the
|
||||
further production of creative, cultural and scientific works, or to gain
|
||||
reputation or greater distribution for their Work in part through the use
|
||||
and efforts of others.
|
||||
.
|
||||
For these and/or other purposes and motivations, and without any expectation
|
||||
of additional consideration or compensation, the person associating CC0 with
|
||||
a Work (the "Affirmer"), to the extent that he or she is an owner of
|
||||
Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to
|
||||
the Work and publicly distribute the Work under its terms, with knowledge of
|
||||
his or her Copyright and Related Rights in the Work and the meaning and
|
||||
intended legal effect of CC0 on those rights.
|
||||
.
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not limited
|
||||
to, the following:
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation thereof,
|
||||
including any amended or successor version of such directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national implementations
|
||||
thereof.
|
||||
.
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention of,
|
||||
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
|
||||
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
|
||||
and Related Rights and associated claims and causes of action, whether now
|
||||
known or unknown (including existing as well as future claims and causes of
|
||||
action), in the Work (i) in all territories worldwide, (ii) for the maximum
|
||||
duration provided by applicable law or treaty (including future time
|
||||
extensions), (iii) in any current or future medium and for any number of
|
||||
copies, and (iv) for any purpose whatsoever, including without limitation
|
||||
commercial, advertising or promotional purposes (the "Waiver"). Affirmer
|
||||
makes the Waiver for the benefit of each member of the public at large and
|
||||
to the detriment of Affirmer's heirs and successors, fully intending that
|
||||
such Waiver shall not be subject to revocation, rescission, cancellation,
|
||||
termination, or any other legal or equitable action to disrupt the quiet
|
||||
enjoyment of the Work by the public as contemplated by Affirmer's express
|
||||
Statement of Purpose.
|
||||
.
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason be
|
||||
judged legally invalid or ineffective under applicable law, then the Waiver
|
||||
shall be preserved to the maximum extent permitted taking into account
|
||||
Affirmer's express Statement of Purpose. In addition, to the extent the
|
||||
Waiver is so judged Affirmer hereby grants to each affected person a
|
||||
royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future time
|
||||
extensions), (iii) in any current or future medium and for any number of
|
||||
copies, and (iv) for any purpose whatsoever, including without limitation
|
||||
commercial, advertising or promotional purposes (the "License"). The License
|
||||
shall be deemed effective as of the date CC0 was applied by Affirmer to the
|
||||
Work. Should any part of the License for any reason be judged legally
|
||||
invalid or ineffective under applicable law, such partial invalidity or
|
||||
ineffectiveness shall not invalidate the remainder of the License, and in
|
||||
such case Affirmer hereby affirms that he or she will not (i) exercise any
|
||||
of his or her remaining Copyright and Related Rights in the Work or (ii)
|
||||
assert any associated claims and causes of action with respect to the Work,
|
||||
in either case contrary to Affirmer's express Statement of Purpose.
|
||||
.
|
||||
4. Limitations and Disclaimers.
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied, statutory or
|
||||
otherwise, including without limitation warranties of title,
|
||||
merchantability, fitness for a particular purpose, non infringement, or the
|
||||
absence of latent or other defects, accuracy, or the present or absence of
|
||||
errors, whether or not discoverable, all to the greatest extent permissible
|
||||
under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without limitation
|
||||
any person's Copyright and Related Rights in the Work. Further, Affirmer
|
||||
disclaims responsibility for obtaining any necessary consents, permissions
|
||||
or other rights required for any use of the Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to this
|
||||
CC0 or use of the Work.
|
||||
.
|
||||
For more information, please see:
|
||||
http://creativecommons.org/publicdomain/zero/1.0/>
|
||||
|
3
third-party/libjxl/libjxl/debian/jxl.install
vendored
Normal file
3
third-party/libjxl/libjxl/debian/jxl.install
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
usr/bin/*
|
||||
usr/share/man/man1/cjxl.1
|
||||
usr/share/man/man1/djxl.1
|
4
third-party/libjxl/libjxl/debian/libjxl-dev.install
vendored
Normal file
4
third-party/libjxl/libjxl/debian/libjxl-dev.install
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
usr/include/jxl/*.h
|
||||
usr/lib/*/*.a
|
||||
usr/lib/*/*.so
|
||||
usr/lib/*/pkgconfig/*.pc
|
3
third-party/libjxl/libjxl/debian/libjxl-gdk-pixbuf.install
vendored
Normal file
3
third-party/libjxl/libjxl/debian/libjxl-gdk-pixbuf.install
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
usr/lib/*/gdk-pixbuf-*/*/loaders/*
|
||||
usr/share/mime/packages/image-jxl.xml
|
||||
usr/share/thumbnailers/jxl.thumbnailer
|
1
third-party/libjxl/libjxl/debian/libjxl-gimp-plugin.install
vendored
Normal file
1
third-party/libjxl/libjxl/debian/libjxl-gimp-plugin.install
vendored
Normal file
@ -0,0 +1 @@
|
||||
usr/lib/gimp
|
1
third-party/libjxl/libjxl/debian/libjxl.install
vendored
Normal file
1
third-party/libjxl/libjxl/debian/libjxl.install
vendored
Normal file
@ -0,0 +1 @@
|
||||
usr/lib/*/libjxl*.so.*
|
21
third-party/libjxl/libjxl/debian/rules
vendored
Executable file
21
third-party/libjxl/libjxl/debian/rules
vendored
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
include /usr/share/dpkg/pkg-info.mk
|
||||
|
||||
%:
|
||||
dh $@ --buildsystem=cmake
|
||||
|
||||
override_dh_auto_configure:
|
||||
# TODO(deymo): Remove the DCMAKE_BUILD_TYPE once builds without NDEBUG
|
||||
# are as useful as Release builds.
|
||||
# TODO(szabadka) Re-enable jpegli after tests are fixed on Ubuntu 20.04,
|
||||
# and debian:buster
|
||||
dh_auto_configure -- \
|
||||
-DJPEGXL_VERSION=$(DEB_VERSION) \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DJPEGXL_FORCE_SYSTEM_GTEST=ON \
|
||||
-DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
-DJPEGXL_FORCE_SYSTEM_HWY=ON \
|
||||
-DJPEGXL_ENABLE_JPEGLI=OFF \
|
||||
-DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF \
|
||||
-DJPEGXL_ENABLE_PLUGINS=ON
|
1
third-party/libjxl/libjxl/debian/source/format
vendored
Normal file
1
third-party/libjxl/libjxl/debian/source/format
vendored
Normal file
@ -0,0 +1 @@
|
||||
3.0 (quilt)
|
93
third-party/libjxl/libjxl/deps.sh
vendored
Executable file
93
third-party/libjxl/libjxl/deps.sh
vendored
Executable file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# This file downloads the dependencies needed to build JPEG XL into third_party.
|
||||
# These dependencies are normally pulled by gtest.
|
||||
|
||||
set -eu
|
||||
|
||||
MYDIR=$(dirname $(realpath "$0"))
|
||||
|
||||
# Git revisions we use for the given submodules. Update these whenever you
|
||||
# update a git submodule.
|
||||
THIRD_PARTY_BROTLI="36533a866ed1ca4b75cf049f4521e4ec5fe24727"
|
||||
THIRD_PARTY_HIGHWAY="591ad359a5aa6c320951ebd35f839604c87abe6c"
|
||||
THIRD_PARTY_SKCMS="b25b07b4b07990811de121c0356155b2ba0f4318"
|
||||
THIRD_PARTY_SJPEG="e5ab13008bb214deb66d5f3e17ca2f8dbff150bf"
|
||||
THIRD_PARTY_ZLIB="cacf7f1d4e3d44d871b605da3b647f07d718623f"
|
||||
THIRD_PARTY_LIBPNG="a40189cf881e9f0db80511c382292a5604c3c3d1"
|
||||
THIRD_PARTY_LIBJPEG_TURBO="8ecba3647edb6dd940463fedf38ca33a8e2a73d1" # 2.1.5.1
|
||||
|
||||
# Download the target revision from GitHub.
|
||||
download_github() {
|
||||
local path="$1"
|
||||
local project="$2"
|
||||
|
||||
local varname=`echo "$path" | tr '[:lower:]' '[:upper:]'`
|
||||
varname="${varname/[\/-]/_}"
|
||||
local sha
|
||||
eval "sha=\${${varname}}"
|
||||
|
||||
local down_dir="${MYDIR}/downloads"
|
||||
local local_fn="${down_dir}/${sha}.tar.gz"
|
||||
if [[ -e "${local_fn}" && -d "${MYDIR}/${path}" ]]; then
|
||||
echo "${path} already up to date." >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
local url
|
||||
local strip_components=0
|
||||
if [[ "${project:0:4}" == "http" ]]; then
|
||||
# "project" is a googlesource.com base url.
|
||||
url="${project}${sha}.tar.gz"
|
||||
else
|
||||
# GitHub files have a top-level directory
|
||||
strip_components=1
|
||||
url="https://github.com/${project}/tarball/${sha}"
|
||||
fi
|
||||
|
||||
echo "Downloading ${path} version ${sha}..." >&2
|
||||
mkdir -p "${down_dir}"
|
||||
curl -L --show-error -o "${local_fn}.tmp" "${url}"
|
||||
mkdir -p "${MYDIR}/${path}"
|
||||
tar -zxf "${local_fn}.tmp" -C "${MYDIR}/${path}" \
|
||||
--strip-components="${strip_components}"
|
||||
mv "${local_fn}.tmp" "${local_fn}"
|
||||
}
|
||||
|
||||
is_git_repository() {
|
||||
local dir="$1"
|
||||
local toplevel=$(git rev-parse --show-toplevel)
|
||||
|
||||
[[ "${dir}" == "${toplevel}" ]]
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
if is_git_repository "${MYDIR}"; then
|
||||
cat >&2 <<EOF
|
||||
Current directory is a git repository, downloading dependencies via git:
|
||||
|
||||
git submodule update --init --recursive
|
||||
|
||||
EOF
|
||||
git -C "${MYDIR}" submodule update --init --recursive --depth 1 --recommend-shallow
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Sources downloaded from a tarball.
|
||||
download_github third_party/brotli google/brotli
|
||||
download_github third_party/highway google/highway
|
||||
download_github third_party/sjpeg webmproject/sjpeg
|
||||
download_github third_party/skcms \
|
||||
"https://skia.googlesource.com/skcms/+archive/"
|
||||
download_github third_party/zlib madler/zlib
|
||||
download_github third_party/libpng glennrp/libpng
|
||||
download_github third_party/libjpeg-turbo libjpeg-turbo/libjpeg-turbo
|
||||
echo "Done."
|
||||
}
|
||||
|
||||
main "$@"
|
29
third-party/libjxl/libjxl/doc/api.txt
vendored
Normal file
29
third-party/libjxl/libjxl/doc/api.txt
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
/* This document is meant for Doxygen use only. If you are looking for the API
|
||||
* documentation generate it with `./ci.sh release` and look under the
|
||||
* build/html directory.
|
||||
*
|
||||
* This file documents all the groups and defines the order in which they appear
|
||||
* in Doxygen. Define the @defgroup commands here and use @addtogroup anywhere
|
||||
* else.
|
||||
*/
|
||||
|
||||
/**
|
||||
@defgroup libjxl JPEG XL library (libjxl)
|
||||
@brief The main JPEG XL decoder / encoder library.
|
||||
|
||||
@addtogroup libjxl
|
||||
@{
|
||||
|
||||
@defgroup libjxl_decoder JPEG XL Decoder
|
||||
|
||||
@defgroup libjxl_encoder JPEG XL Encoder
|
||||
|
||||
@defgroup libjxl_common JPEG XL common definitions
|
||||
|
||||
@defgroup libjxl_butteraugli Butteraugli metric
|
||||
|
||||
@}
|
||||
|
||||
@defgroup libjxl_threads JPEG XL Multi-thread library (libjxl_threads)
|
||||
@brief Additional multi-threaded implementations for the parallel runner.
|
||||
*/
|
82
third-party/libjxl/libjxl/doc/benchmarking.md
vendored
Normal file
82
third-party/libjxl/libjxl/doc/benchmarking.md
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
# Benchmarking
|
||||
|
||||
For speed benchmarks on single images in single or multi-threaded decoding
|
||||
`djxl` can print decoding speed information. See `djxl --help` for details
|
||||
on the decoding options and note that the output image is optional for
|
||||
benchmarking purposes.
|
||||
|
||||
For a more comprehensive comparison of compression density between multiple
|
||||
options, the tool `benchmark_xl` can be used (see below).
|
||||
|
||||
## Benchmarking with benchmark_xl
|
||||
|
||||
We recommend `build/tools/benchmark_xl` as a convenient method for reading
|
||||
images or image sequences, encoding them using various codecs (jpeg jxl png
|
||||
webp), decoding the result, and computing objective quality metrics. An example
|
||||
invocation is:
|
||||
|
||||
```bash
|
||||
build/tools/benchmark_xl --input "/path/*.png" --codec jxl:wombat:d1,jxl:cheetah:d2
|
||||
```
|
||||
|
||||
Multiple comma-separated codecs are allowed. The characters after : are
|
||||
parameters for the codec, separated by colons, in this case specifying maximum
|
||||
target psychovisual distances of 1 and 2 (higher implies lower quality) and
|
||||
the encoder effort (see below). Other common parameters are `r0.5` (target
|
||||
bitrate 0.5 bits per pixel) and `q92` (quality 92, on a scale of 0-100, where
|
||||
higher is better). The `jxl` codec supports the following additional parameters:
|
||||
|
||||
Speed: `lightning`, `thunder`, `falcon`, `cheetah`, `hare`, `wombat`, `squirrel`,
|
||||
`kitten`, `tortoise` control the encoder effort in ascending order. This also
|
||||
affects memory usage: using lower effort will typically reduce memory consumption
|
||||
during encoding.
|
||||
|
||||
* `lightning` and `thunder` are fast modes useful for lossless mode (modular).
|
||||
* `falcon` disables all of the following tools.
|
||||
* `cheetah` enables coefficient reordering, context clustering, and heuristics
|
||||
for selecting DCT sizes and quantization steps.
|
||||
* `hare` enables Gaborish filtering, chroma from luma, and an initial estimate
|
||||
of quantization steps.
|
||||
* `wombat` enables error diffusion quantization and full DCT size selection
|
||||
heuristics.
|
||||
* `squirrel` (default) enables dots, patches, and spline detection, and full
|
||||
context clustering.
|
||||
* `kitten` optimizes the adaptive quantization for a psychovisual metric.
|
||||
* `tortoise` enables a more thorough adaptive quantization search.
|
||||
|
||||
Mode: JPEG XL has two modes. The default is Var-DCT mode, which is suitable for
|
||||
lossy compression. The other mode is Modular mode, which is suitable for lossless
|
||||
compression. Modular mode can also do lossy compression (e.g. `jxl:m:q50`).
|
||||
|
||||
* `m` activates modular mode.
|
||||
|
||||
Other arguments to benchmark_xl include:
|
||||
|
||||
* `--save_compressed`: save codestreams to `output_dir`.
|
||||
* `--save_decompressed`: save decompressed outputs to `output_dir`.
|
||||
* `--output_extension`: selects the format used to output decoded images.
|
||||
* `--num_threads`: number of codec instances that will independently
|
||||
encode/decode images, or 0.
|
||||
* `--inner_threads`: how many threads each instance should use for parallel
|
||||
encoding/decoding, or 0.
|
||||
* `--encode_reps`/`--decode_reps`: how many times to repeat encoding/decoding
|
||||
each image, for more consistent measurements (we recommend 10).
|
||||
|
||||
The benchmark output begins with a header:
|
||||
|
||||
```
|
||||
Compr Input Compr Compr Compr Decomp Butteraugli
|
||||
Method Pixels Size BPP # MP/s MP/s Distance Error p norm BPP*pnorm Errors
|
||||
```
|
||||
|
||||
`ComprMethod` lists each each comma-separated codec. `InputPixels` is the number
|
||||
of pixels in the input image. `ComprSize` is the codestream size in bytes and
|
||||
`ComprBPP` the bitrate. `Compr MP/s` and `Decomp MP/s` are the
|
||||
compress/decompress throughput, in units of Megapixels/second.
|
||||
`Butteraugli Distance` indicates the maximum psychovisual error in the decoded
|
||||
image (larger is worse). `Error p norm` is a similar summary of the psychovisual
|
||||
error, but closer to an average, giving less weight to small low-quality
|
||||
regions. `BPP*pnorm` is the product of `ComprBPP` and `Error p norm`, which is a
|
||||
figure of merit for the codec (lower is better). `Errors` is nonzero if errors
|
||||
occurred while loading or encoding/decoding the image.
|
||||
|
166
third-party/libjxl/libjxl/doc/building_and_testing.md
vendored
Normal file
166
third-party/libjxl/libjxl/doc/building_and_testing.md
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
# Building and Testing
|
||||
|
||||
This file describes the building and testing facilities provided by the `ci.sh`
|
||||
script. It assumes you already have the build environment set up.
|
||||
|
||||
## Basic building
|
||||
|
||||
To build the JPEG XL software and run its unit tests, run:
|
||||
|
||||
```bash
|
||||
./ci.sh release
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
`./ci.sh` build commands including `release`, `opt`, etc. will also run tests.
|
||||
You can set the environment variable `SKIP_TEST=1` to skip this.
|
||||
|
||||
It is possible to manually run all the tests in parallel in all your CPUs with
|
||||
the command:
|
||||
|
||||
```bash
|
||||
./ci.sh test
|
||||
```
|
||||
|
||||
It is also possible for faster iteration to run a specific test binary directly.
|
||||
Tests are run with the `ctest` command and arguments passed to `ci.sh test` are
|
||||
forwarded to `ctest` with the appropriate environment variables set. For
|
||||
example, to list all the available tests you can run:
|
||||
|
||||
```bash
|
||||
./ci.sh test -N
|
||||
```
|
||||
|
||||
To run a specific test from the list or actually a set of tests matching a
|
||||
regular expression you can use `ctest`'s parameter `-R`:
|
||||
|
||||
```bash
|
||||
./ci.sh test -R ^MyPrefixTe
|
||||
```
|
||||
|
||||
That command would run any test whose name that starts with `MyPrefixTe`. For
|
||||
more options run `ctest --help`, for example, you can pass `-j1` if you want
|
||||
to run only one test at a time instead of our default of multiple tests in
|
||||
parallel.
|
||||
|
||||
## Other commands
|
||||
|
||||
Running `./ci.sh` with no parameters shows a list of available commands. For
|
||||
example, you can run `opt` for optimized developer builds with symbols or
|
||||
`debug` for debug builds which do not have NDEBUG defined and therefore include
|
||||
more runtime debug information.
|
||||
|
||||
### Cross-compiling
|
||||
|
||||
To compile the code for an architecture different than the one you are running
|
||||
you can pass a
|
||||
[toolchain file](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html)
|
||||
to cmake if you have one for your target, or you can use the `BUILD_TARGET`
|
||||
environment variable in `./ci.sh`. For some targets such the Windows targets
|
||||
`ci.sh` sets up extra environment variables that are needed for testing.
|
||||
|
||||
This assumes that you already have a cross-compiling environment set up and the
|
||||
library dependencies are already installed for the target architecture as well.
|
||||
|
||||
For example, to compile for the `aarch64-linux-gnu` target triplet you can run:
|
||||
|
||||
```bash
|
||||
BUILD_TARGET=aarch64-linux-gnu ./ci.sh release
|
||||
```
|
||||
|
||||
Whenever using a `BUILD_TARGET` or even a custom `BUILD_DIR` these variables
|
||||
must be set for **every call** to `ci.sh` even calls to `ci.sh test`, for which
|
||||
we recommend exporting them in your shell session, for example:
|
||||
|
||||
```bash
|
||||
export BUILD_TARGET=x86_64-w64-mingw32 BUILD_DIR=build-foobar
|
||||
```
|
||||
|
||||
### Format checks (lint)
|
||||
|
||||
```bash
|
||||
./ci.sh lint
|
||||
```
|
||||
|
||||
Linter checks will verify that the format of your patch conforms to the project
|
||||
style. For this, we run clang-format only on the lines that were changed by
|
||||
your commits.
|
||||
|
||||
If your local git branch is tracking `origin/master` and you landed a few
|
||||
commits in your branch, running this lint command will check all the changes
|
||||
made from the common ancestor with `origin/master` to the latest changes,
|
||||
including uncommitted changes. The output of the program will show the patch
|
||||
that should be applied to fix your commits. You can apply these changes with the
|
||||
following command from the base directory of the git checkout:
|
||||
|
||||
```bash
|
||||
./ci.sh lint | patch -p1
|
||||
```
|
||||
|
||||
### Programming errors (tidy)
|
||||
|
||||
```bash
|
||||
./ci.sh tidy
|
||||
```
|
||||
|
||||
clang-tidy is a tool to check common programming errors in C++, and other valid
|
||||
C++ constructions that are discouraged by the style guide or otherwise dangerous
|
||||
and may constitute a bug.
|
||||
|
||||
To run clang-tidy on the files changed by your changes you can run `./ci.sh
|
||||
tidy`. Note that this will report all the problems encountered in any file that
|
||||
was modified by one of your commits, not just on the lines that your commits
|
||||
modified.
|
||||
|
||||
|
||||
### Address Sanitizer (asan)
|
||||
|
||||
```bash
|
||||
./ci.sh asan
|
||||
```
|
||||
|
||||
ASan builds allow to check for invalid address usages, such as use-after-free.
|
||||
To perform these checks, as well as other undefined behavior checks we only need
|
||||
to build and run the unittests with ASan enabled which can be easily achieved
|
||||
with the command above. If you want to have the ASan build files separated from
|
||||
your regular `build/` directory to quickly switch between asan and regular
|
||||
builds, you can pass the build directory target as follows:
|
||||
|
||||
```bash
|
||||
BUILD_DIR=build-asan ./ci.sh asan
|
||||
```
|
||||
|
||||
### Memory Sanitizer (msan)
|
||||
|
||||
MSan allows to check for invalid memory accesses at runtime, such as using an
|
||||
uninitialized value which likely means that there is a bug. To run these checks,
|
||||
a specially compiled version of the project and tests is needed.
|
||||
|
||||
For building with MSan, you need to build a version of libc++ with
|
||||
`-fsanitize=memory` so we can link against it from the MSan build. Also, having
|
||||
an `llvm-symbolizer` installed is very helpful to obtain stack traces that
|
||||
include the symbols (functions and line numbers). To install `llvm-symbolizer`
|
||||
on a Debian-based system run:
|
||||
|
||||
```bash
|
||||
sudo apt install llvm # or llvm-7, etc for a specific version.
|
||||
```
|
||||
|
||||
To install a version of libc++ compiled with `-fsanitize=memory` you can use the
|
||||
`./ci.sh msan_install` command helper. This will download, compile and install
|
||||
libc++ and libc++abi in the `${HOME}/.msan` directory to be used later.
|
||||
|
||||
After this is set up, you can build the project using the following command:
|
||||
|
||||
```bash
|
||||
./ci.sh msan
|
||||
```
|
||||
|
||||
This command by default uses the `build` directory to store the cmake and object
|
||||
files. If you want to have a separate build directory configured with msan you
|
||||
can for example call:
|
||||
|
||||
```bash
|
||||
BUILD_DIR=build-msan ./ci.sh msan
|
||||
```
|
62
third-party/libjxl/libjxl/doc/building_wasm.md
vendored
Normal file
62
third-party/libjxl/libjxl/doc/building_wasm.md
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
# Building WASM artifacts
|
||||
|
||||
This file describes the building and testing of JPEG XL
|
||||
[Web Assembly](https://webassembly.org/) bundles and wrappers.
|
||||
|
||||
These instructions assume an up-to-date Debian/Ubuntu system.
|
||||
|
||||
For the sake of simplicity, it is considered, that the following environment
|
||||
variables are set:
|
||||
|
||||
* `OPT` - path to the directory containing additional software;
|
||||
the `emsdk` directory with the Emscripten SDK should reside there.
|
||||
|
||||
## Requirements
|
||||
|
||||
[CMake](https://cmake.org/) is used as a build system. To install it, follow
|
||||
[Debian build instructions](developing_in_debian.md).
|
||||
|
||||
[Emscripten SDK](https://emscripten.org/) is required for building
|
||||
WebAssembly artifacts. To install it, follow the
|
||||
[Download and Install](https://emscripten.org/docs/getting_started/downloads.html)
|
||||
guide:
|
||||
|
||||
```bash
|
||||
cd $OPT
|
||||
|
||||
# Get the emsdk repo.
|
||||
git clone https://github.com/emscripten-core/emsdk.git
|
||||
|
||||
# Enter that directory.
|
||||
cd emsdk
|
||||
|
||||
# Download and install the latest SDK tools.
|
||||
./emsdk install latest
|
||||
|
||||
# Make the "latest" SDK "active" for the current user. (writes ~/.emscripten file)
|
||||
./emsdk activate latest
|
||||
```
|
||||
|
||||
## Building and testing the project
|
||||
|
||||
```bash
|
||||
# Setup EMSDK and other environment variables. In practice EMSDK is set to be
|
||||
# $OPT/emsdk.
|
||||
source $OPT/emsdk/emsdk_env.sh
|
||||
|
||||
# This should set the $EMSDK variable.
|
||||
# If your node version is <16.4.0, you might need to update to a newer version or override
|
||||
# the node binary with a version which supports SIMD:
|
||||
echo "NODE_JS='/path/to/node_binary'" >> $EMSDK/.emscripten
|
||||
|
||||
# Assuming you are in the root level of the cloned libjxl repo,
|
||||
# either build with regular WASM:
|
||||
BUILD_TARGET=wasm32 emconfigure ./ci.sh release
|
||||
# or with SIMD WASM:
|
||||
BUILD_TARGET=wasm32 ENABLE_WASM_SIMD=1 emconfigure ./ci.sh release
|
||||
```
|
||||
|
||||
## Example site
|
||||
|
||||
Once you have build the wasm binary, you can give it a try by building a site
|
||||
that decodes jxl images, see [wasm_demo](../tools/wasm_demo/README.md).
|
68
third-party/libjxl/libjxl/doc/color_management.md
vendored
Normal file
68
third-party/libjxl/libjxl/doc/color_management.md
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
# Color Management
|
||||
|
||||
[TOC]
|
||||
|
||||
<!--*
|
||||
# Document freshness: For more information, see go/fresh-source.
|
||||
freshness: { owner: 'sboukortt' reviewed: '2022-09-27' }
|
||||
*-->
|
||||
|
||||
## Why
|
||||
|
||||
The vast majority of web images are still sRGB. However, wide-gamut material is
|
||||
increasingly being produced (photography, cinema, 4K). Screens covering most of
|
||||
the Adobe RGB gamut are readily available and some also cover most of DCI P3
|
||||
(iPhone, Pixel2) or even BT.2020.
|
||||
|
||||
Currently, after a camera records a very saturated red pixel, most raw
|
||||
processors would clip it to the rather small sRGB gamut before saving as JPEG.
|
||||
In keeping with our high-quality goal, we prevent such loss by allowing wider
|
||||
input color spaces.
|
||||
|
||||
## Which color space
|
||||
|
||||
Even wide gamuts could be expressed relative to the sRGB primaries, but the
|
||||
resulting coordinates may be outside the valid 0..1 range. Surprisingly, such
|
||||
'unbounded' coordinates can be passed through color transforms provided the
|
||||
transfer functions are expressed as parametric functions (not lookup tables).
|
||||
However, most image file formats (including PNG and PNM) lack min/max metadata
|
||||
and thus do not support unbounded coordinates.
|
||||
|
||||
Instead, we need a larger working gamut to ensure most pixel coordinates are
|
||||
within bounds and thus not clipped. However, larger gamuts result in lower
|
||||
precision/resolution when using <= 16 bit encodings (as opposed to 32-bit float
|
||||
in PFM). BT.2100 or P3 DCI appear to be good compromises.
|
||||
|
||||
## CMS library
|
||||
|
||||
Transforms with unbounded pixels are desirable because they reduce round-trip
|
||||
error in tests. This requires parametric curves, which are only supported for
|
||||
the common sRGB case in ICC v4 profiles. ArgyllCMS does not support v4. The
|
||||
other popular open-source CMS is LittleCMS. It is also used by color-managed
|
||||
editors (Krita/darktable), which increases the chances of interoperability.
|
||||
However, LCMS has race conditions and overflow issues that prevent fuzzing. We
|
||||
will later switch to the newer skcms. Note that this library does not intend to
|
||||
support multiProcessElements, so HDR transfer functions cannot be represented
|
||||
accurately. Thus in the long term, we will probably migrate away from ICC
|
||||
profiles entirely.
|
||||
|
||||
## Which viewer
|
||||
|
||||
On Linux, Krita and darktable support loading our PNG output images and their
|
||||
ICC profile.
|
||||
|
||||
## How to compress/decompress
|
||||
|
||||
### Embedded ICC profile
|
||||
|
||||
- Create an 8-bit or 16-bit PNG with an iCCP chunk, e.g. using darktable.
|
||||
- Pass it to `cjxl`, then `djxl` with no special arguments. The decoded output
|
||||
will have the same bit depth (can override with `--output_bit_depth`) and
|
||||
color space.
|
||||
|
||||
### Images without metadata (e.g. HDR)
|
||||
|
||||
- Create a PGM/PPM/PFM file in a known color space.
|
||||
- Invoke `cjxl` with `-x color_space=RGB_D65_202_Rel_Lin` (linear 2020). For
|
||||
details/possible values, see color_encoding.cc `Description`.
|
||||
- Invoke `djxl` as above with no special arguments.
|
56
third-party/libjxl/libjxl/doc/developing_in_debian.md
vendored
Normal file
56
third-party/libjxl/libjxl/doc/developing_in_debian.md
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
# Developing in Debian
|
||||
|
||||
These instructions assume an up-to-date Debian/Ubuntu system.
|
||||
For other platforms, please instead use the following:
|
||||
|
||||
* [Cross Compiling for Windows with Crossroad](developing_with_crossroad.md).
|
||||
|
||||
## Minimum build dependencies
|
||||
|
||||
Apart from the dependencies in `third_party`, some of the tools use external
|
||||
dependencies that need to be installed on your system first:
|
||||
|
||||
```bash
|
||||
sudo apt install cmake clang doxygen g++ extra-cmake-modules \
|
||||
libgif-dev libjpeg-dev ninja-build libgoogle-perftools-dev
|
||||
```
|
||||
|
||||
Make sure your default `clang` compiler is at least version 6 by running
|
||||
|
||||
```bash
|
||||
clang --version
|
||||
```
|
||||
|
||||
If it still shows an old version despite having, for example, `clang-7` installed, you need
|
||||
to update the default `clang` compiler. On Debian-based systems run:
|
||||
|
||||
```bash
|
||||
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-7 100
|
||||
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-7 100
|
||||
```
|
||||
|
||||
Optionally, to compile some of the extra tool support and tests you can install
|
||||
the following packages:
|
||||
|
||||
```bash
|
||||
sudo apt install qt6-base-dev libwebp-dev libgimp2.0-dev libopenexr-dev \
|
||||
libgtest-dev libgmock-dev libbenchmark-dev libbenchmark-tools
|
||||
```
|
||||
|
||||
For the lint/coverage commands, you will also need additional packages:
|
||||
|
||||
```bash
|
||||
sudo apt install clang-format clang-tidy curl parallel gcovr
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
The `libjxl` project uses CMake to build. We provide a script that simplifies the
|
||||
invocation. To build and test the project, run
|
||||
|
||||
```bash
|
||||
./ci.sh opt
|
||||
```
|
||||
|
||||
This writes binaries to `build/tools` and runs unit tests. More information
|
||||
on [build modes and testing](building_and_testing.md) is available.
|
357
third-party/libjxl/libjxl/doc/developing_in_github.md
vendored
Normal file
357
third-party/libjxl/libjxl/doc/developing_in_github.md
vendored
Normal file
@ -0,0 +1,357 @@
|
||||
# Developing in GitHub
|
||||
|
||||
This document describes the development steps related to handling the git
|
||||
repository.
|
||||
|
||||
If you are new to GitHub, there's a nice [quickstart
|
||||
guide](https://docs.github.com/en/github/getting-started-with-github/quickstart)
|
||||
on GitHub explaining the basics.
|
||||
|
||||
## Initial setup
|
||||
|
||||
You need to perform this set up at least once if you haven't use GitHub before.
|
||||
Read through the quickstart guide [Set up
|
||||
Git](https://docs.github.com/en/github/getting-started-with-github/set-up-git)
|
||||
page to get your git up and running. You will need to Fork a repository next.
|
||||
After that "Life of a Pull Request" describes the common everyday workflows.
|
||||
|
||||
### Configure your SSH access
|
||||
|
||||
The easiest way to configure access to your Github repository is to use SSH
|
||||
keys. For that you need an SSH private and public key, ideally a strong one. You
|
||||
can use different keys for different sites if you want. In this example, we will
|
||||
create one for using in GitHub only.
|
||||
|
||||
Create the `~/.ssh/id_rsa_github` file executing the following. (Here and
|
||||
elsewhere, {{X}} are placeholders for your email/username)
|
||||
|
||||
```bash
|
||||
ssh-keygen -t rsa -b 4096 -C "{{EMAIL}}" -f ~/.ssh/id_rsa_github
|
||||
```
|
||||
|
||||
Go to your [SSH and GPG keys](https://github.com/settings/keys) settings and
|
||||
paste the contents of your *public key* (the one ending in `.pub`), that would
|
||||
be the output of this command:
|
||||
|
||||
```bash
|
||||
cat ~/.ssh/id_rsa_github.pub
|
||||
```
|
||||
|
||||
To use a specific key when SSHing to the github.com domain, you can add this
|
||||
snippet of config to your .ssh/config file executing the following.
|
||||
|
||||
```bash
|
||||
cat >> ~/.ssh/config <<EOF
|
||||
|
||||
Host github.com
|
||||
Hostname github.com
|
||||
IdentityFile ~/.ssh/id_rsa_github
|
||||
IdentitiesOnly yes
|
||||
EOF
|
||||
```
|
||||
|
||||
The `IdentitiesOnly yes` part forces to only use the provided IdentityFile when
|
||||
talking to GitHub.
|
||||
|
||||
### Fork your private copy
|
||||
|
||||
The JPEG XL code is located in [this repo](https://github.com/libjxl/libjxl).
|
||||
|
||||
The normal developer workflow in GitHub involves creating your own fork of a
|
||||
repository and uploading your own changes there. From your own copy you can
|
||||
request merges *to* the upstream repository directly, there's no need to create
|
||||
a branch in the upstream repository.
|
||||
|
||||
[Fork the
|
||||
repository](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo)
|
||||
in GitHub to create your own copy of the repository in GitHub. You can then
|
||||
propose to include changes in the main repository via a Pull Request.
|
||||
|
||||
Once you are done you should have your repository at
|
||||
|
||||
https://<!-- not a link -->github.com<!-- not a link -->/*{{USERNAME}}*/libjxl
|
||||
|
||||
where {{USERNAME}} denotes your GitHub username.
|
||||
|
||||
### Checkout the JPEG XL code from GitHub
|
||||
|
||||
To get the source code on your computer you need to "clone" it. There are two
|
||||
repositories at play here, the upstream repository (`libjxl/lbjxl`) and your
|
||||
fork (`{{USERNAME}}/libjxl`). You will be normally fetching new changes from
|
||||
the upstream repository and push changes to your fork. Getting your changes from
|
||||
your fork to the upstream repository is done through the Web interface, via Pull
|
||||
Requests.
|
||||
|
||||
The [Fork a
|
||||
repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo)
|
||||
goes in great detail, but uses the git remote names `upstream` for the shared
|
||||
upstream repository and `origin` for your work. This guide proposes an
|
||||
alternative naming scheme, used in the examples below.
|
||||
|
||||
In this guide `origin` is the upstream shared repository and `myfork` is your
|
||||
fork. You can use any other name for your fork if you want. Use the following
|
||||
commands to set things up, replacing `{{USERNAME}}` with your GitHub username:
|
||||
|
||||
```bash
|
||||
git clone git https://github.com/libjxl/libjxl --recursive
|
||||
cd libjxl
|
||||
git remote set-url --push origin git@github.com:{{USERNAME}}/libjxl.git
|
||||
git remote add myfork git@github.com:{{USERNAME}}/libjxl.git
|
||||
git remote -vv
|
||||
```
|
||||
|
||||
These commands did three things:
|
||||
|
||||
* Created the repository with `origin` as the upstream remote,
|
||||
* Changed the "push" URL to point to your fork, and
|
||||
* Create a new remote pointing to your fork.
|
||||
|
||||
The last step is optional. Since the "fetch" URL of `origin` points to the
|
||||
shared repository and the "push" URL points to your fork, fetching from `origin`
|
||||
always gets the latest changes from the upstream repository regardless of the
|
||||
contents of your fork.
|
||||
|
||||
Having a second origin called `myfork` is only useful if you need to download
|
||||
pending changes from your fork from a different computer. For example, if you
|
||||
work on multiple computers, each one with this setup, you can push to your
|
||||
fork from one, and then fetch from `myfork` from another computer to get those.
|
||||
|
||||
# Life of a Pull Request
|
||||
|
||||
The general [GitHub flow
|
||||
guide](https://docs.github.com/en/github/getting-started-with-github/github-flow)
|
||||
applies to sending Pull Requests to this project.
|
||||
|
||||
All the commands here assume you are in a git checkout as setup here.
|
||||
|
||||
### Sync to the latest version
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
```
|
||||
|
||||
The last upstream version is now on `origin/main` and none of your local
|
||||
branches have been modified by this command.
|
||||
|
||||
### Start a new branch
|
||||
|
||||
To start a new change you need a local branch. Each branch will represent a list
|
||||
of individual commits which can then be requested to be merged as a single merge
|
||||
request. So in general one branch is one code review, but each branch can have
|
||||
multiple individual commits in it.
|
||||
|
||||
```bash
|
||||
git checkout origin/main -b mybranch
|
||||
```
|
||||
|
||||
This will create a new branch `mybranch` tracking `origin/main`. A branch can
|
||||
track any remove or local branch, which is used by some tools. Running `git
|
||||
branch -vv` will show all the branches you have have, what are they tracking and
|
||||
how many commits are ahead or behind. If you create a branch without tracking
|
||||
any other, you can add or change the tracking branch of the current branch
|
||||
running `git branch --set-upstream-to=...`.
|
||||
|
||||
### Add changes to your branch
|
||||
|
||||
Follow any of the many online tutorials, for example
|
||||
[The basics](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository)
|
||||
chapter from the https://git-scm.com/doc website is a good starting guide.
|
||||
Create, change or delete files and do a git commit with a message.
|
||||
|
||||
The commit message is required. A commit message should follow the 50/72 rule:
|
||||
|
||||
* First line is 50 characters or less.
|
||||
* Then a blank line.
|
||||
* Remaining text should be wrapped at 72 characters.
|
||||
|
||||
The first line should identify your commit, since that's what most tools will
|
||||
show to the user. First lines like "Some fixes" are not useful. Explain what the
|
||||
commit contains and why.
|
||||
|
||||
We follow the [Google C++ Coding
|
||||
Style](https://google.github.io/styleguide/cppguide.html). A
|
||||
[clang-format](https://clang.llvm.org/docs/ClangFormat.html) configuration
|
||||
file is available to automatically format your code, you can invoke it with
|
||||
the `./ci.sh lint` helper tool.
|
||||
|
||||
Read the [CONTRIBUTING.md](../CONTRIBUTING.md) file for more information about
|
||||
contributing to libjxl.
|
||||
|
||||
### Upload your changes for review
|
||||
|
||||
The first step is a local review of your changes to see what will you be sending
|
||||
for review. `gitg` is a nice Gtk UI for reviewing your local changes, or `tig`
|
||||
for similar ncurses console-based interface. Otherwise, from the terminal you
|
||||
can run:
|
||||
|
||||
```bash
|
||||
git branch -vv
|
||||
```
|
||||
|
||||
To show the current status of your local branches. In particular, since your
|
||||
branch is tracking origin/main (as seen in the output) git will tell you that
|
||||
you are one commit ahead of the tracking branch.
|
||||
|
||||
```
|
||||
* mybranch e74ae1a [origin/main: ahead 1] Improved decoding speed by 40%
|
||||
```
|
||||
|
||||
It is a good idea before uploading to sync again with upstream (`git fetch
|
||||
origin`) and then run `git branch -vv` to check whether there are new changes
|
||||
upstream. If that is the case, you will see a "behind" flag in the output:
|
||||
|
||||
```
|
||||
* mybranch e74ae1a [origin/main: ahead 1, behind 2] Improved decoding speed by 40%
|
||||
```
|
||||
|
||||
To sync your changes on top of the latest changes in upstream you need to
|
||||
rebase:
|
||||
|
||||
```bash
|
||||
git rebase
|
||||
```
|
||||
|
||||
This will by default rebase your current branch changes on top of the tracking
|
||||
branch. In this case, this will try to apply the current commit on top of the
|
||||
latest origin/main (which has 2 more commits than the ones we have in our
|
||||
branch) and your branch will now include that. There could be conflicts that you
|
||||
have to deal with. A shortcut to do both fetch and rebase is to run `git pull
|
||||
-r`, where the `-r` stands for "rebase" and will rebase the local commits on top
|
||||
of the remote ones.
|
||||
|
||||
Before uploading a patch, make sure your patch conforms to the
|
||||
[contributing guidelines](../CONTRIBUTING.md) and it
|
||||
[builds and passes tests](building_and_testing.md).
|
||||
|
||||
Once you are ready to send your branch for review, upload it to *your* fork:
|
||||
|
||||
```bash
|
||||
git push origin mybranch
|
||||
```
|
||||
|
||||
This will push your local branch "mybranch" to a remote in your fork called
|
||||
"mybranch". The name can be anything, but keep in mind that it is public. A link
|
||||
to the URL to create a merge request will be displayed.
|
||||
|
||||
```
|
||||
Enumerating objects: 627, done.
|
||||
Counting objects: 100% (627/627), done.
|
||||
Delta compression using up to 56 threads
|
||||
Compressing objects: 100% (388/388), done.
|
||||
Writing objects: 100% (389/389), 10.71 MiB | 8.34 MiB/s, done.
|
||||
Total 389 (delta 236), reused 0 (delta 0)
|
||||
emote:
|
||||
remote: Create a pull request for 'mybranch' on GitHub by visiting:
|
||||
remote: https://github.com/{{USERNAME}}/libjxl/pull/new/mybranch
|
||||
remote:
|
||||
To github.com:{{USERNAME}}/libjxl.git
|
||||
* [new branch] mybranch -> mybranch
|
||||
```
|
||||
|
||||
### Updating submodules
|
||||
|
||||
The repository uses submodules for external library dependencies in
|
||||
third_party. Each submodule points to a particular external commit of the
|
||||
external repository by the hash code of that external commit. Just like
|
||||
regular source code files, this hash code is part of the current branch and
|
||||
jpeg xl commit you have checked out.
|
||||
|
||||
When changing branches or when doing `git rebase`, git will unfortunately
|
||||
*not* automatically set those hashes to the ones of the branch or jpeg xl
|
||||
commit you changed to nor set the source files of the third_party submodules
|
||||
to the new state. That is, even though git will have updated the jpeg xl
|
||||
source code files on your disk to the new ones, it will leave the submodule
|
||||
hashes and the files in third_party in your workspace to the ones they were
|
||||
before you changed branches. This will show up in a git diff because this
|
||||
is seen as a change compared to the branch you switched to. The git diff shows
|
||||
the difference in hash codes (as if you are changing to the old ones), it does
|
||||
not show changes in files inside the third_party directory.
|
||||
|
||||
This mismatch can cause at least two problems:
|
||||
|
||||
*) the jpeg xl codebase may not compile due to third_party library version
|
||||
mismatch if e.g. API changed or a submodule was added/removed.
|
||||
|
||||
*) when using `commit -a` your commit, which may be a technical change
|
||||
unrelated to submodule changes, will unintentionally contain a change to the
|
||||
submodules hash code, which is undesired unless you actually want to change
|
||||
the version of third_party libraries.
|
||||
|
||||
To resolve this, the submodules must be updated manually with
|
||||
the following command after those actions (at least when the submodules
|
||||
changed):
|
||||
|
||||
```
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
Here, the init flag ensures new modules get added when encessary and the
|
||||
recursive flag is required for the submodules depending on other submodules.
|
||||
|
||||
If you checkout a different branch, you can spot that submodules changed
|
||||
when it shows a message similar to this:
|
||||
|
||||
```
|
||||
M third_party/brotli
|
||||
M third_party/lcms
|
||||
```
|
||||
|
||||
If you do a rebase you may end up in a harder to solve situation, where
|
||||
`git submodule update --init --recursive` itself fails with errors such as:
|
||||
|
||||
```
|
||||
Unable to checkout '35ef5c554d888bef217d449346067de05e269b30' in submodule path 'third_party/brotli'
|
||||
```
|
||||
|
||||
In that case, you can use the force flag:
|
||||
|
||||
```
|
||||
git submodule update --init --recursive --force
|
||||
```
|
||||
|
||||
### Iterating changes in your merge request
|
||||
|
||||
To address reviewer changes you need to amend the local changes in your branch
|
||||
first. Make the changes you need in your commit locally by running `git commit
|
||||
--amend file1 file2 file3 ...` or `git commit --amend -a` to amend all the
|
||||
changes from all the staged files.
|
||||
|
||||
Once you have the new version of the "mybranch" branch to re-upload, you need to
|
||||
force push it to the same branch in your fork. Since you are pushing a different
|
||||
version of the same commit (as opposed to another commit on top of the existing
|
||||
ones), you need to force the operation to replace the old version.
|
||||
|
||||
```bash
|
||||
git push origin mybranch --force
|
||||
```
|
||||
|
||||
The merge request should now be updated with the new changes.
|
||||
|
||||
### Merging your changes
|
||||
|
||||
We use "rebase" as a merge policy, which means that there a no "merge" commits
|
||||
(commits with more than one parent) but instead only a linear history of
|
||||
changes.
|
||||
|
||||
It is possible that other changes where added to the main branch since the last
|
||||
time you rebased your changes. These changes could create a conflict with your
|
||||
Pull Request, if so you need to `git fetch`, `git rebase` and push again your
|
||||
changes which need to go through the continuous integration workflow again to
|
||||
verify that all the tests pass again after including the latest changes.
|
||||
|
||||
### Trying locally a pending Pull Request
|
||||
|
||||
If you want to review in your computer a pending pull request proposed by
|
||||
another user you can fetch the merge request commit with the following command,
|
||||
replacing `NNNN` with the pull request number:
|
||||
|
||||
```bash
|
||||
git fetch origin refs/pull/NNNN/head
|
||||
git checkout FETCH_HEAD
|
||||
```
|
||||
|
||||
The first command will add to your local git repository the remote commit for
|
||||
the pending pull request and store a temporary reference called `FETCH_HEAD`.
|
||||
The second command then checks out that reference. From this point you can
|
||||
review the files in your computer, create a local branch for this FETCH_HEAD or
|
||||
build on top of it.
|
168
third-party/libjxl/libjxl/doc/developing_in_windows_msys.md
vendored
Normal file
168
third-party/libjxl/libjxl/doc/developing_in_windows_msys.md
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
# Developing for Windows with MSYS2
|
||||
|
||||
[MSYS2](https://www.msys2.org/) ("minimal system 2") is a software distribution and a development platform based on MinGW and Cygwin. It provides a Unix-like environment to build code on Windows. These instructions were written with a 64-bit instance of Windows 10 running on a VM. They may also work on native instances of Windows and other versions of Windows.
|
||||
|
||||
## Build Environments
|
||||
|
||||
MSYS2 provides multiple development [environments](https://www.msys2.org/docs/environments/). By convention, they are referred to in uppercase. They target slightly different platforms, runtime libraries, and compiler toolchains. For example, to build for 32-bit Windows, use the MINGW32 environment. For interoperability with Visual Studio projects, use the UCRT64 environment.
|
||||
|
||||
Since all of the build environments are built on top of the MSYS environment, **all updates and package installation must be done from within the MSYS environment**. After making any package changes, `exit` all MSYS2 terminals and restart the desired build-environment. This reminder is repeated multiple times throughout this guide.
|
||||
|
||||
* **MINGW32:** To compile for 32-bit Windows (on 64-bit Windows), use packages from the `mingw32` group. Package names are prefixed with `mingw-w64-i686`. The naming scheme may be different on the 32-bit version of MSYS2.
|
||||
|
||||
* **MINGW64:** This is the primary environment to building for 64-bit Windows. It uses the older MSVCRT runtime, which is widely available across Windows systems. Package names are prefixed with `mingw-w64-x86_64`.
|
||||
|
||||
* **UCRT64:** The Universal C Runtime (UCRT) is used by recent versions of Microsoft Visual Studio. It ships by default with Windows 10. For older versions of Windows, it must be provided with the application or installed by the user. Package names are prefixed with `mingw-w64-ucrt-x86_64`.
|
||||
|
||||
* **CLANG64:** Unfortunately, the `gimp` packages are not available for the CLANG64 environment. However, `libjxl` will otherwise build in this environment if the appropriate packages are installed. Packages are prefixed with `mingw-w64-clang-x86_64`.
|
||||
|
||||
## Install and Upgrade MSYS2
|
||||
|
||||
Download MSYS2 from the homepage. Install at a location without any spaces on a drive with ample free space. After installing the packages used in this guide, MSYS2 used about 15GB of space.
|
||||
|
||||
Toward the end of installation, select the option to run MSYS2 now. A command-line window will open. Run the following command, and answer the prompts to update the repository and close the terminal.
|
||||
|
||||
```bash
|
||||
pacman -Syu
|
||||
```
|
||||
|
||||
Now restart the MSYS environment and run the following command to complete updates:
|
||||
|
||||
```bash
|
||||
pacman -Su
|
||||
```
|
||||
|
||||
## Package Management
|
||||
|
||||
Packages are organized in groups, which share the build environment name, but in lower case. Then they have name prefixes that indicate which group they belong to. Consider this package search: `pacman -Ss cmake`
|
||||
|
||||
```
|
||||
mingw32/mingw-w64-i686-cmake
|
||||
mingw64/mingw-w64-x86_64-cmake
|
||||
ucrt64/mingw-w64-ucrt-x86_64-cmake
|
||||
clang64/mingw-w64-clang-x86_64-cmake
|
||||
msys/cmake
|
||||
```
|
||||
|
||||
We can see the organization `group/prefix-name`. When installing packages, the group name is optional.
|
||||
|
||||
```bash
|
||||
pacman -S mingw-w64-x86_64-cmake
|
||||
```
|
||||
|
||||
For tools that need to be aware of the compiler to function, install the package that corresponds with the specific build-environment you plan to use. For `cmake`, install the `mingw64` version. The generic `msys/cmake` will not function correctly because it will not find the compiler. For other tools, the generic `msys` version is adequate, like `msys/git`.
|
||||
|
||||
To remove packages, use:
|
||||
|
||||
```bash
|
||||
pacman -Rsc [package-name]
|
||||
```
|
||||
|
||||
## Worst-Case Scenario...
|
||||
|
||||
If packages management is done within a build environment other than MSYS, the environment structure will be disrupted and compilation will likely fail. If this happens, it may be necessary to reinstall MSYS2.
|
||||
|
||||
1. Rename the `msys64` folder to `msys64.bak`.
|
||||
|
||||
2. Use the installer to reinstall MSYS2 to `msys64`.
|
||||
|
||||
3. Copy packages from `msys64.bak/var/cache/pacman/pkg/` to the new installation to save download time and bandwidth.
|
||||
|
||||
4. Use `pacman` from within the MSYS environment to install and update packages.
|
||||
|
||||
5. After successfully building a project, it is safe to delete `msys64.bak`
|
||||
|
||||
## The MING64 Environment
|
||||
|
||||
Next set up the MING64 environment. The following commands should be run within the MSYS environment. `pacman -S` is used to install packages. The `--needed` argument prevents packages from being reinstalled.
|
||||
|
||||
```bash
|
||||
pacman -S --needed base-devel mingw-w64-x86_64-toolchain
|
||||
pacman -S git mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja \
|
||||
mingw-w64-x86_64-gtest mingw-w64-x86_64-giflib \
|
||||
mingw-w64-x86_64-libpng mingw-w64-x86_64-libjpeg-turbo
|
||||
```
|
||||
|
||||
## Build `libjxl`
|
||||
|
||||
Download the source from the libjxl [releases](https://github.com/libjxl/libjxl/releases) page. Alternatively, you may obtain the latest development version with `git`. Run `./deps.sh` to ensure additional third-party dependencies are downloaded.
|
||||
|
||||
Start the MINGW64 environment, create a build directory within the source directory, and configure with `cmake`.
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=OFF \
|
||||
-DJPEGXL_ENABLE_BENCHMARK=OFF -DJPEGXL_ENABLE_PLUGINS=ON \
|
||||
-DJPEGXL_ENABLE_MANPAGES=OFF -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
-DJPEGXL_FORCE_SYSTEM_GTEST=ON ..
|
||||
```
|
||||
|
||||
Check the output to see if any dependencies were missed and need to be installed. Adding `-G Ninja` may be helpful, but on my computer, Ninja was selected by default. Remember that package changes must be done from the MSYS environment. Then exit all MSYS2 terminals and restart the build environment.
|
||||
|
||||
If all went well, you may now run `cmake` to build `libjxl`:
|
||||
|
||||
```bash
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
Do not be alarmed by the compiler warnings. They are a caused by differences between gcc/g++ and clang. The build should complete successfully. Then `cjxl`, `djxl`, `jxlinfo`, and others can be run from within the build environment. Moving them into the native Windows environment requires resolving `dll` issues that are beyond the scope of this document.
|
||||
|
||||
## The `clang` Compiler
|
||||
|
||||
To use the `clang` compiler, install the packages that correspond with the environment you wish to use. Remember to make package changes from within the MSYS environment.
|
||||
|
||||
```
|
||||
mingw-w64-i686-clang
|
||||
mingw-w64-i686-clang-tools-extra
|
||||
mingw-w64-i686-clang-compiler-rt
|
||||
|
||||
mingw-w64-x86_64-clang
|
||||
mingw-w64-x86_64-clang-tools-extra
|
||||
mingw-w64-x86_64-clang-compiler-rt
|
||||
|
||||
mingw-w64-ucrt64-x86_64-clang
|
||||
mingw-w64-ucrt64-x86_64-clang-tools-extra
|
||||
mingw-w64-ucrt64-x86_64-clang-compiler-rt
|
||||
```
|
||||
|
||||
After the `clang` compiler is installed, 'libjxl' can be built with the `./ci.sh` script.
|
||||
|
||||
```bash
|
||||
./ci.sh opt -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=OFF \
|
||||
-DJPEGXL_ENABLE_BENCHMARK=OFF -DJPEGXL_ENABLE_MANPAGES=OFF \
|
||||
-DJPEGXL_FORCE_SYSTEM_BROTLI=ON -DJPEGXL_FORCE_SYSTEM_GTEST=ON
|
||||
```
|
||||
|
||||
On my computer, `doxygen` packages needed to be installed to proceed with building. Use `pacman -Ss doxygen` to find the packages to install.
|
||||
|
||||
## The GIMP Plugin
|
||||
|
||||
To build the GIMP plugin, install the relevant `gimp` package. This will also install dependencies. Again, perform package management tasks from only the MSYS environment. Then restart the build environment.
|
||||
|
||||
```bash
|
||||
pacman -S mingw-w64-i686-gimp
|
||||
pacman -S mingw-w64-x86_64-gimp
|
||||
pacman -S mingw-w64-ucrt-x86_64-gimp
|
||||
```
|
||||
|
||||
If `clang` is installed, you can use the `./ci.sh` script to build. Otherwise, navigate to the build directory to reconfigure and build with `cmake`.
|
||||
|
||||
```bash
|
||||
cd build
|
||||
rm -r CM*
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=OFF \
|
||||
-DJPEGXL_ENABLE_BENCHMARK=OFF -DJPEGXL_ENABLE_MANPAGES=OFF \
|
||||
-DJPEGXL_ENABLE_PLUGINS=ON -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
-DJPEGXL_FORCE_SYSTEM_GTEST=ON ..
|
||||
```
|
||||
|
||||
The plugin is built statically, so there should be no need to install `dll` files. To try out the plugin:
|
||||
|
||||
1. [Download](https://www.gimp.org/downloads/) and install the stable version of GIMP (currently 2.10.24).
|
||||
|
||||
2. Create a new folder: `C:\Program Files\GIMP 2\lib\gimp\2.0\plug-ins\file-jxl`
|
||||
|
||||
3. Copy `build/plugins/gimp/file-jxl.exe` to the new folder.
|
90
third-party/libjxl/libjxl/doc/developing_in_windows_vcpkg.md
vendored
Normal file
90
third-party/libjxl/libjxl/doc/developing_in_windows_vcpkg.md
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
# Developing on Windows with Visual Studio 2019
|
||||
|
||||
These instructions assume an up-to-date Windows 10 (e.g. build 19041.928) with
|
||||
**Microsoft Visual Studio 2019** (e.g. Version 16.9.0 Preview 4.0) installed. If
|
||||
unavailable, please use another build environment:
|
||||
|
||||
* [MSYS2 on Windows](developing_in_windows_msys.md)
|
||||
* [Crossroad on Linux](developing_with_crossroad.md) (cross compilation for Windows)
|
||||
|
||||
## Minimum build dependencies
|
||||
|
||||
Apart from the dependencies in third_party, some of the tools use external
|
||||
dependencies that need to be installed in your system first.
|
||||
|
||||
Please install [vcpkg](https://vcpkg.readthedocs.io/en/latest/examples/installing-and-using-packages/)
|
||||
(tested with version 2019.07.18), and use it to install the following libraries:
|
||||
|
||||
```
|
||||
vcpkg install gtest:x64-windows
|
||||
vcpkg install giflib:x64-windows
|
||||
vcpkg install libjpeg-turbo:x64-windows
|
||||
vcpkg install libpng:x64-windows
|
||||
vcpkg install zlib:x64-windows
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
From Visual Studio, open the CMakeLists.txt in the JPEG XL root directory.
|
||||
Right-click the CMakeLists.txt entry in the Folder View of the Solution
|
||||
Explorer. In the context menu, select CMake Settings. Click on the green plus
|
||||
to add an x64-Clang configuration and the red minus to remove any non-Clang
|
||||
configuration (the MSVC compiler is currently not supported). Click on the blue
|
||||
hyperlink marked "CMakeSettings.json" and an editor will open. Insert the
|
||||
following text after replacing $VCPKG with the directory where you installed
|
||||
vcpkg above.
|
||||
|
||||
```
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Clang-Release",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "MinSizeRel",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "-DCMAKE_TOOLCHAIN_FILE=$VCPKG/scripts/buildsystems/vcpkg.cmake",
|
||||
"buildCommandArgs": "-v",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "clang_cl_x64" ],
|
||||
"variables": [
|
||||
{
|
||||
"name": "VCPKG_TARGET_TRIPLET",
|
||||
"value": "x64-windows",
|
||||
"type": "STRING"
|
||||
},
|
||||
{
|
||||
"name": "JPEGXL_ENABLE_TCMALLOC",
|
||||
"value": "False",
|
||||
"type": "BOOL"
|
||||
},
|
||||
{
|
||||
"name": "BUILD_GMOCK",
|
||||
"value": "True",
|
||||
"type": "BOOL"
|
||||
},
|
||||
{
|
||||
"name": "gtest_force_shared_crt",
|
||||
"value": "True",
|
||||
"type": "BOOL"
|
||||
},
|
||||
{
|
||||
"name": "JPEGXL_ENABLE_FUZZERS",
|
||||
"value": "False",
|
||||
"type": "BOOL"
|
||||
},
|
||||
{
|
||||
"name": "JPEGXL_ENABLE_VIEWERS",
|
||||
"value": "False",
|
||||
"type": "BOOL"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The project is now ready for use. To build, simply press F7 (or choose
|
||||
Build All from the Build menu). This writes binaries to
|
||||
`out/build/x64-Clang-Release/tools`. The main [README.md](../README.md) explains
|
||||
how to use the encoder/decoder and benchmark binaries.
|
116
third-party/libjxl/libjxl/doc/developing_with_crossroad.md
vendored
Normal file
116
third-party/libjxl/libjxl/doc/developing_with_crossroad.md
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
# Cross Compiling for Windows with Crossroad
|
||||
|
||||
[Crossroad](https://pypi.org/project/crossroad/) is a tool to set up cross-compilation environments on GNU/Linux distributions. These instructions assume a Debian/Ubuntu system. However, they can likely be adapted to other Linux environments. Since Ubuntu can be run on Windows through WSL, these instruction may be useful for developing directly on Windows.
|
||||
|
||||
## Install Crossroad
|
||||
|
||||
Crossroad requires tools included with `python3-docutils` and `mingw-w64`. They may be installed using:
|
||||
|
||||
```bash
|
||||
sudo aptitude install python3-docutils mingw-w64
|
||||
```
|
||||
|
||||
The `zstandard` python package is also required, but is not available in the repositories. It may be installed using `pip`.
|
||||
|
||||
```bash
|
||||
pip3 install zstandard
|
||||
```
|
||||
|
||||
After the dependencies are installed, crossroad itself maybe installed with `pip`.
|
||||
|
||||
```bash
|
||||
pip3 install crossroad
|
||||
```
|
||||
|
||||
If there are errors while running crossroad, it may need to be downloaded and installed directly using `setup.py`. Instructions are on the crossroad homepage.
|
||||
|
||||
## Update Debian Alternatives
|
||||
|
||||
Since `libjxl` uses C++ features that require posix threads, the symlinks used by the Debian alternative system need to be updated:
|
||||
|
||||
```bash
|
||||
sudo update-alternatives --config x86_64-w64-mingw32-g++
|
||||
```
|
||||
|
||||
Select the option that indicates `posix` usage. Repeat for `gcc` and `i686`:
|
||||
|
||||
```bash
|
||||
sudo update-alternatives --config x86_64-w64-mingw32-gcc
|
||||
sudo update-alternatives --config i686-w64-mingw32-gcc
|
||||
sudo update-alternatives --config i686-w64-mingw32-g++
|
||||
```
|
||||
|
||||
## Create a New Crossroad Project
|
||||
|
||||
Crossroad supports the following platforms:
|
||||
|
||||
```
|
||||
native Native platform (x86_64 GNU/Linux)
|
||||
android-x86 Generic Android/Bionic on x86
|
||||
android-mips64 Generic Android/Bionic on MIPS64
|
||||
android-x86-64 Generic Android/Bionic on x86-64
|
||||
w64 Windows 64-bit
|
||||
w32 Windows 32-bit
|
||||
android-arm64 Generic Android/Bionic on ARM64
|
||||
android-mips Generic Android/Bionic on MIPS
|
||||
android-arm Generic Android/Bionic on ARM
|
||||
```
|
||||
|
||||
To begin cross compiling for Windows, a new project needs to be created:
|
||||
|
||||
```bash
|
||||
crossroad w64 [project-name]
|
||||
```
|
||||
|
||||
## Install Dependencies
|
||||
|
||||
Since the `gimp` development package is required to build the GIMP plugin and also includes most of the packages required by `libjxl`, install it first.
|
||||
|
||||
```bash
|
||||
crossroad install gimp
|
||||
```
|
||||
|
||||
`gtest` and `brotli` are also required.
|
||||
|
||||
```bash
|
||||
crossroad install gtest brotli
|
||||
```
|
||||
|
||||
If any packages are later found to be missing, you may search for them using:
|
||||
|
||||
```bash
|
||||
crossroad search [...]
|
||||
```
|
||||
|
||||
## Build `libjxl`
|
||||
|
||||
Download the source from the libjxl [releases](https://github.com/libjxl/libjxl/releases) page. Alternatively, you may obtain the latest development version with `git`. Run `./deps.sh` to ensure additional third-party dependencies are downloaded. Unfortunately, the script `./ci.sh` does not work with Crossroad, so `cmake` will need to be called directly.
|
||||
|
||||
Create a build directory within the source directory. If you haven't already, start your crossroad project and run `cmake`:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
crossroad w64 libjxl
|
||||
crossroad cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=OFF \
|
||||
-DJPEGXL_ENABLE_BENCHMARK=OFF -DJPEGXL_ENABLE_MANPAGES=OFF \
|
||||
-DJPEGXL_ENABLE_PLUGINS=ON -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
-DJPEGXL_FORCE_SYSTEM_GTEST=ON ..
|
||||
```
|
||||
|
||||
Check the output to see if any dependencies were missed and need to be installed. If all went well, you may now run `cmake` to build `libjxl`:
|
||||
|
||||
```bash
|
||||
cmake --build .
|
||||
```
|
||||
|
||||
## Try out the GIMP Plugin
|
||||
|
||||
The plugin is built statically, so there should be no need to install `dll` files. To try out the plugin:
|
||||
|
||||
1. [Download](https://www.gimp.org/downloads/) and install the stable version of GIMP (currently 2.10.24).
|
||||
|
||||
2. Create a new folder: `C:\Program Files\GIMP 2\lib\gimp\2.0\plug-ins\file-jxl`
|
||||
|
||||
3. Copy `build/plugins/gimp/file-jxl.exe` to the new folder.
|
32
third-party/libjxl/libjxl/doc/encode_effort.md
vendored
Normal file
32
third-party/libjxl/libjxl/doc/encode_effort.md
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# Encode effort settings
|
||||
|
||||
Various trade-offs between encode speed and compression performance can be selected in libjxl. In `cjxl`, this is done via the `--effort` (`-e`) option.
|
||||
Higher effort means slower encoding; generally the higher the effort, the more coding tools are used, computationally more expensive heuristics are used,
|
||||
and more exhaustive search is performed.
|
||||
Generally efforts range between `1` and `9`, but there is also `e10` you pass the flag `--allow_expert_options` (in combination with "lossless", i.e. `-d 0`). It is considered an expert option because it can be extremely slow.
|
||||
|
||||
|
||||
For lossy compression, higher effort results in better visual quality at a given filesize, and also better
|
||||
encoder consistency, i.e. less image-dependent variation in the actual visual quality that is achieved. This means that for lossy compression,
|
||||
higher effort does not necessarily mean smaller filesizes for every image — some images may be somewhat lower quality than desired when using
|
||||
lower effort heuristics, and to improve consistency, higher effort heuristics may decide to use more bytes for them.
|
||||
|
||||
For lossless compression, higher effort should result in smaller filesizes, although this is not guaranteed;
|
||||
in particular, e2 can be better than e3 for non-photographic images, and e3 can be better than e4 for photographic images.
|
||||
|
||||
The following table describes what the various effort settings do:
|
||||
|
||||
|Effort | Modular (lossless) | VarDCT (lossy) |
|
||||
|-------|--------------------|----------------|
|
||||
| e1 | fast-lossless, fixed YCoCg RCT, fixed ClampedGradient predictor, simple palette detection, no MA tree (one context for everything), Huffman, simple rle-only lz77 | |
|
||||
| e2 | global channel palette, fixed MA tree (context based on Gradient-error), ANS, otherwise same as e1 | |
|
||||
| e3 | same as e2 but fixed Weighted predictor and fixed MA tree with context based on WP-error | only 8x8, basically XYB jpeg with ANS |
|
||||
| e4 | try both ClampedGradient and Weighted predictor, learned MA tree, global palette | simple variable blocks heuristics, adaptive quantization, coefficient reordering |
|
||||
| e5 | e4 + patches, local palette / local channel palette, different local RCTs | e4 + gabor-like transform, chroma from luma |
|
||||
| e6 | e5 + more RCTs and MA tree properties | e5 + error diffusion, full variable blocks heuristics |
|
||||
| e7 | e6 + more RCTs and MA tree properties | e6 + patches (including dots) |
|
||||
| e8 | e7 + more RCTs, MA tree properties and Weighted predictor parameters | e7 + Butteraugli iterations for adaptive quantization |
|
||||
| e9 | e8 + more RCTs, MA tree properties and Weighted predictor parameters, try all predictors | e8 + more Butteraugli iterations |
|
||||
| e10 | e9 + previous-channel MA tree properties, different group dimensions, exhaustively try various e9 options | |
|
||||
|
||||
For the entropy coding (context clustering, lz77 search, hybriduint configuration): slower/more exhaustive search as effort goes up.
|
284
third-party/libjxl/libjxl/doc/format_overview.md
vendored
Normal file
284
third-party/libjxl/libjxl/doc/format_overview.md
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
# JPEG XL Format Overview
|
||||
|
||||
This document gives an overview of the JPEG XL file format and codestream,
|
||||
its features, and the underlying design rationale.
|
||||
The aim of this document is to provide general insight into the
|
||||
format capabilities and design, thus helping developers
|
||||
better understand how to use the `libjxl` API.
|
||||
|
||||
## Codestream and File Format
|
||||
|
||||
The JPEG XL format is defined in ISO/IEC 18181. This standard consists of
|
||||
four parts:
|
||||
|
||||
* 18181-1: Core codestream
|
||||
* 18181-2: File format
|
||||
* 18181-3: Conformance testing
|
||||
* 18181-4: Reference implementation
|
||||
|
||||
### Core codestream
|
||||
|
||||
The core codestream contains all the data necessary to decode and display
|
||||
still image or animation data. This includes basic metadata like image dimensions,
|
||||
the pixel data itself, colorspace information, orientation, upsampling, etc.
|
||||
|
||||
### File format
|
||||
|
||||
The JPEG XL file format can take two forms:
|
||||
|
||||
* A 'naked' codestream. In this case, only the image/animation data itself is
|
||||
stored, and no additional metadata can be included. Such a file starts with the
|
||||
bytes `0xFF0A` (the JPEG marker for "start of JPEG XL codestream").
|
||||
* An ISOBMFF-based container. This is a box-based container that includes a
|
||||
JPEG XL codestream box (`jxlc`), and can optionally include other boxes with
|
||||
additional information, such as Exif metadata. In this case, the file starts with
|
||||
the bytes `0x0000000C 4A584C20 0D0A870A`.
|
||||
|
||||
### Conformance testing
|
||||
|
||||
This part of the standard defines precision bounds and test cases for conforming
|
||||
decoders, to verify that they implement all coding tools correctly and accurately.
|
||||
|
||||
### Reference implementation
|
||||
|
||||
The `libjxl` software is the reference implementation of JPEG XL.
|
||||
|
||||
|
||||
## Metadata versus Image Data
|
||||
|
||||
JPEG XL makes a clear separation between metadata and image data.
|
||||
Everything that is needed to correctly display an image is
|
||||
considered to be image data, and is part of the core codestream. This includes
|
||||
elements that have traditionally been considered 'metadata', such as ICC profiles
|
||||
and Exif orientation. The goal is to reduce the ambiguity and potential for
|
||||
incorrect implementations that can be caused by having a 'black box' codestream
|
||||
that only contains numerical pixel data, requiring applications to figure out how
|
||||
to correctly interpret the data (i.e. apply color transforms, upsampling,
|
||||
orientation, blending, cropping, etc.). By including this functionality in the
|
||||
codestream itself, the decoder can provide output in a normalized way
|
||||
(e.g. in RGBA, orientation already applied, frames blended and coalesced),
|
||||
simplifying things and making it less error-prone for applications.
|
||||
|
||||
The remaining metadata, e.g. Exif or XMP, can be stored in the container format,
|
||||
but it does not influence image rendering. In the case of Exif orientation,
|
||||
this field has to be ignored by applications, since the orientation in the
|
||||
codestream always takes precedence (and will already have been applied
|
||||
transparently by the decoder). This means that stripping metadata can be done
|
||||
without affecting the displayed image.
|
||||
|
||||
|
||||
## Codestream Features
|
||||
|
||||
### Color Management
|
||||
|
||||
In JPEG XL, images always have a fully defined colorspace, i.e. it is always
|
||||
unambiguous how to interpret the pixel values. There are two options:
|
||||
|
||||
* Pixel data is in a specified (non-XYB) colorspace, and the decoder will produce
|
||||
a pixel buffer in this colorspace plus an ICC profile that describes that
|
||||
colorspace. Mathematically lossless encoding can only use this option.
|
||||
* Pixel data is in the XYB colorspace, which is an absolute colorspace.
|
||||
In this case, the decoder can produce a pixel buffer directly in a desired
|
||||
display space like sRGB, Display-P3 or Rec.2100 PQ.
|
||||
|
||||
The image header always contains a colorspace; however, its meaning depends on
|
||||
which of the above two options were used:
|
||||
|
||||
* In the first case (non-XYB), the signaled colorspace defines the
|
||||
interpretation of the pixel data.
|
||||
* In the second case (XYB), the signaled colorspace is merely a _suggestion_
|
||||
of a target colorspace to represent the image in, i.e. it is the colorspace
|
||||
the original image was in, that has a sufficiently wide gamut and a
|
||||
suitable transfer curve to represent the image data with high fidelity
|
||||
using a limited bit depth representation.
|
||||
|
||||
Colorspaces can be signaled in two ways in JPEG XL:
|
||||
|
||||
* CICP-style Enum values: This is a very compact representation that
|
||||
covers most or all of the common colorspaces. The decoder can convert
|
||||
XYB to any of these colorspaces without requiring an external color management
|
||||
library.
|
||||
* ICC profiles: Arbitrary ICC profiles can also be used, including
|
||||
CMYK ones. The ICC profile data gets compressed. In this case, external
|
||||
color management software (e.g. lcms2 or skcms) has to be used for color
|
||||
conversions.
|
||||
|
||||
### Frames
|
||||
|
||||
A JPEG XL codestream contains one or more frames. In the case of animation,
|
||||
these frames have a duration and can be looped (infinitely or a number of times).
|
||||
Zero-duration frames are possible and represent different layers of the image.
|
||||
|
||||
Frames can have a blendmode (Replace, Add, Alpha-blend, Multiply, etc.) and
|
||||
they can use any previous frame as a base.
|
||||
They can be smaller than the image canvas, in which case the pixels outside the
|
||||
crop are copied from the base frame. They can be positioned at an arbitrary
|
||||
offset from the image canvas; this offset can also be negative and frames can
|
||||
also be larger than the image canvas, in which case parts of the frame will
|
||||
be invisible and only the intersection with the image canvas will be shown.
|
||||
|
||||
By default, the decoder will blend and coalesce frames, producing only a single
|
||||
output frame when there are subsequent zero-duration frames, and all output frames
|
||||
are of the same size (the size of the image canvas) and have either no duration
|
||||
(in case of a still image) or a non-zero duration (in case of animation).
|
||||
|
||||
### Pixel Data
|
||||
|
||||
Every frame contains pixel data encoded in one of two modes:
|
||||
|
||||
* VarDCT mode: In this mode, variable-sized DCT transforms are applied
|
||||
and the image data is encoded in the form of DCT coefficients. This mode is
|
||||
always lossy, but it can also be used to losslessly represent an existing
|
||||
(already lossy) JPEG image, in which case only the DCT8x8 is used.
|
||||
* Modular mode: In this mode, only integer arithmetic is used, which
|
||||
enables lossless compression. However, this mode can also be used for lossy
|
||||
compression. Multiple transformations can be used to improve compression or to
|
||||
obtain other desirable effects: reversible color transforms (RCTs),
|
||||
(delta) palette transforms, and a modified non-linear Haar transform
|
||||
called Squeeze, which facilitates (but does not require) lossy compression
|
||||
and enables progressive decoding.
|
||||
|
||||
Internally, the VarDCT mode uses Modular sub-bitstreams to encode
|
||||
various auxiliary images, such as the "LF image" (a 1:8 downscaled version
|
||||
of the image that contains the DC coefficients of DCT8x8 and low-frequency
|
||||
coefficients of the larger DCT transforms), extra channels besides the
|
||||
three color channels (e.g. alpha), and weights for adaptive quantization.
|
||||
|
||||
In addition, both modes can separately encode additional 'image features' that
|
||||
are rendered on top of the decoded image:
|
||||
|
||||
* Patches: rectangles from a previously decoded frame (which can be a
|
||||
'hidden' frame that is not displayed but only stored to be referenced later)
|
||||
can be blended using one of the blendmodes on top of the current frame.
|
||||
This allows the encoder to identify repeating patterns (such as letters of
|
||||
text) and encode them only once, using patches to insert the pattern in
|
||||
multiple spots. These patterns are encoded in a previous frame, making
|
||||
it possible to add Modular-encoded pixels to a VarDCT-encoded frame or
|
||||
vice versa.
|
||||
* Splines: centripetal Catmull-Rom splines can be encoded, with a color
|
||||
and a thickness that can vary along the arclength of the curve.
|
||||
Although the current encoder does not use this bitstream feature yet, we
|
||||
anticipate that it can be useful to complement DCT-encoded data, since
|
||||
thin lines are hard to represent faithfully using the DCT.
|
||||
* Noise: luma-modulated synthetic noise can be added to an image, e.g.
|
||||
to emulate photon noise, in a way that avoids poor compression due to
|
||||
high frequency DCT coefficients.
|
||||
|
||||
Finally, both modes can also optionally apply two filtering methods to
|
||||
the decoded image, which both have the goal of reducing block artifacts
|
||||
and ringing:
|
||||
|
||||
* Gabor-like transform ('Gaborish'): a small (3x3) blur that gets
|
||||
applied across block and group boundaries, reducing blockiness. The
|
||||
encoder applies the inverse sharpening transform before encoding,
|
||||
effectively getting the benefits of lapped transforms without the
|
||||
disadvantages.
|
||||
* Edge-preserving filter ('EPF'): similar to a bilateral filter,
|
||||
this smoothing filter avoids blurring edges while reducing ringing.
|
||||
The strength of this filter is signaled and can locally be adapted.
|
||||
|
||||
### Groups
|
||||
|
||||
In both modes (Modular and VarDCT), the frame data is signaled as
|
||||
a sequence of groups. These groups can be decoded independently,
|
||||
and the frame header contains a table of contents (TOC) with bitstream
|
||||
offsets for the start of each group. This enables parallel decoding,
|
||||
and also partial decoding of a region of interest or a progressive preview.
|
||||
|
||||
In VarDCT mode, all groups have dimensions 256x256 (or smaller at the
|
||||
right and bottom borders). First the LF image is encoded, also in
|
||||
256x256 groups (corresponding to 2048x2048 pixels, since this data
|
||||
corresponds to the 1:8 image). This means there is always a basic
|
||||
progressive preview available in VarDCT mode.
|
||||
Optionally, the LF image can be encoded separately in a (hidden)
|
||||
LF frame, which can itself recursively be encoded in VarDCT mode
|
||||
and have its own LF frame. This makes it possible to represent huge
|
||||
images while still having an overall preview that can be efficiently
|
||||
decoded.
|
||||
Then the HF groups are encoded, corresponding to the remaining AC
|
||||
coefficients. The HF groups can be encoded in multiple passes for
|
||||
more progressive refinement steps; the coefficients of all passes
|
||||
are added. Unlike JPEG progressive scan scripts, JPEG XL allows
|
||||
signaling any amount of detail in any part of the image in any pass.
|
||||
|
||||
In Modular mode, groups can have dimensions 128x128, 256x256, 512x512
|
||||
or 1024x1024. If the Squeeze transform was used, the data will
|
||||
be split in three parts: the Global groups (the top of the Laplacian
|
||||
pyramid that fits in a single group), the LF groups (the middle part
|
||||
of the Laplacian pyramid that corresponds to the data needed to
|
||||
reconstruct the 1:8 image) and the HF groups (the base of the Laplacian
|
||||
pyramid), where the HF groups are again possibly encoded in multiple
|
||||
passes (up to three: one for the 1:4 image, one for the 1:2 image,
|
||||
and one for the 1:1 image).
|
||||
|
||||
In case of a VarDCT image with extra channels (e.g. alpha), the
|
||||
VarDCT groups and the Modular groups are interleaved in order to
|
||||
allow progressive previews of all the channels.
|
||||
|
||||
The default group order is to encode the LF and HF groups in
|
||||
scanline order (top to bottom, left to right), but this order
|
||||
can be permuted arbitrarily. This allows, for example, a center-first
|
||||
ordering or a saliency-based ordering, causing the bitstream
|
||||
to prioritize progressive refinements in a different way.
|
||||
|
||||
|
||||
## File Format Features
|
||||
|
||||
Besides the image data itself (stored in the `jxlc` codestream box),
|
||||
the optional container format allows storing additional information.
|
||||
|
||||
## Metadata
|
||||
|
||||
Three types of metadata can be included in a JPEG XL container:
|
||||
|
||||
* Exif (`Exif`)
|
||||
* XMP (`xml `)
|
||||
* JUMBF (`jumb`)
|
||||
|
||||
This metadata can contain information about the image, such as copyright
|
||||
notices, GPS coordinates, camera settings, etc.
|
||||
If it contains rendering-impacting information (such as Exif orientation),
|
||||
the information in the codestream takes precedence.
|
||||
|
||||
## Compressed Metadata
|
||||
|
||||
The container allows the above metadata to be stored either uncompressed
|
||||
(e.g. plaintext XML in the case of XMP) or by Brotli-compression.
|
||||
In the latter case, the box type is `brob` (Brotli-compressed Box) and
|
||||
the first four bytes of the box contents define the actual box type
|
||||
(e.g. `xml `) it represents.
|
||||
|
||||
## JPEG Bitstream Reconstruction Data
|
||||
|
||||
JPEG XL can losslessly recompress existing JPEG files.
|
||||
The general design philosophy still applies in this case:
|
||||
all the image data is stored in the codestream box, including the DCT
|
||||
coefficients of the original JPEG image and possibly an ICC profile or
|
||||
Exif orientation.
|
||||
|
||||
In order to allow bit-identical reconstruction of the original JPEG file
|
||||
(not just the image but the actual file), additional information is needed,
|
||||
since the same image data can be encoded in multiple ways as a JPEG file.
|
||||
The `jbrd` box (JPEG Bitstream Reconstruction Data) contains this information.
|
||||
Typically it is relatively small. Using the image data from the codestream,
|
||||
the JPEG bitstream reconstruction data, and possibly other metadata boxes
|
||||
that were present in the JPEG file (Exif/XMP/JUMBF), the exact original
|
||||
JPEG file can be reconstructed.
|
||||
|
||||
This box is not needed to display a recompressed JPEG image; it is only
|
||||
needed to reconstruct the original JPEG file.
|
||||
|
||||
## Frame Index
|
||||
|
||||
The container can optionally store a `jxli` box, which contains an index
|
||||
of offsets to keyframes of a JPEG XL animation. It is not needed to display
|
||||
the animation, but it does facilitate efficient seeking.
|
||||
|
||||
## Partial Codestream
|
||||
|
||||
The codestream can optionally be split into multiple `jxlp` boxes;
|
||||
conceptually, this is equivalent to a single `jxlc` box that contains the
|
||||
concatenation of all partial codestream boxes.
|
||||
This makes it possible to create a file that starts with
|
||||
the data needed for a progressive preview of the image, followed by
|
||||
metadata, followed by the remaining image data.
|
184
third-party/libjxl/libjxl/doc/fuzzing.md
vendored
Normal file
184
third-party/libjxl/libjxl/doc/fuzzing.md
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
# Fuzzing
|
||||
|
||||
Fuzzing is a technique to find potential bugs by providing randomly generated
|
||||
invalid inputs. To detect potential bugs such as programming errors we use
|
||||
fuzzing in combination with ASan (Address Sanitizer), MSan (Memory Sanitizer),
|
||||
UBSan (Undefined Behavior Sanitizer) and asserts in the code. An invalid input
|
||||
will likely produce a decoding error (some API function returning error), which
|
||||
is absolutely not a problem, but what it should not do is access memory out of
|
||||
bounds, use uninitialized memory or hit a false assert condition.
|
||||
|
||||
## Automated Fuzzing with oss-fuzz
|
||||
|
||||
libjxl fuzzing is integrated into [oss-fuzz](https://github.com/google/oss-fuzz)
|
||||
as the project `libjxl`. oss-fuzz regularly runs the fuzzers on the `main`
|
||||
branch and reports bugs into their bug tracker which remains private until the
|
||||
bugs are fixed in main.
|
||||
|
||||
## Fuzzer targets
|
||||
|
||||
There are several fuzzer executable targets defined in the `tools/` directory
|
||||
to fuzz different parts of the code. The main one is `djxl_fuzzer`, which uses
|
||||
the public C decoder API to attempt to decode an image. The fuzzer input is not
|
||||
directly the .jxl file, the last few bytes of the fuzzer input are used to
|
||||
decide *how* will the API be used (if preview is requested, the pixel format
|
||||
requested, if the .jxl input data is provided altogether, etc) and the rest of
|
||||
the fuzzer input is provided as the .jxl file to the decoder. Some bugs might
|
||||
reproduce only if the .jxl input is decoded in certain way.
|
||||
|
||||
The remaining fuzzer targets execute a specific portion the codec that might be
|
||||
easier to fuzz independently from the whole codec.
|
||||
|
||||
## Reproducing fuzzer bugs
|
||||
|
||||
A fuzzer target, like `djxl_fuzzer` accepts as a parameter one or more files
|
||||
that will be used as inputs. This runs the fuzzer program in test-only mode
|
||||
where no new inputs are generated and only the provided files are tested. This
|
||||
is the easiest way to reproduce a bug found by the fuzzer using the generated
|
||||
test case from the bug report.
|
||||
|
||||
oss-fuzz uses a specific compiler version and flags, and it is built using
|
||||
Docker. Different compiler versions will have different support for detecting
|
||||
certain actions as errors, so we want to reproduce the build from oss-fuzz as
|
||||
close as possible. To reproduce the build as generated by oss-fuzz there are a
|
||||
few helper commands in `ci.sh` as explained below.
|
||||
|
||||
### Generate the gcr.io/oss-fuzz/libjxl image
|
||||
|
||||
First you need the ossfuzz libjxl builder image. This is the base oss-fuzz
|
||||
builder image with a few dependencies installed. To generate it you need to
|
||||
check out the oss-fuzz project and build it:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/google/oss-fuzz.git ~/oss-fuzz
|
||||
cd ~/oss-fuzz
|
||||
sudo infra/helper.py build_image libjxl
|
||||
```
|
||||
|
||||
This will create the `gcr.io/oss-fuzz/libjxl` docker image. You can check if it
|
||||
was created verifying that it is listed in the output of the `sudo docker image
|
||||
ls` command.
|
||||
|
||||
### Build the fuzzer targets with oss-fuzz
|
||||
|
||||
To build the fuzzer targets from the current libjxl source checkout, use the
|
||||
`./ci.sh ossfuzz_msan` command for MSan, `./ci.sh ossfuzz_asan` command for ASan
|
||||
or `./ci.sh ossfuzz_ubsan` command for UBSan. All the `JXL_ASSERT` and
|
||||
`JXL_DASSERT` calls are enabled in all the three modes. These ci.sh helpers will
|
||||
reproduce the oss-fuzz docker call to build libjxl mounting the current source
|
||||
directory into the Docker container. Ideally you will run this command in a
|
||||
different build directory separated from your regular builds.
|
||||
|
||||
For example, for MSan builds run:
|
||||
|
||||
```bash
|
||||
BUILD_DIR=build-fuzzmsan ./ci.sh ossfuzz_msan
|
||||
```
|
||||
|
||||
After this, the fuzzer program will be generated in the build directory like
|
||||
for other build modes: `build-fuzzmsan/tools/djxl_fuzzer`.
|
||||
|
||||
### Iterating changes with oss-fuzz builds
|
||||
|
||||
After modifying the source code to fix the fuzzer-found bug, or to include more
|
||||
debug information, you can rebuild only a specific fuzzer target to save on
|
||||
rebuilding time and immediately run the test case again. For example, for
|
||||
rebuilding and testing only `djxl_fuzzer` in MSan mode we can run:
|
||||
|
||||
```bash
|
||||
BUILD_DIR=build-fuzzmsan ./ci.sh ossfuzz_msan djxl_fuzzer && build-fuzzmsan/tools/djxl_fuzzer path/to/testcase.bin
|
||||
```
|
||||
|
||||
When MSan and ASan fuzzers fail they will print a stack trace at the point where
|
||||
the error occurred, and some related information. To make these these stack
|
||||
traces useful we need to convert the addresses to function names and source file
|
||||
names and lines, which is done with the "symbolizer". For UBSan to print a stack
|
||||
trace we need to set the `UBSAN_OPTIONS` environment variables when running the
|
||||
fuzzer.
|
||||
|
||||
Set the following environment variables when testing the fuzzer binaries. Here
|
||||
`clang` should match the compiler version used by the container, you can pass a
|
||||
different compiler version in the following example by first installing the
|
||||
clang package for that version outside the container and using `clang-NN`
|
||||
(for example `clang-11`) instead of `clang` in the following commands:
|
||||
|
||||
```bash
|
||||
symbolizer=$($(realpath $(which clang)) -print-prog-name=llvm-symbolizer)
|
||||
export MSAN_SYMBOLIZER_PATH="${symbolizer}"
|
||||
export UBSAN_SYMBOLIZER_PATH="${symbolizer}"
|
||||
export ASAN_SYMBOLIZER_PATH="${symbolizer}"
|
||||
export ASAN_OPTIONS=detect_leaks=1
|
||||
export UBSAN_OPTIONS=print_stacktrace=1
|
||||
```
|
||||
|
||||
Note: The symbolizer binary must be a program called `llvm-symbolizer`, any
|
||||
other file name will fail. There are normally symlinks already installed with
|
||||
the right name which the `-print-prog-name` would print.
|
||||
|
||||
## Running the fuzzers locally
|
||||
|
||||
Running the fuzzer targets in fuzzing mode can be achieved by running them with
|
||||
no parameters, or better with a parameter with the path to a *directory*
|
||||
containing a seed of files to use as a starting point. Note that passing a
|
||||
directory is considered a corpus to use for fuzzing while passing a file is
|
||||
considered an input to evaluate. Multi-process fuzzing is also supported. For
|
||||
details about all the fuzzing options run:
|
||||
|
||||
```bash
|
||||
build-fuzzmsan/tools/djxl_fuzzer -help=1
|
||||
```
|
||||
|
||||
## Writing fuzzer-friendly code
|
||||
|
||||
Fuzzing on itself can't find programming bugs unless an input makes the program
|
||||
perform an invalid operation (read/write out of bounds, perform an undefined
|
||||
behavior operation, etc). You can help the fuzzer find invalid situations by
|
||||
adding asserts:
|
||||
|
||||
* `JXL_ASSERT()` is enabled in Release mode by default. It can be disabled
|
||||
with `-DJXL_ENABLE_ASSERT=0` but the intention is that it will run for all
|
||||
the users in released code. If performance of the check is not an issue (like
|
||||
checks done once per image, once per channel, once per group, etc) a
|
||||
JXL_ASSERT is appropriate. A failed assert is preferable to an out of bounds
|
||||
write.
|
||||
|
||||
* `JXL_DASSERT()` is only enabled in Debug builds, which includes all the ASan,
|
||||
MSan and UBSan builds. Performance of these checks is not an issue if kept
|
||||
within reasonable limits (automated msan/asan test should finish withing 1
|
||||
hour for example). Fuzzing is more effective when the given input runs
|
||||
faster, so keep that in mind when adding a complex DASSERT that runs multiple
|
||||
times per output pixel.
|
||||
|
||||
* For MSan builds it is also possible to specify that certain values must be
|
||||
initialized. This is automatic for values that are used to make decisions
|
||||
(like when used in an `if` statement or in the ternary operator condition)
|
||||
but those checks can be made explicit for image data using the
|
||||
`JXL_CHECK_IMAGE_INITIALIZED(image, rect)` macro. This helps document and
|
||||
check (only in MSan builds) that a given portion of the image is expected to
|
||||
be initialized, allowing to catch errors earlier in the process.
|
||||
|
||||
## Dealing with use-of-uninitialized memory
|
||||
|
||||
In MSan builds it is considered an error to *use* uninitialized memory. Using
|
||||
the memory normally requires something like a decision / branch based on the
|
||||
uninitialized value, just running `memcpy()` or simple arithmetic over
|
||||
uninitialized memory is not a problem. Notably, computing `DemoteTo()`,
|
||||
`NearestInt()` or similar expressions that create a branch based on the value of
|
||||
the uninitialized memory will trigger an MSan error.
|
||||
|
||||
In libjxl we often run vectorized operations over a series of values, rounding
|
||||
up to the next multiple of a vector size, thus operating over uninitialized
|
||||
values past the end of the requested region. These values are part of the image
|
||||
padding but are not initialized. This behavior would not create an MSan error
|
||||
unless the processing includes operations like `NearestInt()`. For such cases
|
||||
the preferred solution is to use `msan::UnpoisonMemory` over the portion of
|
||||
memory of the last SIMD vector before processing, and then running
|
||||
`msan::PoisonMemory` over the corresponding value in the output side. A note
|
||||
including why this is safe to do must be added, for example if the processing
|
||||
doesn't involve any cross-lane computation.
|
||||
|
||||
Initializing padding memory in MSan builds is discouraged because it may hide
|
||||
bugs in functions that weren't supposed to read from the padding. Initializing
|
||||
padding memory in all builds, including Release builds, would mitigate the
|
||||
MSan potential security issue but it would hide the logic bug for a longer time
|
||||
and potentially incur in a performance hit.
|
1
third-party/libjxl/libjxl/doc/jxl.svg
vendored
Normal file
1
third-party/libjxl/libjxl/doc/jxl.svg
vendored
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="891.6" height="836.9" viewBox="0 0 891.6 836.9" overflow="visible"><style>.st0{fill:#5fb4b1}</style><path class="st0" d="M495.7 420.6C533 348.3 570.2 276 607.4 203.7H503.8c-24 46.6-48.1 93.3-72.1 139.9-38.8-46.6-77.6-93.3-116.4-139.9H211.7L392 420.6c-36.5 70.8-73 141.7-109.5 212.5h103.6c23.3-45.2 46.6-90.4 69.8-135.5 37.6 45.2 75.1 90.4 112.7 135.5h103.6c-58.7-70.8-117.6-141.6-176.5-212.5zM153 625.6l.3 2.3.7 2.6c3.8 15.1 8.9 59.5-12 86.3-6.2 8-14.8 14.5-25.6 19.3L53.9 836.9c36.9 0 69.4-5.8 96.5-17.4 25.9-11 47.2-27.2 63.2-48.1 22.2-28.9 33.9-66.6 33.8-109.1 0-24.8-4-44.6-5.7-52L200.8 337h.1v-90.2H0V337h109.8L153 625.6zM738.5 211.2l-.3-2.3-.7-2.6c-3.8-15.1-8.9-59.5 12-86.3 6.2-8 14.8-14.5 25.6-19.3L837.6 0c-36.9 0-69.4 5.8-96.5 17.4-25.9 11-47.2 27.2-63.2 48.1-22.2 28.9-33.9 66.6-33.8 109.1 0 24.8 4 44.6 5.7 52l40.9 273.3h-.1v90.2h200.9v-90.2H781.7l-43.2-288.7z"/><path class="st0" d="M153 625.6l.3 2.3.7 2.6c3.8 15.1 8.9 59.5-12 86.3-6.2 8-14.8 14.5-25.6 19.3L53.9 836.9c36.9 0 69.4-5.8 96.5-17.4 25.9-11 47.2-27.2 63.2-48.1 22.2-28.9 33.9-66.6 33.8-109.1 0-24.8-4-44.6-5.7-52L200.8 337h.1v-90.2H0V337h109.8L153 625.6z"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
102
third-party/libjxl/libjxl/doc/man/cjxl.txt
vendored
Normal file
102
third-party/libjxl/libjxl/doc/man/cjxl.txt
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
cjxl(1)
|
||||
=======
|
||||
:doctype: manpage
|
||||
|
||||
Name
|
||||
----
|
||||
|
||||
cjxl - compress images to JPEG XL
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
*cjxl* ['options'...] 'input' ['output.jxl']
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
`cjxl` compresses an image or animation to the JPEG XL format. It is intended to
|
||||
spare users the trouble of determining a set of optimal parameters for each
|
||||
individual image. Instead, for a given target quality, it should provide
|
||||
consistent visual results across various kinds of images. The defaults have been
|
||||
chosen to be sensible, so that the following commands should give satisfactory
|
||||
results in most cases:
|
||||
|
||||
----
|
||||
cjxl input.png output.jxl
|
||||
cjxl input.jpg output.jxl
|
||||
cjxl input.gif output.jxl
|
||||
----
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
-h::
|
||||
--help::
|
||||
Displays the options that `cjxl` supports. On its own, it will only show
|
||||
basic options. It can be combined with `-v` or `-v -v` to show increasingly
|
||||
advanced options as well.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Increases verbosity. Can be repeated to increase it further, and also
|
||||
applies to `--help`.
|
||||
|
||||
-d 'distance'::
|
||||
--distance='distance'::
|
||||
The preferred way to specify quality. It is specified in multiples of a
|
||||
just-noticeable difference. That is, `-d 0` is mathematically lossless,
|
||||
`-d 1` should be visually lossless, and higher distances yield denser and
|
||||
denser files with lower and lower fidelity. Lossy sources such as JPEG and
|
||||
GIF files are compressed losslessly by default, and in the case of JPEG
|
||||
files specifically, the original JPEG can then be reconstructed bit-for-bit.
|
||||
For lossless sources, `-d 1` is the default.
|
||||
|
||||
-q 'quality'::
|
||||
--quality='quality'::
|
||||
Alternative way to indicate the desired quality. 100 is lossless and lower
|
||||
values yield smaller files. There is no lower bound to this quality
|
||||
parameter, but positive values should approximately match the quality
|
||||
setting of libjpeg.
|
||||
|
||||
-e 'effort'::
|
||||
--effort='effort'::
|
||||
Controls the amount of effort that goes into producing an ``optimal'' file
|
||||
in terms of quality/size. That is to say, all other parameters being equal,
|
||||
a higher effort should yield a file that is at least as dense and possibly
|
||||
denser, and with at least as high and possibly higher quality.
|
||||
+
|
||||
Recognized effort settings, from fastest to slowest, are:
|
||||
+
|
||||
- 1 or ``lightning''
|
||||
- 2 or ``thunder''
|
||||
- 3 or ``falcon''
|
||||
- 4 or ``cheetah''
|
||||
- 5 or ``hare''
|
||||
- 6 or ``wombat''
|
||||
- 7 or ``squirrel'' (default)
|
||||
- 8 or ``kitten''
|
||||
- 9 or ``tortoise''
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
----
|
||||
# Compress a PNG file to a high-quality JPEG XL version.
|
||||
$ cjxl input.png output.jxl
|
||||
|
||||
# Compress it at a slightly lower quality, appropriate for web use.
|
||||
$ cjxl -d 2 input.png output.jxl
|
||||
|
||||
# Compress it losslessly. These are equivalent.
|
||||
$ cjxl -d 0 input.png lossless.jxl
|
||||
$ cjxl -q 100 input.png lossless.jxl
|
||||
|
||||
# Compress a JPEG file losslessly.
|
||||
$ cjxl input.jpeg lossless-jpeg.jxl
|
||||
----
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
*djxl*(1)
|
61
third-party/libjxl/libjxl/doc/man/djxl.txt
vendored
Normal file
61
third-party/libjxl/libjxl/doc/man/djxl.txt
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
djxl(1)
|
||||
=======
|
||||
:doctype: manpage
|
||||
|
||||
Name
|
||||
----
|
||||
|
||||
djxl - decompress JPEG XL images
|
||||
|
||||
Synopsis
|
||||
--------
|
||||
|
||||
*djxl* ['options'...] 'input.jxl' ['output']
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
`djxl` decompresses a JPEG XL image or animation. The output format is determined
|
||||
by the extension of the output file, which can be `.png`, `.jpg`, `.ppm`, `.pfm`.
|
||||
If the JPEG XL input file contains an animation, multiple output files will be
|
||||
produced, with names of the form "'output'-*framenumber*.ext".
|
||||
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
-h::
|
||||
--help::
|
||||
Displays the options that `djxl` supports.
|
||||
|
||||
-j::
|
||||
--pixels_to_jpeg::
|
||||
By default, if the input JPEG XL contains a recompressed JPEG file,
|
||||
djxl reconstructs the exact original JPEG file if the output file has the
|
||||
`.jpg` (or `.jpeg`) filename extension.
|
||||
This flag causes the decoder to instead decode the image to pixels and
|
||||
encode a new (lossy) JPEG in this case.
|
||||
|
||||
|
||||
-q 'quality'::
|
||||
--jpeg_quality='quality'::
|
||||
When decoding to `.jpg`, use this output quality. This option implicitly
|
||||
enables the --pixels_to_jpeg option.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
----
|
||||
# Decompress a JPEG XL file to PNG
|
||||
$ djxl input.jxl output.png
|
||||
|
||||
# Reconstruct a losslessly-recompressed JPEG file
|
||||
$ djxl lossless-jpeg.jxl reconstructed.jpeg
|
||||
----
|
||||
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
*cjxl*(1)
|
314
third-party/libjxl/libjxl/doc/release.md
vendored
Normal file
314
third-party/libjxl/libjxl/doc/release.md
vendored
Normal file
@ -0,0 +1,314 @@
|
||||
# libjxl release process
|
||||
|
||||
This guide documents the release process for the libjxl project.
|
||||
|
||||
libjxl follows the [semantic versioning](https://semver.org/spec/v2.0.0.html)
|
||||
specification for released versions. Releases are distributed as tags in the git
|
||||
repository with the semantic version prefixed by the letter "v". For example,
|
||||
release version "0.3.7" will have a git tag "v0.3.7".
|
||||
|
||||
The public API is explicitly defined as C headers in the `lib/include`
|
||||
directory, normally installed in your include path. All other headers are
|
||||
internal API and are not covered by the versioning rules.
|
||||
|
||||
## Development and release workflow
|
||||
|
||||
New code development is performed on the `main` branch of the git repository.
|
||||
Pre-submit checks enforce minimum build and test requirements for new patches
|
||||
that balance impact and test latency, but not all checks are performed before
|
||||
pull requests are merged. Several slower checks only run *after* the code has
|
||||
been merged to `main`, resulting in some errors being detected hours after the
|
||||
code is merged or even days after in the case of fuzzer-detected bugs.
|
||||
|
||||
Release tags are cut from *release branches*. Each MAJOR.MINOR version has its
|
||||
own release branch, for example releases `0.7.0`, `0.7.1`, `0.7.2`, ... would
|
||||
have tags `v0.7.0`, `v0.7.1`, `v0.7.2`, ... on commits from the `v0.7.x` branch.
|
||||
`v0.7.x` is a branch name, not a tag name, and doesn't represent a released
|
||||
version since semantic versioning requires that the PATCH is a non-negative
|
||||
number. Released tags don't each one have their own release branch, all releases
|
||||
from the same MAJOR.MINOR version will share the same branch. The first commit
|
||||
after the branch-off points between the main branch and the release branch
|
||||
should be tagged with the suffix `-snapshot` and the name of the next
|
||||
MAJOR.MINOR version, in order to get meaningful output for `git describe`.
|
||||
|
||||
The main purpose of the release branch is to stabilize the code before a
|
||||
release. This involves including fixes to existing bugs but **not** including
|
||||
new features. New features often come with new bugs which take time to fix, so
|
||||
having a release branch allows us to cherry-pick *bug fixes* from the `main`
|
||||
branch into the release branch without including the new *features* from `main`.
|
||||
For this reason it is important to make small commits in `main` and separate bug
|
||||
fixes from new features.
|
||||
|
||||
After the initial minor release (`MAJOR.MINOR.PATCH`, for example `0.5.0`) the
|
||||
release branch is used to continue to cherry-pick fixes to be included in a
|
||||
patch release, for example a version `0.5.1` release. Patch fixes are only meant
|
||||
to fix security bugs or other critical bugs that can't wait until the next major
|
||||
or minor release.
|
||||
|
||||
Release branches *may* continue to be maintained even after the next minor or
|
||||
major version has been released to support users that can't update to a newer
|
||||
minor release. In that case, the same process applies to all the maintained
|
||||
release branches.
|
||||
|
||||
A release branch with specific cherry-picks from `main` means that the release
|
||||
code is actually a version of the code that never existed in the `main` branch,
|
||||
so it needs to be tested independently. Pre-submit and post-submit tests run on
|
||||
release branches (branches matching `v*.*.x`) but extra manual checks should be
|
||||
performed before a release, specially if multiple bug fixes interact with each
|
||||
other. Take this into account when selecting which commits to include in a
|
||||
release. The objective is to have a stable version that can be used without
|
||||
problems for months. Having the latest improvements at the time the release tag
|
||||
is created is a non-goal.
|
||||
|
||||
## Creating a release branch
|
||||
|
||||
A new release branch is needed before creating a new major or minor release,
|
||||
that is, a new release where the MAJOR or MINOR numbers are increased. Patch
|
||||
releases, where only the PATCH number is increased, reuse the branch from the
|
||||
previous release of the same MAJOR and MINOR numbers.
|
||||
|
||||
The following instructions assume that you followed the recommended [libjxl git
|
||||
setup](developing_in_github.md) where `origin` points to the upstream
|
||||
libjxl/libjxl project, otherwise use the name of your upstream remote repository
|
||||
instead of `origin`.
|
||||
|
||||
The release branch is normally created from the latest work in `main` at the
|
||||
time the branch is created, but it is possible to create the branch from an
|
||||
older commit if the current `main` is particularly unstable or includes commits
|
||||
that were not intended to be included in the release. The following example
|
||||
creates the branch `v0.5.x` from the latest commit in main (`origin/main`), if a
|
||||
different commit is to be used then replace `origin/main` with the SHA of that
|
||||
commit. Change the `v0.5.x` branch name to the one you are creating.
|
||||
|
||||
```bash
|
||||
git fetch origin main
|
||||
git push git@github.com:libjxl/libjxl.git origin/main:refs/heads/v0.5.x
|
||||
```
|
||||
|
||||
Here we use the SSH URL explicitly since you are pushing to the `libjxl/libjxl`
|
||||
project directly to a branch there. If you followed the guide `origin` will have
|
||||
the HTTPS URL which wouldn't normally let you push since you wouldn't be
|
||||
authenticated. The `v*.*.x` branches are [GitHub protected
|
||||
branches](https://docs.github.com/en/github/administering-a-repository/defining-the-mergeability-of-pull-requests/about-protected-branches)
|
||||
in our repository, however you can push to a protected branch when *creating* it
|
||||
but you can't directly push to it after it is created. To include more changes
|
||||
in the release branch see the "Cherry-picking fixes to a release" section below.
|
||||
|
||||
## Creating a merge label
|
||||
|
||||
We use GitHub labels in Pull Requests to keep track of the changes that should
|
||||
be merged into a given release branch. For this purpose create a new label for
|
||||
each new MAJOR.MINOR release branch called `merge-MAJOR.MINOR`, for example,
|
||||
`merge-0.5`.
|
||||
|
||||
In the [edit labels](https://github.com/libjxl/libjxl/issues/labels) page, click
|
||||
on "New label" and create the label. Pick your favorite color.
|
||||
|
||||
Labels are a GitHub-only concept and are not represented in git. You can add the
|
||||
label to a Pull Request even after it was merged, whenever it is decided that
|
||||
the Pull Request should be included in the given release branch. Adding the
|
||||
label doesn't automatically merge it to the release branch.
|
||||
|
||||
## Update the versioning number
|
||||
|
||||
The version number (as returned by `JxlDecoderVersion`) in the source code in
|
||||
`main` must match the semantic versioning of a release. After the release
|
||||
branch is created the code in `main` will only be included in the next major
|
||||
or minor release. Right after a release branch update the version targeting the
|
||||
next release. Artifacts from `main` should include the new (unreleased) version,
|
||||
so it is important to update it. For example, after the `v0.5.x` branch is
|
||||
created from main, you should update the version on `main` to `0.6.0`.
|
||||
|
||||
To help update it, run this helper command (in a Debian-based system):
|
||||
|
||||
```bash
|
||||
./ci.sh bump_version 0.6.0
|
||||
```
|
||||
|
||||
This will update the version in the following files:
|
||||
|
||||
* `lib/CMakeLists.txt`
|
||||
* `lib/lib.gni`, automatically updated with
|
||||
`tools/scripts/build_cleaner.py --update`.
|
||||
* `debian/changelog` to create the Debian package release with the new version.
|
||||
Debian changelog shouldn't repeat the library changelog, instead it should
|
||||
include changes to the packaging scripts.
|
||||
* `.github/workflows/conformance.yml`
|
||||
|
||||
If there were incompatible API/ABI changes, make sure to also adapt the
|
||||
corresponding section in
|
||||
[CMakeLists.txt](https://github.com/libjxl/libjxl/blob/main/lib/CMakeLists.txt#L12).
|
||||
|
||||
## Cherry-pick fixes to a release
|
||||
|
||||
After a Pull Request that should be included in a release branch has been merged
|
||||
to `main` it can be cherry-picked to the release branch. Before cherry-picking a
|
||||
change to a release branch it is important to check that it doesn't introduce
|
||||
more problems, in particular it should run for some time in `main` to make sure
|
||||
post-submit tests and the fuzzers run on it. Waiting for a day is a good idea.
|
||||
|
||||
Most of the testing is done on the `main` branch, so be careful with what
|
||||
commits are cherry-picked to a branch. Refactoring code is often not a good
|
||||
candidate to cherry-pick.
|
||||
|
||||
To cherry-pick a single commit to a release branch (in this example to `v0.5.x`)
|
||||
you can run:
|
||||
|
||||
```bash
|
||||
git fetch origin
|
||||
git checkout origin/v0.5.x -b merge_to_release
|
||||
git cherry-pick -x SHA_OF_MAIN_COMMIT
|
||||
# -x will annotate the cherry-pick with the original SHA_OF_MAIN_COMMIT value.
|
||||
# If not already mentioned in the original commit, add the original PR number to
|
||||
# the commit, for example add "(cherry picked from PR #NNNN)".
|
||||
git commit --amend
|
||||
```
|
||||
|
||||
The `SHA_OF_MAIN_COMMIT` is the hash of the commit as it landed in main. Use
|
||||
`git log origin/main` to list the recent main commits and their hashes.
|
||||
|
||||
Making sure that the commit message on the cherry-picked commit contains a
|
||||
reference to the original pull request (like `#NNNN`) is important. It creates
|
||||
an automatic comment in the original pull request notifying that it was
|
||||
mentioned in another commit, helping keep track of the merged pull requests. If
|
||||
the original commit was merged with the "Squash and merge" policy it will
|
||||
automatically contain the pull request number on the first line, if this is not
|
||||
the case you can amend the commit message of the cherry-pick to include a
|
||||
reference.
|
||||
|
||||
Multiple commits can be cherry-picked and tested at once to save time. Continue
|
||||
running `git cherry-pick` and `git commit --amend` multiple times for all the
|
||||
commits you need to cherry-pick, ideally in the same order they were merged on
|
||||
the `main` branch. At the end you will have a local branch with multiple commits
|
||||
on top of the release branch.
|
||||
|
||||
To update the version number, for example from v0.8.0 to v0.8.1 run this helper
|
||||
command (in a Debian-based system):
|
||||
|
||||
```bash
|
||||
./ci.sh bump_version 0.8.1
|
||||
```
|
||||
|
||||
as described above and commit the changes.
|
||||
|
||||
Finally, upload your changes to *your fork* like normal, except that when
|
||||
creating a pull request select the desired release branch as a target:
|
||||
|
||||
```bash
|
||||
git push myfork merge_to_release
|
||||
```
|
||||
|
||||
If you used the [guide](developing_in_github.md) `myfork` would be `origin` in
|
||||
that example. Click on the URL displayed, which will be something like
|
||||
|
||||
`https://github.com/mygithubusername/libjxl/pull/new/merge_to_release`
|
||||
|
||||
In the "Open a pull request" page, change the drop-down base branch from
|
||||
"base: main" (the default) to the release branch you are targeting.
|
||||
|
||||
The pull request approval and pre-submit rules apply as with normal pull
|
||||
requests to the `main` branch.
|
||||
|
||||
**Important:** When merging multiple cherry-picks use "Rebase and merge" policy,
|
||||
not the squash one since otherwise you would discard the individual commit
|
||||
message references from the git history in the release branch.
|
||||
|
||||
## Publishing a release
|
||||
|
||||
Once a release tag is created it must not be modified, so you need to prepare
|
||||
the changes before creating the release. Make sure you checked the following:
|
||||
|
||||
* The semantic version number in the release branch (see `lib/CMakeLists.txt`)
|
||||
matches the number you intend to release, all three MAJOR, MINOR and PATCH
|
||||
should match. Otherwise send a pull request to the release branch to
|
||||
update them.
|
||||
|
||||
* The GitHub Actions checks pass on the release branch. Look for the green
|
||||
tick next to the last commit on the release branch. This should be visible
|
||||
on the branch page, for example: https://github.com/libjxl/libjxl/tree/v0.5.x
|
||||
|
||||
* There no open fuzzer-found bugs for the release branch. The most effective
|
||||
way is to [run the fuzzer](fuzzing.md) on the release branch for a while. You
|
||||
can seed the fuzzer with corpus generated by oss-fuzz by [downloading
|
||||
it](https://google.github.io/oss-fuzz/advanced-topics/corpora/#downloading-the-corpus),
|
||||
for example `djxl_fuzzer` with libFuzzer will use:
|
||||
gs://libjxl-corpus.clusterfuzz-external.appspot.com/libFuzzer/libjxl_djxl_fuzzer
|
||||
|
||||
* Manually check that images encode/decode ok.
|
||||
|
||||
* Manually check that downstream projects compile with our code. Sometimes
|
||||
bugs on build scripts are only detected when other projects try to use our
|
||||
library. For example, test compiling
|
||||
[imagemagick](https://github.com/ImageMagick/ImageMagick) and Chrome.
|
||||
|
||||
A [GitHub
|
||||
"release"](https://docs.github.com/en/github/administering-a-repository/releasing-projects-on-github/about-releases)
|
||||
consists of two different concepts:
|
||||
|
||||
* a git "tag": this is a name (`v` plus the semantic version number) with a
|
||||
commit hash associated, defined in the git repository. Most external projects
|
||||
will use git tags or HTTP URLs to these tags to fetch the code.
|
||||
|
||||
* a GitHub "release": this is a GitHub-only concept and is not represented in
|
||||
git other than by having a git tag associated with the release. A GitHub
|
||||
release has a given source code commit SHA associated (through the tag) but
|
||||
it *also* contains release notes and optional binary files attached to the
|
||||
release.
|
||||
|
||||
Releases from the older GitLab repository only have a git tag in GitHub, while
|
||||
newer releases have both a git tag and a release entry in GitHub.
|
||||
|
||||
To publish a release open the [New Release
|
||||
page](https://github.com/libjxl/libjxl/releases/new) and follow these
|
||||
instructions:
|
||||
|
||||
* Set the "Tag version" as "v" plus the semantic version number.
|
||||
|
||||
* Select the "Target" as your release branch. For example for a "v0.7.1"
|
||||
release tag you should use the "v0.7.x" branch.
|
||||
|
||||
* Use the version number as the release title.
|
||||
|
||||
* Copy-paste the relevant section of the [CHANGELOG.md](../CHANGELOG.md) to the
|
||||
release notes into the release notes. Add any other information pertaining
|
||||
the release itself that are not included in the CHANGELOG.md, although prefer
|
||||
to include those in the CHANGELOG.md file. You can switch to the Preview tab
|
||||
to see the results.
|
||||
|
||||
* Finally click "Publish release" and go celebrate with the team. 🎉
|
||||
|
||||
* Make sure to manually push the commit of the release also to https://gitlab.com/wg1/jpeg-xl.
|
||||
|
||||
### How to build downstream projects
|
||||
|
||||
```bash
|
||||
docker run -it debian:bullseye /bin/bash
|
||||
|
||||
apt update
|
||||
apt install -y clang cmake git libbrotli-dev nasm pkg-config ninja-build
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
|
||||
git clone --recurse-submodules --depth 1 -b v0.7.x \
|
||||
https://github.com/libjxl/libjxl.git
|
||||
git clone --recurse-submodules --depth 1 \
|
||||
https://github.com/ImageMagick/ImageMagick.git
|
||||
git clone --recurse-submodules --depth 1 \
|
||||
https://github.com/FFmpeg/FFmpeg.git
|
||||
|
||||
cd ~/libjxl
|
||||
git checkout v0.7.x
|
||||
cmake -B build -G Ninja .
|
||||
cmake --build build
|
||||
cmake --install build
|
||||
|
||||
cd ~/ImageMagick
|
||||
./configure --with-jxl=yes
|
||||
# check for "JPEG XL --with-jxl=yes yes"
|
||||
make -j 80
|
||||
|
||||
cd ~/FFmpeg
|
||||
./configure --enable-libjxl
|
||||
# check for libjxl decoder/encoder support
|
||||
make -j 80
|
||||
```
|
75
third-party/libjxl/libjxl/doc/software_support.md
vendored
Normal file
75
third-party/libjxl/libjxl/doc/software_support.md
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
# JPEG XL software support
|
||||
|
||||
This document attempts to keep track of software that is using libjxl to support JPEG XL.
|
||||
This list serves several purposes:
|
||||
|
||||
- thank/acknowledge other projects for integrating jxl support
|
||||
- point end-users to software that can read/write jxl
|
||||
- keep track of the adoption status of jxl
|
||||
- in case of a (security) bug in libjxl, it's easier to see who might be affected and check if they are updated (in case they use static linking)
|
||||
|
||||
Please add missing software to this list.
|
||||
|
||||
## Browsers
|
||||
|
||||
- Chromium: behind a flag from version 91 to 109, [tracking bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1178058)
|
||||
- Firefox: behind a flag since version 90, [tracking bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1539075)
|
||||
- Safari: supported since version 17 beta [release notes](https://developer.apple.com/documentation/safari-release-notes/safari-17-release-notes), [tracking bug](https://bugs.webkit.org/show_bug.cgi?id=208235)
|
||||
- Edge: behind a flag since version 91, start with `.\msedge.exe --enable-features=JXL`
|
||||
- Opera: behind a flag since version 77.
|
||||
- Basilisk: supported since version v2023.01.07, [release notes](https://www.basilisk-browser.org/releasenotes.shtml)
|
||||
- Pale Moon: supported since version 31.4.0, [release notes](https://www.palemoon.org/releasenotes-archived.shtml#v31.4.0)
|
||||
- Waterfox: [enabled by default](https://github.com/WaterfoxCo/Waterfox/pull/2936)
|
||||
|
||||
For all browsers and to track browsers progress see [Can I Use](https://caniuse.com/jpegxl).
|
||||
|
||||
## Image libraries
|
||||
|
||||
- [ImageMagick](https://imagemagick.org/): supported since 7.0.10-54
|
||||
- [libvips](https://libvips.github.io/libvips/): supported since 8.11
|
||||
- [Imlib2](https://github.com/alistair7/imlib2-jxl)
|
||||
- [FFmpeg](https://github.com/FFmpeg/FFmpeg/search?q=jpeg-xl&type=commits)
|
||||
- [GDAL](https://gdal.org/drivers/raster/jpegxl.html): supported since 3.4.0 as a TIFF codec, and 3.6.0 as standalone format
|
||||
- [GraphicsMagick](http://www.graphicsmagick.org/NEWS.html#march-26-2022): supported since 1.3.38
|
||||
|
||||
## OS-level support / UI frameworks / file browser plugins
|
||||
|
||||
- Qt / KDE: [plugin available](https://github.com/novomesk/qt-jpegxl-image-plugin)
|
||||
- GDK-pixbuf: plugin available in libjxl repo
|
||||
- [gThumb](https://ubuntuhandbook.org/index.php/2021/04/gthumb-3-11-3-adds-jpeg-xl-support/)
|
||||
- [MacOS viewer/QuickLook plugin](https://github.com/yllan/JXLook)
|
||||
- [Windows Imaging Component](https://github.com/mirillis/jpegxl-wic)
|
||||
- [Windows thumbnail handler](https://github.com/saschanaz/jxl-winthumb)
|
||||
- [OpenMandriva Lx (since 4.3 RC)](https://www.openmandriva.org/en/news/article/openmandriva-lx-4-3-rc-available-for-testing)
|
||||
- [KaOS (since 2021.06)](https://news.itsfoss.com/kaos-2021-06-release/)
|
||||
- [EFL (since 1.27, no external plugin needed)](https://www.enlightenment.org)
|
||||
|
||||
## Image editors
|
||||
|
||||
- [Adobe Camera Raw (since version 15)](https://helpx.adobe.com/camera-raw/using/hdr-output.html)
|
||||
- [Affinity (since V2)](https://affinity.serif.com/en-gb/whats-new/)
|
||||
- [darktable (since 4.2)](https://github.com/darktable-org/darktable/releases/tag/release-4.2.0)
|
||||
- [GIMP (since 2.99.8)](https://www.gimp.org/news/2021/10/20/gimp-2-99-8-released/); plugin for older versions available in libjxl repo
|
||||
- [Graphic Converter (since 11.5)](https://www.lemkesoft.de/en/products/graphicconverter/)
|
||||
- [Krita](https://invent.kde.org/graphics/krita/-/commit/13e5d2e5b9f0eac5c8064b7767f0b62264a0797b)
|
||||
- [Paint.NET](https://www.getpaint.net/index.html); supported since 4.3.12 - requires a [plugin](https://github.com/0xC0000054/pdn-jpegxl) to be downloaded and installed.
|
||||
- Photoshop: no plugin available yet, no official support yet
|
||||
|
||||
## Image viewers
|
||||
|
||||
- [XnView](https://www.xnview.com/en/)
|
||||
- [ImageGlass](https://imageglass.org/)
|
||||
- [IrfanView](https://www.irfanview.com/); supported since 4.59 - requires a [plugin](https://www.irfanview.com/plugins.htm) to be downloaded and enabled.
|
||||
- [Tachiyomi](https://github.com/tachiyomiorg/tachiyomi/releases/tag/v0.12.1)
|
||||
- Any viewer based on Qt, KDE, GDK-pixbuf, EFL, ImageMagick, libvips or imlib2 (see above)
|
||||
- Qt viewers: gwenview, digiKam, KolourPaint, KPhotoAlbum, LXImage-Qt, qimgv, qView, nomacs, VookiImageViewer, PhotoQt
|
||||
- GTK viewers: Eye of Gnome (eog), gThumb, Geeqie
|
||||
- EFL viewers: entice, ephoto
|
||||
- [Swayimg](https://github.com/artemsen/swayimg)
|
||||
|
||||
## Online tools
|
||||
|
||||
- [Squoosh](https://squoosh.app/)
|
||||
- [Cloudinary](https://cloudinary.com/blog/cloudinary_supports_jpeg_xl)
|
||||
- [MConverter](https://mconverter.eu/)
|
||||
- [jpegxl.io](https://jpegxl.io/)
|
15
third-party/libjxl/libjxl/doc/sphinx/api.rst
vendored
Normal file
15
third-party/libjxl/libjxl/doc/sphinx/api.rst
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
API reference
|
||||
=============
|
||||
|
||||
``libjxl`` exposes a C API for encoding and decoding JPEG XL files with some
|
||||
C++ header-only helpers for C++ users.
|
||||
|
||||
.. toctree::
|
||||
:caption: API REFERENCE
|
||||
:maxdepth: 2
|
||||
|
||||
api_decoder
|
||||
api_encoder
|
||||
api_common
|
||||
api_butteraugli
|
||||
api_threads
|
6
third-party/libjxl/libjxl/doc/sphinx/api_butteraugli.rst
vendored
Normal file
6
third-party/libjxl/libjxl/doc/sphinx/api_butteraugli.rst
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
Butteraugli API - ``jxl/butteraugli.h``
|
||||
=======================================
|
||||
|
||||
.. doxygengroup:: libjxl_butteraugli
|
||||
:members:
|
||||
:private-members:
|
6
third-party/libjxl/libjxl/doc/sphinx/api_common.rst
vendored
Normal file
6
third-party/libjxl/libjxl/doc/sphinx/api_common.rst
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
Common API concepts
|
||||
===================
|
||||
|
||||
.. doxygengroup:: libjxl_common
|
||||
:members:
|
||||
:private-members:
|
6
third-party/libjxl/libjxl/doc/sphinx/api_decoder.rst
vendored
Normal file
6
third-party/libjxl/libjxl/doc/sphinx/api_decoder.rst
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
Decoder API - ``jxl/decode.h``
|
||||
==============================
|
||||
|
||||
.. doxygengroup:: libjxl_decoder
|
||||
:members:
|
||||
:private-members:
|
6
third-party/libjxl/libjxl/doc/sphinx/api_encoder.rst
vendored
Normal file
6
third-party/libjxl/libjxl/doc/sphinx/api_encoder.rst
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
Encoder API - ``jxl/encode.h``
|
||||
==============================
|
||||
|
||||
.. doxygengroup:: libjxl_encoder
|
||||
:members:
|
||||
:private-members:
|
6
third-party/libjxl/libjxl/doc/sphinx/api_threads.rst
vendored
Normal file
6
third-party/libjxl/libjxl/doc/sphinx/api_threads.rst
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
Multi-threaded Encoder/Decoder
|
||||
==============================
|
||||
|
||||
.. doxygengroup:: libjxl_threads
|
||||
:members:
|
||||
:private-members:
|
110
third-party/libjxl/libjxl/doc/sphinx/conf.py
vendored
Normal file
110
third-party/libjxl/libjxl/doc/sphinx/conf.py
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# See https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
def GetVersion():
|
||||
"""Function to get the version of the current code."""
|
||||
with open(os.path.join(
|
||||
os.path.dirname(__file__), '../../lib/CMakeLists.txt'), 'r') as f:
|
||||
cmakevars = {}
|
||||
for line in f:
|
||||
m = re.match(r'set\(JPEGXL_([A-Z]+)_VERSION ([^\)]+)\)', line)
|
||||
if m:
|
||||
cmakevars[m.group(1)] = m.group(2)
|
||||
return '%s.%s.%s' % (cmakevars['MAJOR'], cmakevars['MINOR'], cmakevars['PATCH'])
|
||||
|
||||
def ConfigProject(app, config):
|
||||
# Configure the doxygen xml directory as the "xml" directory next to the
|
||||
# sphinx output directory. Doxygen generates by default the xml files in a
|
||||
# "xml" sub-directory of the OUTPUT_DIRECTORY.
|
||||
build_dir = os.path.dirname(app.outdir)
|
||||
xml_dir = os.path.join(build_dir, 'xml')
|
||||
config.breathe_projects['libjxl'] = xml_dir
|
||||
|
||||
# Read the docs build environment doesn't run our cmake script so instead we
|
||||
# need to run doxygen manually here.
|
||||
if os.environ.get('READTHEDOCS', None) != 'True':
|
||||
return
|
||||
root_dir = os.path.realpath(os.path.join(app.srcdir, '../../'))
|
||||
doxyfile = os.path.join(build_dir, 'Doxyfile-rtd.doc')
|
||||
with open(doxyfile, 'w') as f:
|
||||
f.write(f"""
|
||||
FILE_PATTERNS = *.c *.h
|
||||
GENERATE_HTML = NO
|
||||
GENERATE_LATEX = NO
|
||||
GENERATE_XML = YES
|
||||
INPUT = lib/include doc/api.txt
|
||||
OUTPUT_DIRECTORY = {build_dir}
|
||||
PROJECT_NAME = LIBJXL
|
||||
QUIET = YES
|
||||
RECURSIVE = YES
|
||||
STRIP_FROM_PATH = lib/include
|
||||
WARN_AS_ERROR = YES
|
||||
""")
|
||||
subprocess.check_call(['doxygen', doxyfile], cwd=root_dir)
|
||||
|
||||
def setup(app):
|
||||
# Generate doxygen XML on init when running from Read the docs.
|
||||
app.connect("config-inited", ConfigProject)
|
||||
|
||||
### Project information
|
||||
|
||||
project = 'libjxl'
|
||||
project_copyright = 'JPEG XL Project Authors'
|
||||
author = 'JPEG XL Project Authors'
|
||||
version = GetVersion()
|
||||
|
||||
### General configuration
|
||||
|
||||
extensions = [
|
||||
# For integration with doxygen documentation.
|
||||
'breathe',
|
||||
# sphinx readthedocs theme.
|
||||
'sphinx_rtd_theme',
|
||||
# Do we use it?
|
||||
'sphinx.ext.graphviz',
|
||||
]
|
||||
|
||||
breathe_default_project = 'libjxl'
|
||||
breathe_projects = {}
|
||||
|
||||
|
||||
# All the API is in C, except those files that end with cxx.h.
|
||||
breathe_domain_by_extension = {'h': 'cpp'}
|
||||
breathe_domain_by_file_pattern = {
|
||||
'*cxx.h': 'cpp',
|
||||
}
|
||||
breathe_implementation_filename_extensions = ['.cc']
|
||||
|
||||
# These are defined at build time by cmake.
|
||||
c_id_attributes = [
|
||||
'JXL_EXPORT',
|
||||
'JXL_DEPRECATED',
|
||||
'JXL_THREADS_EXPORT',
|
||||
]
|
||||
cpp_id_attributes = c_id_attributes
|
||||
|
||||
|
||||
breathe_projects_source = {
|
||||
'libjxl' : ('../../', [
|
||||
'doc/api.txt',
|
||||
'lib/include/jxl',
|
||||
])
|
||||
}
|
||||
|
||||
# Recognized suffixes.
|
||||
source_suffix = ['.rst', '.md']
|
||||
|
||||
### Options for HTML output
|
||||
|
||||
# Use the readthedocs.io theme when generating the HTML output.
|
||||
html_theme = 'sphinx_rtd_theme'
|
18
third-party/libjxl/libjxl/doc/sphinx/index.rst
vendored
Normal file
18
third-party/libjxl/libjxl/doc/sphinx/index.rst
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
.. libjxl sphinx documentation entrypoint
|
||||
|
||||
JPEG XL image format reference implementation
|
||||
=============================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:caption: Contents:
|
||||
|
||||
api
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
|
3
third-party/libjxl/libjxl/doc/sphinx/requirements.txt
vendored
Normal file
3
third-party/libjxl/libjxl/doc/sphinx/requirements.txt
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
breathe
|
||||
sphinx
|
||||
sphinx-rtd-theme
|
245
third-party/libjxl/libjxl/doc/vuln_playbook.md
vendored
Normal file
245
third-party/libjxl/libjxl/doc/vuln_playbook.md
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
# Security Vulnerabilities Playbook
|
||||
|
||||
## Reporting security bugs
|
||||
|
||||
Report security bugs by emailing libjxl-security@google.com.
|
||||
|
||||
Don't open a GitHub issue, don't discuss it public forums like Discord and don't
|
||||
send a Pull Request if you think you have found a security bug.
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the guidelines followed by the project when handling
|
||||
security bugs, their fixes, disclosure and coordination with security
|
||||
researchers. For more context about this guide, read the [coordinated
|
||||
vulnerability disclosure
|
||||
guidelines](https://github.com/google/oss-vulnerability-guide/blob/main/guide.md)
|
||||
from Google Open Source Programs Office.
|
||||
|
||||
The main target audience of this guide is the coordinator from the libjxl
|
||||
Vulnerability Management Team (VMT) handling the requests, however it is useful
|
||||
for other people to understand what to expect from this process.
|
||||
|
||||
Members of the VMT monitor the reports received by email and will coordinate
|
||||
for these to be addressed. This doesn't mean that said member would fix the bug,
|
||||
but their responsibility is to make sure it is handled properly according to
|
||||
this guide.
|
||||
|
||||
## Life of security bug
|
||||
|
||||
The Coordinator from VMT will make sure that the following steps are taken.
|
||||
|
||||
1. Acknowledge the bug report.
|
||||
|
||||
Our policy mandates a maximum of **3 business days** to respond to bug reports
|
||||
in the given email, but you should respond as soon as possible and keep a fluid
|
||||
communication with the reporter, who has spent some time looking at the issue.
|
||||
|
||||
2. Determine if the bug is a security bug covered by our policy.
|
||||
|
||||
Not all bugs are security bugs, and not all security bugs are covered by this
|
||||
vulnerability disclosure policy. See the [What's a Security bug] section below.
|
||||
|
||||
3. Determine the affected versions.
|
||||
|
||||
Often new bugs on stable projects are found on new features or because of those
|
||||
new features, so only the most recent versions are affected. It is important to
|
||||
determine both what older versions are affected, so users running those older
|
||||
versions can patch or update the software, and also what older versions are
|
||||
*not* affected. It is possible that stable distributions ship older versions
|
||||
that didn't contain the bug and therefore don't need to patch the code. Often
|
||||
maintainers of package distributions need to patch older versions instead of
|
||||
updating due to incompatibilities with newer ones and they need to understand
|
||||
what's the vulnerable code.
|
||||
|
||||
Security bugs that have already been fixed in `main` or in already released code
|
||||
but not disclosed as a vulnerability, for example if fixed as a result of a
|
||||
refactor, should be treated like any other security bug in this policy and
|
||||
disclosed indicating the range of older affected versions (expect for versions
|
||||
before 0.5, see below). In such case a new release would likely not be needed if
|
||||
one already exists, but stable distributions may be still using those version
|
||||
and need to be aware of the issue and fix.
|
||||
|
||||
If no released version is affected by the bug, for example because it was only
|
||||
introduced in the `main` branch but not yet released, then no vulnerability
|
||||
disclosure is needed.
|
||||
|
||||
Note: Versions before 0.5 are not covered by the security policy. Those versions
|
||||
have multiple security issues and should not be used anyway.
|
||||
|
||||
4. Communicate with the reporter
|
||||
|
||||
Communicate the decision to the reporter.
|
||||
|
||||
If the bug was not considered a security bug or not covered by this policy,
|
||||
explain why and direct the reporter to open a public [issue in
|
||||
GitHub](https://github.com/libjxl/libjxl/issues) or open one on their behalf.
|
||||
You don't need to follow the rest of the guide in this case.
|
||||
|
||||
If the bug *is* a covered security bug then follow the rest of this guide.
|
||||
|
||||
Ask the reporter how they want to be credited in the disclosure: name and
|
||||
company affiliation if any. Security researchers often value this recognition
|
||||
and helps them dedicate their time to finding security bugs in our project.
|
||||
|
||||
There's no bug bounty (monetary compensation for security bugs) available for
|
||||
libjxl.
|
||||
|
||||
5. Create a Security Advisory draft in GitHub
|
||||
|
||||
At this point it was established that the bug is a security issue that requires
|
||||
a vulnerability disclosure. Start by creating a Security Advisory draft in the
|
||||
[Security Advisories](https://github.com/libjxl/libjxl/security/advisories) page
|
||||
in GitHub.
|
||||
|
||||
Add a short description of the bug explaining what's the issue and what's the
|
||||
impact of the issue. Being 'hard' or 'complex' to exploit is not a reason to
|
||||
discard the potential impact. You can update this description later, save it as
|
||||
a draft in GitHub.
|
||||
|
||||
Add the reporter to the security advisory draft if they have a GitHub account,
|
||||
and add the project members that will be working on a fix for the bug.
|
||||
|
||||
Establish the severity of the issue according to the impact and tag the
|
||||
appropriate Common Weakness Enumeration (CWE) values. This helps classify the
|
||||
security issues according to their nature.
|
||||
|
||||
6. Work on a fix in a private branch
|
||||
|
||||
Coordinators can work on the fix themselves, use a proposed fix from the
|
||||
reporter if there is one, or work with other project members to create one.
|
||||
|
||||
Work on a fix for the bug in *private*. Don't publish a Pull Request with the
|
||||
fix like you normally do, and don't upload the fix to your libjxl fork. If you
|
||||
ask another project member to work on it, explain them that they should follow
|
||||
this guide.
|
||||
|
||||
7. Request a CVE number
|
||||
|
||||
The Common Vulnerabilities and Exposures (CVE) is the system used to disclose
|
||||
vulnerabilities in software. A CVE number, like CVE-2021-NNNNNN, is a unique
|
||||
identifier for a given vulnerability. These numbers are assigned by a CVE
|
||||
Numbering Authority (CNA) with scope on the given project that has the
|
||||
vulnerability. For libjxl, we use Google's Generic CNA.
|
||||
|
||||
For VMT coordinators at Google, file a bug at
|
||||
[go/cve-request](https://goto.google.com/cve-request) to request a CVE. See
|
||||
go/vcp-cna for context.
|
||||
|
||||
When requesting the CVE include:
|
||||
|
||||
* A description of the problem (example: bug when parsing this field)
|
||||
* A description of the impact of the bug (example: OOB read, remote code
|
||||
execution, etc)
|
||||
* The proposed CWE id(s) determined earlier.
|
||||
* List of affected versions.
|
||||
* Reporter of the bug and their preferred name/company to include in the
|
||||
disclosure.
|
||||
* Links to the issues/fixes (if already public), these can be added later, even
|
||||
after the CVE is public.
|
||||
* The CPE prefix of the affected project (`cpe:2.3:a:libjxl_project:libjxl`)
|
||||
|
||||
When in doubt, you can discuss these with the security team while requesting it.
|
||||
|
||||
8. File a Security bug in Chromium (if affected).
|
||||
|
||||
libjxl project is in charge of updating and maintaining Chromium's libjxl
|
||||
integration code, this includes updating the libjxl library when needed. While
|
||||
the regular CVE disclosure process will eventually create a bug to update
|
||||
Chromium, filing one at this stage speeds up the process.
|
||||
|
||||
[go/crbug](https://goto.google.com/crbug), select the "Security Bug" template
|
||||
and complete the details. This bug will be used to keep track of what versions
|
||||
of Chromium need backporting. The new bug in Chromium will not be public
|
||||
initially, but will be made public some time after the issue is fixed.
|
||||
|
||||
9. Test the fixes on the intended releases
|
||||
|
||||
When disclosing a vulnerability normally two ways to fix it are offered:
|
||||
|
||||
* A patch or set of patches that fix the issue on `main` branch, and
|
||||
* A new release that contains the security fix for the user to update to.
|
||||
|
||||
New releases that fix the vulnerability should be PATCH releases, that is, a
|
||||
previous release (like 1.2.3) plus the patches that fix the vulnerability,
|
||||
becoming a new version (like 1.2.4). See the [release process](release.md) for
|
||||
details. At least the latest MINOR release branch should have a PATCH release
|
||||
with the fix, however it might make sense to also backport the fix to older
|
||||
minor branch releases, depending on long-term support schedule for certain
|
||||
releases. For example, if many users are still using a particular older version
|
||||
of the library and updating to a new version requires significant changes (due
|
||||
to a redesigned API or new unavailable dependencies) it is helpful to provide a
|
||||
PATCH release there too.
|
||||
|
||||
In either case, make sure that you test the fix in all the branches that you
|
||||
intend to release it to.
|
||||
|
||||
The Continuous Integration pipelines don't work on the private forks created by
|
||||
the Security Advisory, so manual testing of the fix is needed there before
|
||||
making it public. Don't upload it to your public fork for testing.
|
||||
|
||||
10. Coordinate a date for release of the vulnerability disclosure.
|
||||
|
||||
Agree with the reporter and security folks from the CNA on a release date. There
|
||||
is a maximum of 90 day disclosure timeline from the day the bug was reported.
|
||||
|
||||
On the disclosure date publish the fixes and tag the new PATCH release with the
|
||||
fix. You can prepare private drafts of the release for review beforehand to
|
||||
reduce the workload.
|
||||
|
||||
Update Chromium to the new release version (if affected) and work with Chrome
|
||||
engineers on the required backports.
|
||||
|
||||
## What's a Security bug
|
||||
|
||||
A security bug is a bug that can potentially be exploited to let an attacker
|
||||
gain unauthorized access or privileges. For example, gaining code execution in
|
||||
libjxl decoder by decoding a malicious .jxl file is a security but hitting a
|
||||
`JXL_ASSERT()` is not necessarily one.
|
||||
|
||||
The supported use cases to consider in the context of security bugs that require
|
||||
a vulnerability disclosure are "release" builds. The disclosure is intended for
|
||||
users of the project, to let them know that there is a security issue and that
|
||||
they should update or patch it.
|
||||
|
||||
Unreleased versions are not relevant in this context. A bug introduced in the
|
||||
`main` branch that is not yet in any release is not covered by this guide even
|
||||
if the bug allows a remote code execution. CVEs should have a non-empty list of
|
||||
affected released versions.
|
||||
|
||||
"Developer only" code is also not covered by this policy. In particular, tools
|
||||
that are not installed by the build, or not installed when packaging `libjxl`
|
||||
are not covered. For example, a bug in `tone_map` would not affect users since
|
||||
is a developer-only tool. The rationale behind this is that users of the
|
||||
released software will not have the developer code. This developer code is in
|
||||
the same libjxl repository for convenience.
|
||||
|
||||
When considering the impact of a bug, "release" mode should be assumed. In
|
||||
release mode `JXL_ASSERT()` and `JXL_CHECK()` are enabled, but `JXL_DASSERT()`
|
||||
are not. This means that if a `JXL_DASSERT()` protects an out-of-bounds (OOB)
|
||||
write, then the impact of a bug hitting the `JXL_DASSERT()` is at least an
|
||||
OOB write. On the other hand, if a bug ends up hitting a `JXL_CHECK()` instead
|
||||
of continuing, the only impact is the process abort instead of whatever else is
|
||||
possible after the `JXL_CHECK()`.
|
||||
|
||||
Asserts in `libjxl` *tools* cause the tool process to abort, but don't affect
|
||||
the caller. Either crashing or returning an error (non-zero exit code) would
|
||||
have the same effect, so `JXL_ASSERT()` failures in the tools have no security
|
||||
or functional impact.
|
||||
|
||||
Asserts in `libjxl` libraries, meant to be linked into other processes, cause
|
||||
the caller process to abort, potentially causing a Denial of Service, however,
|
||||
Denial of Service issues are *not* considered security bugs by this policy.
|
||||
These are still issues and should be fixed, but they are not security issues.
|
||||
|
||||
Out-of-bounds (OOB) reads in process memory are considered security
|
||||
vulnerabilities. OOB reads may allow an attacker to read other buffers from the
|
||||
same process that it shouldn't have access to, even a small OOB read can
|
||||
allow the attacker to read an address in the stack or in the heap, defeating
|
||||
address space randomization techniques. In combination with other bugs these
|
||||
can enable or simplify attacks to the process using libjxl. OOB reads don't need
|
||||
to require a segmentation fault to be a problem, leaking process information in
|
||||
decoded RGB pixels could be used as part of an exploit in some scenarios.
|
||||
|
||||
OOB writes and remote code execution (RCE) are security bugs of at least high
|
||||
security impact.
|
181
third-party/libjxl/libjxl/doc/xl_overview.md
vendored
Normal file
181
third-party/libjxl/libjxl/doc/xl_overview.md
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
# XL Overview
|
||||
|
||||
## Requirements
|
||||
|
||||
JPEG XL was designed for two main requirements:
|
||||
|
||||
* high quality: visually lossless at reasonable bitrates;
|
||||
* decoding speed: multithreaded decoding should be able to reach around
|
||||
400 Megapixel/s on large images.
|
||||
|
||||
These goals apply to various types of images, including HDR content, whose
|
||||
support is made possible by full-precision (float32) computations and extensive
|
||||
support of color spaces and transfer functions.
|
||||
|
||||
High performance is achieved by designing the format with careful consideration
|
||||
of memory bandwidth usage and ease of SIMD/GPU implementation.
|
||||
|
||||
The full requirements for JPEG XL are listed in document wg1m82079.
|
||||
|
||||
## General architecture
|
||||
|
||||
The architecture follows the traditional block transform model with improvements
|
||||
in the individual components. For a quick overview, we sketch a "block diagram"
|
||||
of the lossy format decoder in the form of module names in **bold** followed by
|
||||
a brief description. Note that post-processing modules in [brackets] are
|
||||
optional - they are unnecessary or even counterproductive at very high quality
|
||||
settings.
|
||||
|
||||
**Header**: decode metadata (e.g. image dimensions) from compressed fields
|
||||
(smaller than Exp-Golomb thanks to per-field encodings). The compression and
|
||||
small number of required fields enables very compact headers - much smaller than
|
||||
JFIF and HEVC. The container supports multiple images (e.g. animations/bursts)
|
||||
and passes (progressive).
|
||||
|
||||
**Bitstream**: decode transform coefficient residuals using rANS-encoded
|
||||
<#bits,bits> symbols
|
||||
|
||||
**Dequantize**: from adaptive quant map side information, plus chroma from luma
|
||||
|
||||
**DC prediction**: expand DC residuals using adaptive (history-based) predictors
|
||||
|
||||
**Chroma from luma**: restore predicted X from B and Y from B
|
||||
|
||||
**IDCT:** 2x2..32x32, floating-point
|
||||
|
||||
**[Gaborish]**: additional deblocking convolution with 3x3 kernel
|
||||
|
||||
**[Edge preserving filter]**: nonlinear adaptive smoothing controlled by side
|
||||
information
|
||||
|
||||
**[Noise injection]**: add perceptually pleasing noise according to a per-image
|
||||
noise model
|
||||
|
||||
**Color space conversion**: from perceptual opsin XYB to linear RGB
|
||||
|
||||
**[Converting to other color spaces via ICC]**
|
||||
|
||||
The encoder is basically the reverse:
|
||||
|
||||
**Color space conversion**: from linear RGB to perceptual opsin XYB
|
||||
|
||||
**[Noise estimation]**: compute a noise model for the image
|
||||
|
||||
**[Gaborish]**: sharpening to counteract the blurring on the decoder side
|
||||
|
||||
**DCT**: transform sizes communicated via per-block side information
|
||||
|
||||
**Chroma from luma**: find the best multipliers of Y for X and B channels of
|
||||
entire image
|
||||
|
||||
**Adaptive quantization**: iterative search for quant map that yields the best
|
||||
perceived restoration
|
||||
|
||||
**Quantize**: store 16-bit prediction residuals
|
||||
|
||||
**DC prediction**: store residuals (prediction happens in quantized space)
|
||||
|
||||
**Entropy coding**: rANS and context modeling with clustering
|
||||
|
||||
|
||||
# File Structure
|
||||
|
||||
A codestream begins with a `FileHeader` followed by one or more "passes"
|
||||
(= scans: e.g. DC or AC_LF) which are then added together (summing the
|
||||
respective color components in Opsin space) to form the final image. There is no
|
||||
limit to the number of passes, so an encoder could choose to send salient parts
|
||||
first, followed by arbitrary decompositions of the final image (in terms of
|
||||
resolution, bit depth, quality or spatial location).
|
||||
|
||||
Each pass contains groups of AC and DC data. A group is a subset of pixels that
|
||||
can be decoded in parallel. DC groups contain 256x256 DCs (from 2048x2048 input
|
||||
pixels), AC groups cover 256x256 input pixels.
|
||||
|
||||
Each pass starts with a table of contents (sizes of each of their DC+AC
|
||||
groups), which enables parallel decoding and/or the decoding of a subset.
|
||||
However, there is no higher-level TOC of passes, as that would prevent
|
||||
appending additional images and could be too constraining for the encoder.
|
||||
|
||||
|
||||
## Lossless
|
||||
|
||||
JPEG XL supports tools for lossless coding designed by Alexander Rhatushnyak and
|
||||
Jon Sneyers. They are about 60-75% of size of PNG, and smaller than WebP
|
||||
lossless for photos.
|
||||
|
||||
An adaptive predictor computes 4 from the NW, N, NE and W pixels and combines
|
||||
them with weights based on previous errors. The error value is encoded in a
|
||||
bucket chosen based on a heuristic max error. The result is entropy-coded using
|
||||
the ANS encoder.
|
||||
|
||||
## Current Reference Implementation
|
||||
|
||||
### Conventions
|
||||
|
||||
The software is written in C++ and built using CMake 3.6 or later.
|
||||
|
||||
Error handling is done by having functions return values of type `jxl::Status`
|
||||
(a thin wrapper around bool which checks that it is not ignored). A convenience
|
||||
macro named `JXL_RETURN_IF_ERROR` makes this more convenient by automatically
|
||||
forwarding errors, and another macro named `JXL_FAILURE` exits with an error
|
||||
message if reached, with no effect in optimized builds.
|
||||
|
||||
To diagnose the cause of encoder/decoder failures (which often only result in a
|
||||
generic "decode failed" message), build using the following command:
|
||||
|
||||
```bash
|
||||
CMAKE_FLAGS="-DJXL_CRASH_ON_ERROR" ./ci.sh opt
|
||||
```
|
||||
|
||||
In such builds, the first JXL_FAILURE will print a message identifying where the
|
||||
problem is and the program will exit immediately afterwards.
|
||||
|
||||
### Architecture
|
||||
|
||||
Getting back to the earlier block diagram:
|
||||
|
||||
**Header** handling is implemented in `headers.h` and `field*`.
|
||||
|
||||
**Bitstream**: `entropy_coder.h`, `dec_ans_*`.
|
||||
|
||||
**(De)quantize**: `quantizer.h`.
|
||||
|
||||
**DC prediction**: `predictor.h`.
|
||||
|
||||
**Chroma from luma**: `chroma_from_luma.h`
|
||||
|
||||
**(I)DCT**: `dct*.h`. Instead of operating directly on blocks of memory, the
|
||||
functions operate on thin wrappers which can handle blocks spread across
|
||||
multiple image lines.
|
||||
|
||||
**DCT size selection**: `ac_strategy.cc`
|
||||
|
||||
**[Gaborish]**: `enc_gaborish.h`.
|
||||
|
||||
**[Edge preserving filter]**: `epf.h`
|
||||
|
||||
**[Noise injection]**: `noise*` (currently disabled)
|
||||
|
||||
**Color space conversion**: `color_*`, `dec_xyb.h`.
|
||||
|
||||
## Decoder overview
|
||||
|
||||
After decoding headers, the decoder begins processing frames (`dec_frame.cc`).
|
||||
|
||||
For each pass, it will read the DC group table of contents (TOC) and start
|
||||
decoding, dequantizing and restoring color correlation of each DC group
|
||||
(covering 2048x2048 pixels in the input image) in parallel
|
||||
(`compressed_dc.cc`). The DC is split into parts corresponding to each AC group
|
||||
(with 1px of extra border); the AC group TOC is read and each AC group (256x256
|
||||
pixels) is processed in parallel (`dec_group.cc`).
|
||||
|
||||
In each AC group, the decoder reads per-block side information indicating the
|
||||
kind of DCT transform; this is followed by the quantization field. Then, AC
|
||||
coefficients are read, dequantized and have color correlation restored on a
|
||||
tile per tile basis for better locality.
|
||||
|
||||
After all the groups are read, postprocessing is applied: Gaborish smoothing
|
||||
and edge preserving filter, to reduce blocking and other artifacts.
|
||||
|
||||
Finally, the image is converted back from the XYB color space
|
||||
(`dec_xyb.cc`) and saved to the output image (`codec_*.cc`).
|
56
third-party/libjxl/libjxl/examples/CMakeLists.txt
vendored
Normal file
56
third-party/libjxl/libjxl/examples/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# Example project using libjxl.
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(SAMPLE_LIBJXL LANGUAGES C CXX)
|
||||
|
||||
# Use pkg-config to find libjxl.
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(Jxl REQUIRED IMPORTED_TARGET libjxl)
|
||||
pkg_check_modules(JxlThreads REQUIRED IMPORTED_TARGET libjxl_threads)
|
||||
|
||||
# Build the example encoder/decoder binaries using the default shared libraries
|
||||
# installed.
|
||||
add_executable(decode_oneshot decode_oneshot.cc)
|
||||
target_link_libraries(decode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
|
||||
|
||||
add_executable(decode_progressive decode_progressive.cc)
|
||||
target_link_libraries(decode_progressive PkgConfig::Jxl PkgConfig::JxlThreads)
|
||||
|
||||
add_executable(encode_oneshot encode_oneshot.cc)
|
||||
target_link_libraries(encode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
|
||||
|
||||
|
||||
# Building a static binary with the static libjxl dependencies. How to load
|
||||
# static library configs from pkg-config and how to build static binaries
|
||||
# depends on the platform, and building static binaries in general has problems.
|
||||
# If you don't need static binaries you can remove this section.
|
||||
add_library(StaticJxl INTERFACE IMPORTED GLOBAL)
|
||||
set_target_properties(StaticJxl PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Jxl_STATIC_INCLUDE_DIR}"
|
||||
INTERFACE_COMPILE_OPTIONS "${Jxl_STATIC_CFLAGS_OTHER}"
|
||||
INTERFACE_LINK_LIBRARIES "${Jxl_STATIC_LDFLAGS}"
|
||||
)
|
||||
add_library(StaticJxlThreads INTERFACE IMPORTED GLOBAL)
|
||||
set_target_properties(StaticJxlThreads PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${JxlThreads_STATIC_INCLUDE_DIR}"
|
||||
INTERFACE_COMPILE_OPTIONS "${JxlThreads_STATIC_CFLAGS_OTHER}"
|
||||
# libgcc uses weak symbols for pthread which means that -lpthread is not
|
||||
# linked when compiling a static binary. This is a platform-specific fix for
|
||||
# that.
|
||||
INTERFACE_LINK_LIBRARIES
|
||||
"${JxlThreads_STATIC_LDFLAGS} -Wl,--whole-archive -lpthread -Wl,--no-whole-archive"
|
||||
)
|
||||
|
||||
add_executable(decode_oneshot_static decode_oneshot.cc)
|
||||
target_link_libraries(decode_oneshot_static
|
||||
-static StaticJxl StaticJxlThreads)
|
||||
|
||||
add_executable(encode_oneshot_static encode_oneshot.cc)
|
||||
target_link_libraries(encode_oneshot_static
|
||||
-static StaticJxl StaticJxlThreads)
|
172
third-party/libjxl/libjxl/examples/decode_exif_metadata.cc
vendored
Normal file
172
third-party/libjxl/libjxl/examples/decode_exif_metadata.cc
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This C++ example decodes a JPEG XL image in one shot (all input bytes
|
||||
// available at once). The example outputs the pixels and color information to a
|
||||
// floating point image and an ICC profile on disk.
|
||||
|
||||
#include <jxl/decode.h>
|
||||
#include <jxl/decode_cxx.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
bool DecodeJpegXlExif(const uint8_t* jxl, size_t size,
|
||||
std::vector<uint8_t>* exif) {
|
||||
auto dec = JxlDecoderMake(nullptr);
|
||||
|
||||
// We're only interested in the Exif boxes in this example, so don't
|
||||
// subscribe to events related to pixel data.
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BOX)) {
|
||||
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
|
||||
return false;
|
||||
}
|
||||
bool support_decompression = true;
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec.get(), JXL_TRUE)) {
|
||||
fprintf(stderr,
|
||||
"NOTE: decompressing brob boxes not supported with the currently "
|
||||
"used jxl library.\n");
|
||||
support_decompression = false;
|
||||
}
|
||||
|
||||
JxlDecoderSetInput(dec.get(), jxl, size);
|
||||
JxlDecoderCloseInput(dec.get());
|
||||
|
||||
const constexpr size_t kChunkSize = 65536;
|
||||
size_t output_pos = 0;
|
||||
|
||||
for (;;) {
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
|
||||
if (status == JXL_DEC_ERROR) {
|
||||
fprintf(stderr, "Decoder error\n");
|
||||
return false;
|
||||
} else if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
fprintf(stderr, "Error, already provided all input\n");
|
||||
return false;
|
||||
} else if (status == JXL_DEC_BOX) {
|
||||
if (!exif->empty()) {
|
||||
size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
|
||||
exif->resize(exif->size() - remaining);
|
||||
// No need to wait for JXL_DEC_SUCCESS or decode other boxes.
|
||||
return true;
|
||||
}
|
||||
JxlBoxType type;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetBoxType(dec.get(), type, support_decompression)) {
|
||||
fprintf(stderr, "Error, failed to get box type\n");
|
||||
return false;
|
||||
}
|
||||
if (!memcmp(type, "Exif", 4)) {
|
||||
exif->resize(kChunkSize);
|
||||
JxlDecoderSetBoxBuffer(dec.get(), exif->data(), exif->size());
|
||||
}
|
||||
} else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
|
||||
size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
|
||||
output_pos += kChunkSize - remaining;
|
||||
exif->resize(exif->size() + kChunkSize);
|
||||
JxlDecoderSetBoxBuffer(dec.get(), exif->data() + output_pos,
|
||||
exif->size() - output_pos);
|
||||
} else if (status == JXL_DEC_SUCCESS) {
|
||||
if (!exif->empty()) {
|
||||
size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
|
||||
exif->resize(exif->size() - remaining);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown decoder status\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
|
||||
FILE* file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
long size = ftell(file);
|
||||
// Avoid invalid file or directory.
|
||||
if (size >= LONG_MAX || size < 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fseek(file, 0, SEEK_SET) != 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
out->resize(size);
|
||||
size_t readsize = fread(out->data(), 1, size, file);
|
||||
if (fclose(file) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return readsize == static_cast<size_t>(size);
|
||||
}
|
||||
|
||||
bool WriteFile(const char* filename, const uint8_t* data, size_t size) {
|
||||
FILE* file = fopen(filename, "wb");
|
||||
if (!file) {
|
||||
fprintf(stderr, "Could not open %s for writing", filename);
|
||||
return false;
|
||||
}
|
||||
fwrite(data, 1, size, file);
|
||||
if (fclose(file) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 3) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <jxl> <exif>\n"
|
||||
"Where:\n"
|
||||
" jxl = input JPEG XL image filename\n"
|
||||
" exif = output exif filename\n"
|
||||
"Output files will be overwritten.\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* jxl_filename = argv[1];
|
||||
const char* exif_filename = argv[2];
|
||||
|
||||
std::vector<uint8_t> jxl;
|
||||
if (!LoadFile(jxl_filename, &jxl)) {
|
||||
fprintf(stderr, "couldn't load %s\n", jxl_filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> exif;
|
||||
if (!DecodeJpegXlExif(jxl.data(), jxl.size(), &exif)) {
|
||||
fprintf(stderr, "Error while decoding the jxl file\n");
|
||||
return 1;
|
||||
}
|
||||
if (exif.empty()) {
|
||||
printf("No exif data present in this image\n");
|
||||
} else {
|
||||
// TODO(lode): the exif box data contains the 4-byte TIFF header at the
|
||||
// beginning, check whether this is desired to be part of the output, or
|
||||
// should be removed.
|
||||
if (!WriteFile(exif_filename, exif.data(), exif.size())) {
|
||||
fprintf(stderr, "Error while writing the exif file\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully wrote %s\n", exif_filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
250
third-party/libjxl/libjxl/examples/decode_oneshot.cc
vendored
Normal file
250
third-party/libjxl/libjxl/examples/decode_oneshot.cc
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This C++ example decodes a JPEG XL image in one shot (all input bytes
|
||||
// available at once). The example outputs the pixels and color information to a
|
||||
// floating point image and an ICC profile on disk.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <jxl/decode.h>
|
||||
#include <jxl/decode_cxx.h>
|
||||
#include <jxl/resizable_parallel_runner.h>
|
||||
#include <jxl/resizable_parallel_runner_cxx.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
/** Decodes JPEG XL image to floating point pixels and ICC Profile. Pixel are
|
||||
* stored as floating point, as interleaved RGBA (4 floating point values per
|
||||
* pixel), line per line from top to bottom. Pixel values have nominal range
|
||||
* 0..1 but may go beyond this range for HDR or wide gamut. The ICC profile
|
||||
* describes the color format of the pixel data.
|
||||
*/
|
||||
bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
|
||||
std::vector<float>* pixels, size_t* xsize,
|
||||
size_t* ysize, std::vector<uint8_t>* icc_profile) {
|
||||
// Multi-threaded parallel runner.
|
||||
auto runner = JxlResizableParallelRunnerMake(nullptr);
|
||||
|
||||
auto dec = JxlDecoderMake(nullptr);
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
|
||||
JXL_DEC_COLOR_ENCODING |
|
||||
JXL_DEC_FULL_IMAGE)) {
|
||||
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
|
||||
JxlResizableParallelRunner,
|
||||
runner.get())) {
|
||||
fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JxlBasicInfo info;
|
||||
JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
|
||||
|
||||
JxlDecoderSetInput(dec.get(), jxl, size);
|
||||
JxlDecoderCloseInput(dec.get());
|
||||
|
||||
for (;;) {
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
|
||||
|
||||
if (status == JXL_DEC_ERROR) {
|
||||
fprintf(stderr, "Decoder error\n");
|
||||
return false;
|
||||
} else if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
fprintf(stderr, "Error, already provided all input\n");
|
||||
return false;
|
||||
} else if (status == JXL_DEC_BASIC_INFO) {
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
|
||||
fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
|
||||
return false;
|
||||
}
|
||||
*xsize = info.xsize;
|
||||
*ysize = info.ysize;
|
||||
JxlResizableParallelRunnerSetThreads(
|
||||
runner.get(),
|
||||
JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
|
||||
} else if (status == JXL_DEC_COLOR_ENCODING) {
|
||||
// Get the ICC color profile of the pixel data
|
||||
size_t icc_size;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetICCProfileSize(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,
|
||||
&icc_size)) {
|
||||
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
|
||||
return false;
|
||||
}
|
||||
icc_profile->resize(icc_size);
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
|
||||
dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,
|
||||
icc_profile->data(), icc_profile->size())) {
|
||||
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
|
||||
return false;
|
||||
}
|
||||
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
|
||||
size_t buffer_size;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
|
||||
fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
|
||||
return false;
|
||||
}
|
||||
if (buffer_size != *xsize * *ysize * 16) {
|
||||
fprintf(stderr, "Invalid out buffer size %" PRIu64 " %" PRIu64 "\n",
|
||||
static_cast<uint64_t>(buffer_size),
|
||||
static_cast<uint64_t>(*xsize * *ysize * 16));
|
||||
return false;
|
||||
}
|
||||
pixels->resize(*xsize * *ysize * 4);
|
||||
void* pixels_buffer = (void*)pixels->data();
|
||||
size_t pixels_buffer_size = pixels->size() * sizeof(float);
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
|
||||
pixels_buffer,
|
||||
pixels_buffer_size)) {
|
||||
fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
|
||||
return false;
|
||||
}
|
||||
} else if (status == JXL_DEC_FULL_IMAGE) {
|
||||
// Nothing to do. Do not yet return. If the image is an animation, more
|
||||
// full frames may be decoded. This example only keeps the last one.
|
||||
} else if (status == JXL_DEC_SUCCESS) {
|
||||
// All decoding successfully finished.
|
||||
// It's not required to call JxlDecoderReleaseInput(dec.get()) here since
|
||||
// the decoder will be destroyed.
|
||||
return true;
|
||||
} else {
|
||||
fprintf(stderr, "Unknown decoder status\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes to .pfm file (Portable FloatMap). Gimp, tev viewer and ImageMagick
|
||||
* support viewing this format.
|
||||
* The input pixels are given as 32-bit floating point with 4-channel RGBA.
|
||||
* The alpha channel will not be written since .pfm does not support it.
|
||||
*/
|
||||
bool WritePFM(const char* filename, const float* pixels, size_t xsize,
|
||||
size_t ysize) {
|
||||
FILE* file = fopen(filename, "wb");
|
||||
if (!file) {
|
||||
fprintf(stderr, "Could not open %s for writing", filename);
|
||||
return false;
|
||||
}
|
||||
uint32_t endian_test = 1;
|
||||
uint8_t little_endian[4];
|
||||
memcpy(little_endian, &endian_test, 4);
|
||||
|
||||
fprintf(file, "PF\n%d %d\n%s\n", (int)xsize, (int)ysize,
|
||||
little_endian[0] ? "-1.0" : "1.0");
|
||||
for (int y = ysize - 1; y >= 0; y--) {
|
||||
for (size_t x = 0; x < xsize; x++) {
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
const float* f = &pixels[(y * xsize + x) * 4 + c];
|
||||
fwrite(f, 4, 1, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fclose(file) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
|
||||
FILE* file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
long size = ftell(file);
|
||||
// Avoid invalid file or directory.
|
||||
if (size >= LONG_MAX || size < 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fseek(file, 0, SEEK_SET) != 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
out->resize(size);
|
||||
size_t readsize = fread(out->data(), 1, size, file);
|
||||
if (fclose(file) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return readsize == static_cast<size_t>(size);
|
||||
}
|
||||
|
||||
bool WriteFile(const char* filename, const uint8_t* data, size_t size) {
|
||||
FILE* file = fopen(filename, "wb");
|
||||
if (!file) {
|
||||
fprintf(stderr, "Could not open %s for writing", filename);
|
||||
return false;
|
||||
}
|
||||
fwrite(data, 1, size, file);
|
||||
if (fclose(file) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 4) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <jxl> <pfm> <icc>\n"
|
||||
"Where:\n"
|
||||
" jxl = input JPEG XL image filename\n"
|
||||
" pfm = output Portable FloatMap image filename\n"
|
||||
" icc = output ICC color profile filename\n"
|
||||
"Output files will be overwritten.\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* jxl_filename = argv[1];
|
||||
const char* pfm_filename = argv[2];
|
||||
const char* icc_filename = argv[3];
|
||||
|
||||
std::vector<uint8_t> jxl;
|
||||
if (!LoadFile(jxl_filename, &jxl)) {
|
||||
fprintf(stderr, "couldn't load %s\n", jxl_filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<float> pixels;
|
||||
std::vector<uint8_t> icc_profile;
|
||||
size_t xsize = 0, ysize = 0;
|
||||
if (!DecodeJpegXlOneShot(jxl.data(), jxl.size(), &pixels, &xsize, &ysize,
|
||||
&icc_profile)) {
|
||||
fprintf(stderr, "Error while decoding the jxl file\n");
|
||||
return 1;
|
||||
}
|
||||
if (!WritePFM(pfm_filename, pixels.data(), xsize, ysize)) {
|
||||
fprintf(stderr, "Error while writing the PFM image file\n");
|
||||
return 1;
|
||||
}
|
||||
if (!WriteFile(icc_filename, icc_profile.data(), icc_profile.size())) {
|
||||
fprintf(stderr, "Error while writing the ICC profile file\n");
|
||||
return 1;
|
||||
}
|
||||
printf("Successfully wrote %s and %s\n", pfm_filename, icc_filename);
|
||||
return 0;
|
||||
}
|
241
third-party/libjxl/libjxl/examples/decode_progressive.cc
vendored
Normal file
241
third-party/libjxl/libjxl/examples/decode_progressive.cc
vendored
Normal file
@ -0,0 +1,241 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This C++ example decodes a JPEG XL image progressively (input bytes are
|
||||
// passed in chunks). The example outputs the intermediate steps to PAM files.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <jxl/decode.h>
|
||||
#include <jxl/decode_cxx.h>
|
||||
#include <jxl/resizable_parallel_runner.h>
|
||||
#include <jxl/resizable_parallel_runner_cxx.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) {
|
||||
FILE* fp = fopen(filename, "wb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Could not open %s for writing", filename);
|
||||
return false;
|
||||
}
|
||||
fprintf(fp,
|
||||
"P7\nWIDTH %" PRIu64 "\nHEIGHT %" PRIu64
|
||||
"\nDEPTH 4\nMAXVAL 255\nTUPLTYPE "
|
||||
"RGB_ALPHA\nENDHDR\n",
|
||||
static_cast<uint64_t>(w), static_cast<uint64_t>(h));
|
||||
size_t num_bytes = w * h * 4;
|
||||
if (fwrite(buffer, 1, num_bytes, fp) != num_bytes) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
};
|
||||
if (fclose(fp) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Decodes JPEG XL image to 8-bit integer RGBA pixels and an ICC Profile, in a
|
||||
* progressive way, saving the intermediate steps.
|
||||
*/
|
||||
bool DecodeJpegXlProgressive(const uint8_t* jxl, size_t size,
|
||||
const char* filename, size_t chunksize) {
|
||||
std::vector<uint8_t> pixels;
|
||||
std::vector<uint8_t> icc_profile;
|
||||
size_t xsize = 0, ysize = 0;
|
||||
|
||||
// Multi-threaded parallel runner.
|
||||
auto runner = JxlResizableParallelRunnerMake(nullptr);
|
||||
|
||||
auto dec = JxlDecoderMake(nullptr);
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
|
||||
JXL_DEC_COLOR_ENCODING |
|
||||
JXL_DEC_FULL_IMAGE)) {
|
||||
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
|
||||
JxlResizableParallelRunner,
|
||||
runner.get())) {
|
||||
fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JxlBasicInfo info;
|
||||
JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
|
||||
|
||||
size_t seen = 0;
|
||||
JxlDecoderSetInput(dec.get(), jxl, chunksize);
|
||||
size_t remaining = chunksize;
|
||||
|
||||
for (;;) {
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
|
||||
|
||||
if (status == JXL_DEC_ERROR) {
|
||||
fprintf(stderr, "Decoder error\n");
|
||||
return false;
|
||||
} else if (status == JXL_DEC_NEED_MORE_INPUT || status == JXL_DEC_SUCCESS ||
|
||||
status == JXL_DEC_FULL_IMAGE) {
|
||||
seen += remaining - JxlDecoderReleaseInput(dec.get());
|
||||
printf("Flushing after %" PRIu64 " bytes\n", static_cast<uint64_t>(seen));
|
||||
if (status == JXL_DEC_NEED_MORE_INPUT &&
|
||||
JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec.get())) {
|
||||
printf("flush error (no preview yet)\n");
|
||||
} else {
|
||||
char fname[1024];
|
||||
if (snprintf(fname, 1024, "%s-%" PRIu64 ".pam", filename,
|
||||
static_cast<uint64_t>(seen)) >= 1024) {
|
||||
fprintf(stderr, "Filename too long\n");
|
||||
return false;
|
||||
};
|
||||
if (!WritePAM(fname, pixels.data(), xsize, ysize)) {
|
||||
fprintf(stderr, "Error writing progressive output\n");
|
||||
}
|
||||
}
|
||||
remaining = size - seen;
|
||||
if (remaining > chunksize) remaining = chunksize;
|
||||
if (remaining == 0) {
|
||||
if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
fprintf(stderr, "Error, already provided all input\n");
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
JxlDecoderSetInput(dec.get(), jxl + seen, remaining);
|
||||
} else if (status == JXL_DEC_BASIC_INFO) {
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
|
||||
fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
|
||||
return false;
|
||||
}
|
||||
xsize = info.xsize;
|
||||
ysize = info.ysize;
|
||||
JxlResizableParallelRunnerSetThreads(
|
||||
runner.get(),
|
||||
JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
|
||||
} else if (status == JXL_DEC_COLOR_ENCODING) {
|
||||
// Get the ICC color profile of the pixel data
|
||||
size_t icc_size;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetICCProfileSize(
|
||||
dec.get(), JXL_COLOR_PROFILE_TARGET_ORIGINAL, &icc_size)) {
|
||||
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
|
||||
return false;
|
||||
}
|
||||
icc_profile.resize(icc_size);
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
|
||||
dec.get(), JXL_COLOR_PROFILE_TARGET_ORIGINAL,
|
||||
icc_profile.data(), icc_profile.size())) {
|
||||
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
|
||||
return false;
|
||||
}
|
||||
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
|
||||
size_t buffer_size;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
|
||||
fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
|
||||
return false;
|
||||
}
|
||||
if (buffer_size != xsize * ysize * 4) {
|
||||
fprintf(stderr, "Invalid out buffer size %" PRIu64 " != %" PRIu64 "\n",
|
||||
static_cast<uint64_t>(buffer_size),
|
||||
static_cast<uint64_t>(xsize * ysize * 4));
|
||||
return false;
|
||||
}
|
||||
pixels.resize(xsize * ysize * 4);
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
|
||||
pixels.data(),
|
||||
pixels.size())) {
|
||||
fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Unknown decoder status\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
|
||||
FILE* file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
long size = ftell(file);
|
||||
// Avoid invalid file or directory.
|
||||
if (size >= LONG_MAX || size < 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fseek(file, 0, SEEK_SET) != 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
out->resize(size);
|
||||
size_t readsize = fread(out->data(), 1, size, file);
|
||||
if (fclose(file) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return readsize == static_cast<size_t>(size);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc < 3) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Usage: %s <jxl> <basename> [chunksize]\n"
|
||||
"Where:\n"
|
||||
" jxl = input JPEG XL image filename\n"
|
||||
" basename = prefix of output filenames\n"
|
||||
" chunksize = loads chunksize bytes at a time and writes\n"
|
||||
" intermediate results to basename-[bytes loaded].pam\n"
|
||||
"Output files will be overwritten.\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* jxl_filename = argv[1];
|
||||
const char* png_filename = argv[2];
|
||||
|
||||
std::vector<uint8_t> jxl;
|
||||
if (!LoadFile(jxl_filename, &jxl)) {
|
||||
fprintf(stderr, "couldn't load %s\n", jxl_filename);
|
||||
return 1;
|
||||
}
|
||||
size_t chunksize = jxl.size();
|
||||
if (argc > 3) {
|
||||
long cs = atol(argv[3]);
|
||||
if (cs < 100) {
|
||||
fprintf(stderr, "Chunk size is too low, try at least 100 bytes\n");
|
||||
return 1;
|
||||
}
|
||||
chunksize = cs;
|
||||
}
|
||||
|
||||
if (!DecodeJpegXlProgressive(jxl.data(), jxl.size(), png_filename,
|
||||
chunksize)) {
|
||||
fprintf(stderr, "Error while decoding the jxl file\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
276
third-party/libjxl/libjxl/examples/encode_oneshot.cc
vendored
Normal file
276
third-party/libjxl/libjxl/examples/encode_oneshot.cc
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This example encodes a file containing a floating point image to another
|
||||
// file containing JPEG XL image with a single frame.
|
||||
|
||||
#include <jxl/encode.h>
|
||||
#include <jxl/encode_cxx.h>
|
||||
#include <jxl/thread_parallel_runner.h>
|
||||
#include <jxl/thread_parallel_runner_cxx.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Reads from .pfm file (Portable FloatMap)
|
||||
*
|
||||
* @param filename name of the file to read
|
||||
* @param pixels vector to fill with loaded pixels as 32-bit floating point with
|
||||
* 3-channel RGB
|
||||
* @param xsize set to width of loaded image
|
||||
* @param ysize set to height of loaded image
|
||||
*/
|
||||
bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize,
|
||||
uint32_t* ysize) {
|
||||
FILE* file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
fprintf(stderr, "Could not open %s for reading.\n", filename);
|
||||
return false;
|
||||
}
|
||||
uint32_t endian_test = 1;
|
||||
uint8_t little_endian[4];
|
||||
memcpy(little_endian, &endian_test, 4);
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
long size = ftell(file);
|
||||
// Avoid invalid file or directory.
|
||||
if (size >= LONG_MAX || size < 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fseek(file, 0, SEEK_SET) != 0) {
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char> data;
|
||||
data.resize(size);
|
||||
|
||||
size_t readsize = fread(data.data(), 1, size, file);
|
||||
if ((long)readsize != size) {
|
||||
return false;
|
||||
}
|
||||
if (fclose(file) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::stringstream datastream;
|
||||
std::string datastream_content(data.data(), data.size());
|
||||
datastream.str(datastream_content);
|
||||
|
||||
std::string pf_token;
|
||||
getline(datastream, pf_token, '\n');
|
||||
if (pf_token != "PF") {
|
||||
fprintf(stderr,
|
||||
"%s doesn't seem to be a 3 channel Portable FloatMap file (missing "
|
||||
"'PF\\n' "
|
||||
"bytes).\n",
|
||||
filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string xsize_token;
|
||||
getline(datastream, xsize_token, ' ');
|
||||
*xsize = std::stoi(xsize_token);
|
||||
|
||||
std::string ysize_token;
|
||||
getline(datastream, ysize_token, '\n');
|
||||
*ysize = std::stoi(ysize_token);
|
||||
|
||||
std::string endianness_token;
|
||||
getline(datastream, endianness_token, '\n');
|
||||
bool input_little_endian;
|
||||
if (endianness_token == "1.0") {
|
||||
input_little_endian = false;
|
||||
} else if (endianness_token == "-1.0") {
|
||||
input_little_endian = true;
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"%s doesn't seem to be a Portable FloatMap file (endianness token "
|
||||
"isn't '1.0' or '-1.0').\n",
|
||||
filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t offset = pf_token.size() + 1 + xsize_token.size() + 1 +
|
||||
ysize_token.size() + 1 + endianness_token.size() + 1;
|
||||
|
||||
if (data.size() != *ysize * *xsize * 3 * 4 + offset) {
|
||||
fprintf(stderr,
|
||||
"%s doesn't seem to be a Portable FloatMap file (pixel data bytes "
|
||||
"are %d, but expected %d * %d * 3 * 4 + %d (%d).\n",
|
||||
filename, (int)data.size(), (int)*ysize, (int)*xsize, (int)offset,
|
||||
(int)(*ysize * *xsize * 3 * 4 + offset));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!!little_endian[0] != input_little_endian) {
|
||||
fprintf(stderr,
|
||||
"%s has a different endianness than we do, conversion is not "
|
||||
"supported.\n",
|
||||
filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
pixels->resize(*ysize * *xsize * 3);
|
||||
|
||||
for (int y = *ysize - 1; y >= 0; y--) {
|
||||
for (int x = 0; x < (int)*xsize; x++) {
|
||||
for (int c = 0; c < 3; c++) {
|
||||
memcpy(pixels->data() + (y * *xsize + x) * 3 + c, data.data() + offset,
|
||||
sizeof(float));
|
||||
offset += sizeof(float);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compresses the provided pixels.
|
||||
*
|
||||
* @param pixels input pixels
|
||||
* @param xsize width of the input image
|
||||
* @param ysize height of the input image
|
||||
* @param compressed will be populated with the compressed bytes
|
||||
*/
|
||||
bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize,
|
||||
const uint32_t ysize, std::vector<uint8_t>* compressed) {
|
||||
auto enc = JxlEncoderMake(/*memory_manager=*/nullptr);
|
||||
auto runner = JxlThreadParallelRunnerMake(
|
||||
/*memory_manager=*/nullptr,
|
||||
JxlThreadParallelRunnerDefaultNumWorkerThreads());
|
||||
if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(),
|
||||
JxlThreadParallelRunner,
|
||||
runner.get())) {
|
||||
fprintf(stderr, "JxlEncoderSetParallelRunner failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JxlPixelFormat pixel_format = {3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
|
||||
|
||||
JxlBasicInfo basic_info;
|
||||
JxlEncoderInitBasicInfo(&basic_info);
|
||||
basic_info.xsize = xsize;
|
||||
basic_info.ysize = ysize;
|
||||
basic_info.bits_per_sample = 32;
|
||||
basic_info.exponent_bits_per_sample = 8;
|
||||
basic_info.uses_original_profile = JXL_FALSE;
|
||||
if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) {
|
||||
fprintf(stderr, "JxlEncoderSetBasicInfo failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JxlColorEncoding color_encoding = {};
|
||||
JxlColorEncodingSetToSRGB(&color_encoding,
|
||||
/*is_gray=*/pixel_format.num_channels < 3);
|
||||
if (JXL_ENC_SUCCESS !=
|
||||
JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) {
|
||||
fprintf(stderr, "JxlEncoderSetColorEncoding failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JxlEncoderFrameSettings* frame_settings =
|
||||
JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
|
||||
|
||||
if (JXL_ENC_SUCCESS !=
|
||||
JxlEncoderAddImageFrame(frame_settings, &pixel_format,
|
||||
(void*)pixels.data(),
|
||||
sizeof(float) * pixels.size())) {
|
||||
fprintf(stderr, "JxlEncoderAddImageFrame failed\n");
|
||||
return false;
|
||||
}
|
||||
JxlEncoderCloseInput(enc.get());
|
||||
|
||||
compressed->resize(64);
|
||||
uint8_t* next_out = compressed->data();
|
||||
size_t avail_out = compressed->size() - (next_out - compressed->data());
|
||||
JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT;
|
||||
while (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
|
||||
process_result = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out);
|
||||
if (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
|
||||
size_t offset = next_out - compressed->data();
|
||||
compressed->resize(compressed->size() * 2);
|
||||
next_out = compressed->data() + offset;
|
||||
avail_out = compressed->size() - offset;
|
||||
}
|
||||
}
|
||||
compressed->resize(next_out - compressed->data());
|
||||
if (JXL_ENC_SUCCESS != process_result) {
|
||||
fprintf(stderr, "JxlEncoderProcessOutput failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes bytes to file.
|
||||
*/
|
||||
bool WriteFile(const std::vector<uint8_t>& bytes, const char* filename) {
|
||||
FILE* file = fopen(filename, "wb");
|
||||
if (!file) {
|
||||
fprintf(stderr, "Could not open %s for writing\n", filename);
|
||||
return false;
|
||||
}
|
||||
if (fwrite(bytes.data(), sizeof(uint8_t), bytes.size(), file) !=
|
||||
bytes.size()) {
|
||||
fprintf(stderr, "Could not write bytes to %s\n", filename);
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
if (fclose(file) != 0) {
|
||||
fprintf(stderr, "Could not close %s\n", filename);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 3) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <pfm> <jxl>\n"
|
||||
"Where:\n"
|
||||
" pfm = input Portable FloatMap image filename\n"
|
||||
" jxl = output JPEG XL image filename\n"
|
||||
"Output files will be overwritten.\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* pfm_filename = argv[1];
|
||||
const char* jxl_filename = argv[2];
|
||||
|
||||
std::vector<float> pixels;
|
||||
uint32_t xsize;
|
||||
uint32_t ysize;
|
||||
if (!ReadPFM(pfm_filename, &pixels, &xsize, &ysize)) {
|
||||
fprintf(stderr, "Couldn't load %s\n", pfm_filename);
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> compressed;
|
||||
if (!EncodeJxlOneshot(pixels, xsize, ysize, &compressed)) {
|
||||
fprintf(stderr, "Couldn't encode jxl\n");
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (!WriteFile(compressed, jxl_filename)) {
|
||||
fprintf(stderr, "Couldn't write jxl file\n");
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
11
third-party/libjxl/libjxl/examples/examples.cmake
vendored
Normal file
11
third-party/libjxl/libjxl/examples/examples.cmake
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
add_executable(decode_oneshot ${CMAKE_CURRENT_LIST_DIR}/decode_oneshot.cc)
|
||||
target_link_libraries(decode_oneshot jxl_dec jxl_threads)
|
||||
add_executable(decode_progressive ${CMAKE_CURRENT_LIST_DIR}/decode_progressive.cc)
|
||||
target_link_libraries(decode_progressive jxl_dec jxl_threads)
|
||||
add_executable(encode_oneshot ${CMAKE_CURRENT_LIST_DIR}/encode_oneshot.cc)
|
||||
target_link_libraries(encode_oneshot jxl jxl_threads)
|
298
third-party/libjxl/libjxl/lib/BUILD
vendored
Normal file
298
third-party/libjxl/libjxl/lib/BUILD
vendored
Normal file
@ -0,0 +1,298 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# Load sources/headers/tests lists.
|
||||
load(
|
||||
"jxl_lists.bzl",
|
||||
"libjxl_base_sources",
|
||||
"libjxl_codec_apng_sources",
|
||||
"libjxl_codec_exr_sources",
|
||||
"libjxl_codec_gif_sources",
|
||||
"libjxl_codec_jpegli_sources",
|
||||
"libjxl_codec_jpg_sources",
|
||||
"libjxl_codec_jxl_sources",
|
||||
"libjxl_codec_npy_sources",
|
||||
"libjxl_codec_pgx_sources",
|
||||
"libjxl_codec_pnm_sources",
|
||||
"libjxl_dec_box_sources",
|
||||
"libjxl_dec_jpeg_sources",
|
||||
"libjxl_dec_sources",
|
||||
"libjxl_enc_sources",
|
||||
"libjxl_extras_for_tools_sources",
|
||||
"libjxl_extras_sources",
|
||||
#'libjxl_gbench_sources',
|
||||
"libjxl_jpegli_lib_version",
|
||||
"libjxl_jpegli_libjpeg_helper_files",
|
||||
"libjxl_jpegli_sources",
|
||||
"libjxl_jpegli_testlib_files",
|
||||
"libjxl_jpegli_tests",
|
||||
"libjxl_major_version",
|
||||
"libjxl_minor_version",
|
||||
"libjxl_patch_version",
|
||||
"libjxl_public_headers",
|
||||
"libjxl_testlib_files",
|
||||
"libjxl_tests",
|
||||
"libjxl_threads_public_headers",
|
||||
"libjxl_threads_sources",
|
||||
)
|
||||
load(
|
||||
"jxl_vars.bzl",
|
||||
"libjxl_deps_brotli",
|
||||
"libjxl_deps_exr",
|
||||
"libjxl_deps_gif",
|
||||
"libjxl_deps_gtest",
|
||||
"libjxl_deps_hwy",
|
||||
"libjxl_deps_hwy_nanobenchmark",
|
||||
"libjxl_deps_hwy_test_util",
|
||||
"libjxl_deps_jpeg",
|
||||
"libjxl_deps_jxl_box",
|
||||
"libjxl_deps_png",
|
||||
"libjxl_deps_runfiles",
|
||||
"libjxl_deps_skcms",
|
||||
"libjxl_deps_testdata",
|
||||
"libjxl_root_package",
|
||||
"libjxl_test_shards",
|
||||
"libjxl_test_timeouts",
|
||||
)
|
||||
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
|
||||
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
|
||||
|
||||
DEFAULT_VISIBILITY = ["//:__subpackages__"]
|
||||
|
||||
DEFAULT_COMPATIBILITY = []
|
||||
|
||||
INCLUDES_DIR = "include"
|
||||
|
||||
package(
|
||||
default_visibility = ["//:__subpackages__"],
|
||||
)
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
EXPORT_TEMPLATE = """
|
||||
#ifndef @_EXPORT_H
|
||||
#define @_EXPORT_H
|
||||
|
||||
#define @_EXPORT
|
||||
#define @_NO_EXPORT
|
||||
|
||||
#ifndef @_DEPRECATED
|
||||
# define @_DEPRECATED __attribute__ ((__deprecated__))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
"""
|
||||
|
||||
JXL_EXPORT_H = INCLUDES_DIR + "/jxl/jxl_export.h"
|
||||
|
||||
genrule(
|
||||
name = "create_jxl_export",
|
||||
outs = [JXL_EXPORT_H],
|
||||
cmd = "echo '" + EXPORT_TEMPLATE.replace("@", "JXL") + "' > $@",
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
)
|
||||
|
||||
JXL_THREADS_EXPORT_H = INCLUDES_DIR + "/jxl/jxl_threads_export.h"
|
||||
|
||||
genrule(
|
||||
name = "create_jxl_threads_export",
|
||||
outs = [JXL_THREADS_EXPORT_H],
|
||||
cmd = "echo '" + EXPORT_TEMPLATE.replace("@", "JXL_THREADS") + "' > $@",
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
)
|
||||
|
||||
JXL_VERSION_H = INCLUDES_DIR + "/jxl/version.h"
|
||||
|
||||
expand_template(
|
||||
name = "expand_jxl_version",
|
||||
out = JXL_VERSION_H,
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
substitutions = {
|
||||
"@JPEGXL_MAJOR_VERSION@": str(libjxl_major_version),
|
||||
"@JPEGXL_MINOR_VERSION@": str(libjxl_minor_version),
|
||||
"@JPEGXL_PATCH_VERSION@": str(libjxl_patch_version),
|
||||
},
|
||||
template = "jxl/version.h.in",
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "jxl_version",
|
||||
hdrs = [JXL_VERSION_H],
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
strip_include_prefix = INCLUDES_DIR,
|
||||
)
|
||||
|
||||
JPEGLI_JCONFIG_H = INCLUDES_DIR + "/jpegli/jconfig.h"
|
||||
|
||||
JPEGLI_JMORECFG_H = INCLUDES_DIR + "/jpegli/jmorecfg.h"
|
||||
|
||||
JPEGLI_JPEGLIB_H = INCLUDES_DIR + "/jpegli/jpeglib.h"
|
||||
|
||||
copy_file(
|
||||
name = "expand_jconfig",
|
||||
src = "@libjpeg_turbo//:jconfig.h",
|
||||
out = JPEGLI_JCONFIG_H,
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
)
|
||||
|
||||
copy_file(
|
||||
name = "copy_jmorecfg",
|
||||
src = "@libjpeg_turbo//:jmorecfg.h",
|
||||
out = JPEGLI_JMORECFG_H,
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
)
|
||||
|
||||
copy_file(
|
||||
name = "copy_jpeglib",
|
||||
src = "@libjpeg_turbo//:jpeglib.h",
|
||||
out = JPEGLI_JPEGLIB_H,
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "includes",
|
||||
hdrs = libjxl_public_headers + [JXL_EXPORT_H],
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
strip_include_prefix = INCLUDES_DIR,
|
||||
deps = [":jxl_version"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "libjpeg_includes",
|
||||
hdrs = [
|
||||
JPEGLI_JCONFIG_H,
|
||||
JPEGLI_JMORECFG_H,
|
||||
JPEGLI_JPEGLIB_H,
|
||||
],
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
strip_include_prefix = INCLUDES_DIR + "/jpegli",
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "base",
|
||||
srcs = [path for path in libjxl_base_sources if path.endswith(".cc")],
|
||||
hdrs = [path for path in libjxl_base_sources if path.endswith(".h")],
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
deps = [
|
||||
":includes",
|
||||
] + libjxl_deps_hwy,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "jpegxl",
|
||||
srcs = libjxl_dec_sources + libjxl_dec_box_sources + libjxl_dec_jpeg_sources + libjxl_enc_sources,
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
defines = ["JPEGXL_ENABLE_SKCMS=1"],
|
||||
deps = [
|
||||
":base",
|
||||
":includes",
|
||||
] + libjxl_deps_brotli + libjxl_deps_hwy + libjxl_deps_skcms,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "jpegxl_private",
|
||||
hdrs = [
|
||||
path
|
||||
for path in libjxl_dec_sources + libjxl_dec_box_sources + libjxl_dec_jpeg_sources + libjxl_enc_sources
|
||||
if path.endswith(".h") and not path.endswith("-inl.h")
|
||||
],
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
deps = [":jpegxl"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "jpegxl_threads",
|
||||
srcs = libjxl_threads_sources,
|
||||
hdrs = libjxl_threads_public_headers + [JXL_THREADS_EXPORT_H],
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
strip_include_prefix = INCLUDES_DIR,
|
||||
deps = [
|
||||
":base",
|
||||
":includes",
|
||||
],
|
||||
)
|
||||
|
||||
CODEC_FILES = libjxl_codec_apng_sources + libjxl_codec_exr_sources + libjxl_codec_gif_sources + libjxl_codec_jpegli_sources + libjxl_codec_jpg_sources + libjxl_codec_jxl_sources + libjxl_codec_npy_sources + libjxl_codec_pgx_sources + libjxl_codec_pnm_sources
|
||||
|
||||
CODEC_SRCS = [path for path in CODEC_FILES if path.endswith(".cc")]
|
||||
|
||||
CODEC_HDRS = [path for path in CODEC_FILES if path.endswith(".h")]
|
||||
|
||||
cc_library(
|
||||
name = "jpegli",
|
||||
srcs = libjxl_jpegli_sources,
|
||||
hdrs = [
|
||||
"jpegli/common_internal.h", # TODO(eustas): should not be here
|
||||
],
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
deps = [
|
||||
":jpegxl_private",
|
||||
":libjpeg_includes",
|
||||
] + libjxl_deps_hwy,
|
||||
)
|
||||
|
||||
# TODO(eustas): build codecs separately?
|
||||
cc_library(
|
||||
name = "jpegxl_extras",
|
||||
srcs = libjxl_extras_sources + libjxl_extras_for_tools_sources + CODEC_SRCS,
|
||||
hdrs = CODEC_HDRS,
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
defines = [
|
||||
"JPEGXL_ENABLE_APNG=1",
|
||||
"JPEGXL_ENABLE_EXR=1",
|
||||
"JPEGXL_ENABLE_GIF=1",
|
||||
"JPEGXL_ENABLE_JPEG=1",
|
||||
"JPEGXL_ENABLE_JPEGLI=1",
|
||||
],
|
||||
deps = [
|
||||
":jpegli",
|
||||
":jpegxl_private",
|
||||
":jpegxl_threads",
|
||||
":jxl_version",
|
||||
] + libjxl_deps_exr + libjxl_deps_gif + libjxl_deps_jpeg + libjxl_deps_png,
|
||||
)
|
||||
|
||||
TESTLIB_FILES = libjxl_testlib_files + libjxl_jpegli_testlib_files + libjxl_jpegli_libjpeg_helper_files
|
||||
|
||||
cc_library(
|
||||
name = "test_utils",
|
||||
testonly = 1,
|
||||
srcs = [path for path in TESTLIB_FILES if not path.endswith(".h")],
|
||||
hdrs = [path for path in TESTLIB_FILES if path.endswith(".h")],
|
||||
compatible_with = DEFAULT_COMPATIBILITY,
|
||||
defines = [
|
||||
'JPEGXL_ROOT_PACKAGE=\'"' + libjxl_root_package + '"\'',
|
||||
],
|
||||
deps = [
|
||||
":jpegli",
|
||||
":jpegxl_extras",
|
||||
":jpegxl_private",
|
||||
] + libjxl_deps_runfiles,
|
||||
)
|
||||
|
||||
TESTS = [path.partition(".")[0] for path in libjxl_tests + libjxl_jpegli_tests]
|
||||
|
||||
[
|
||||
cc_test(
|
||||
name = test,
|
||||
timeout = libjxl_test_timeouts.get(test, "moderate"),
|
||||
srcs = [
|
||||
test + ".cc",
|
||||
"jpegli/testing.h",
|
||||
"jxl/testing.h",
|
||||
],
|
||||
data = ["//:testdata"],
|
||||
shard_count = libjxl_test_shards.get(test, 1),
|
||||
deps = [
|
||||
":jpegxl_extras",
|
||||
":jpegxl_private",
|
||||
":jpegxl_threads",
|
||||
":test_utils",
|
||||
] + libjxl_deps_gtest + libjxl_deps_hwy_test_util + libjxl_deps_hwy_nanobenchmark + libjxl_deps_jxl_box,
|
||||
)
|
||||
for test in TESTS
|
||||
]
|
167
third-party/libjxl/libjxl/lib/CMakeLists.txt
vendored
Normal file
167
third-party/libjxl/libjxl/lib/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
set(JPEGXL_MAJOR_VERSION 0)
|
||||
set(JPEGXL_MINOR_VERSION 9)
|
||||
set(JPEGXL_PATCH_VERSION 0)
|
||||
set(JPEGXL_LIBRARY_VERSION
|
||||
"${JPEGXL_MAJOR_VERSION}.${JPEGXL_MINOR_VERSION}.${JPEGXL_PATCH_VERSION}")
|
||||
|
||||
# This is the library API/ABI compatibility version. Changing this value makes
|
||||
# the shared library incompatible with previous version. A program linked
|
||||
# against this shared library SOVERSION will not run with an older SOVERSION.
|
||||
# It is important to update this value when making incompatible API/ABI changes
|
||||
# so that programs that depend on libjxl can update their dependencies. Semantic
|
||||
# versioning allows 0.y.z to have incompatible changes in minor versions.
|
||||
set(JPEGXL_SO_MINOR_VERSION 9)
|
||||
if (JPEGXL_MAJOR_VERSION EQUAL 0)
|
||||
set(JPEGXL_LIBRARY_SOVERSION
|
||||
"${JPEGXL_MAJOR_VERSION}.${JPEGXL_SO_MINOR_VERSION}")
|
||||
else()
|
||||
set(JPEGXL_LIBRARY_SOVERSION "${JPEGXL_MAJOR_VERSION}")
|
||||
endif()
|
||||
|
||||
|
||||
# List of warning and feature flags for our library and tests.
|
||||
if (MSVC)
|
||||
set(JPEGXL_INTERNAL_FLAGS
|
||||
# TODO(janwas): add flags
|
||||
)
|
||||
else ()
|
||||
set(JPEGXL_INTERNAL_FLAGS
|
||||
# F_FLAGS
|
||||
-fmerge-all-constants
|
||||
-fno-builtin-fwrite
|
||||
-fno-builtin-fread
|
||||
|
||||
# WARN_FLAGS
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wc++11-compat
|
||||
-Warray-bounds
|
||||
-Wformat-security
|
||||
-Wimplicit-fallthrough
|
||||
-Wno-register # Needed by public headers in lcms
|
||||
-Wno-unused-function
|
||||
-Wno-unused-parameter
|
||||
-Wnon-virtual-dtor
|
||||
-Woverloaded-virtual
|
||||
-Wvla
|
||||
)
|
||||
|
||||
# Warning flags supported by clang.
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS
|
||||
-Wdeprecated-increment-bool
|
||||
# TODO(deymo): Add -Wextra-semi once we update third_party/highway.
|
||||
# -Wextra-semi
|
||||
-Wfloat-overflow-conversion
|
||||
-Wfloat-zero-conversion
|
||||
-Wfor-loop-analysis
|
||||
-Wgnu-redeclared-enum
|
||||
-Winfinite-recursion
|
||||
-Wliteral-conversion
|
||||
-Wno-c++98-compat
|
||||
-Wno-unused-command-line-argument
|
||||
-Wprivate-header
|
||||
-Wself-assign
|
||||
-Wstring-conversion
|
||||
-Wtautological-overlap-compare
|
||||
-Wthread-safety-analysis
|
||||
-Wundefined-func-template
|
||||
-Wunreachable-code
|
||||
-Wunused-comparison
|
||||
)
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0)
|
||||
list(APPEND HWY_FLAGS -Wc++2a-extensions)
|
||||
endif()
|
||||
endif() # Clang
|
||||
|
||||
if (WIN32)
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS
|
||||
-Wno-cast-align
|
||||
-Wno-double-promotion
|
||||
-Wno-float-equal
|
||||
-Wno-format-nonliteral
|
||||
-Wno-shadow
|
||||
-Wno-sign-conversion
|
||||
-Wno-zero-as-null-pointer-constant
|
||||
)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS
|
||||
-Wno-used-but-marked-unused
|
||||
-Wno-unused-template
|
||||
-Wno-unused-member-function
|
||||
-Wno-shadow-field-in-constructor
|
||||
-Wno-language-extension-token
|
||||
-Wno-global-constructors
|
||||
-Wno-c++98-compat-pedantic
|
||||
)
|
||||
endif() # Clang
|
||||
else() # WIN32
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS
|
||||
-fsized-deallocation
|
||||
-fno-exceptions
|
||||
|
||||
# Language flags
|
||||
-fmath-errno
|
||||
)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS
|
||||
-fnew-alignment=8
|
||||
-fno-cxx-exceptions
|
||||
-fno-slp-vectorize
|
||||
-fno-vectorize
|
||||
|
||||
-disable-free
|
||||
-disable-llvm-verifier
|
||||
)
|
||||
endif() # Clang
|
||||
endif() # WIN32
|
||||
endif() #!MSVC
|
||||
|
||||
# strips the -static suffix from all the elements in LIST
|
||||
function(strip_static OUTPUT_VAR LIB_LIST)
|
||||
foreach(lib IN LISTS ${LIB_LIST})
|
||||
string(REGEX REPLACE "-static$" "" lib "${lib}")
|
||||
list(APPEND out_list "${lib}")
|
||||
endforeach()
|
||||
set(${OUTPUT_VAR} ${out_list} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# The jxl library definition.
|
||||
include(jxl.cmake)
|
||||
|
||||
# Other libraries outside the core jxl library.
|
||||
if(JPEGXL_ENABLE_TOOLS)
|
||||
include(jxl_extras.cmake)
|
||||
endif()
|
||||
include(jxl_threads.cmake)
|
||||
if (JPEGXL_ENABLE_JPEGLI)
|
||||
include(jpegli.cmake)
|
||||
endif()
|
||||
|
||||
# Install all the library headers from the source and the generated ones. There
|
||||
# is no distinction on which libraries use which header since it is expected
|
||||
# that all developer libraries are available together at build time.
|
||||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/jxl
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/jxl
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
if(BUILD_TESTING)
|
||||
cmake_policy(SET CMP0057 NEW) # https://gitlab.kitware.com/cmake/cmake/issues/18198
|
||||
include(GoogleTest)
|
||||
endif()
|
||||
|
||||
# Tests for the jxl library.
|
||||
include(jxl_tests.cmake)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
# Google benchmark for the jxl library
|
||||
include(jxl_benchmark.cmake)
|
||||
endif()
|
30
third-party/libjxl/libjxl/lib/compatibility.cmake
vendored
Normal file
30
third-party/libjxl/libjxl/lib/compatibility.cmake
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
#
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
function(jxl_discover_tests TESTNAME)
|
||||
if (CMAKE_VERSION VERSION_LESS "3.10.3")
|
||||
gtest_discover_tests(${TESTNAME} TIMEOUT 240)
|
||||
else ()
|
||||
gtest_discover_tests(${TESTNAME} DISCOVERY_TIMEOUT 240)
|
||||
endif ()
|
||||
endfunction()
|
||||
|
||||
function(jxl_link_libraries DST SRC)
|
||||
if (CMAKE_VERSION VERSION_LESS "3.13.5")
|
||||
target_include_directories(${DST} SYSTEM PUBLIC
|
||||
$<BUILD_INTERFACE:$<TARGET_PROPERTY:${SRC},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>>
|
||||
)
|
||||
add_dependencies(${DST} ${SRC})
|
||||
else()
|
||||
target_link_libraries(${DST} PUBLIC ${SRC})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS "3.12.4")
|
||||
set(JXL_HWY_INCLUDE_DIRS "$<BUILD_INTERFACE:$<TARGET_PROPERTY:hwy,INTERFACE_INCLUDE_DIRECTORIES>>")
|
||||
else()
|
||||
set(JXL_HWY_INCLUDE_DIRS "$<BUILD_INTERFACE:$<TARGET_PROPERTY:$<IF:$<TARGET_EXISTS:hwy::hwy>,hwy::hwy,hwy>,INTERFACE_INCLUDE_DIRECTORIES>>")
|
||||
endif()
|
27
third-party/libjxl/libjxl/lib/extras/LICENSE.apngdis
vendored
Normal file
27
third-party/libjxl/libjxl/lib/extras/LICENSE.apngdis
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
APNG Disassembler 2.8
|
||||
|
||||
Deconstructs APNG files into individual frames.
|
||||
|
||||
http://apngdis.sourceforge.net
|
||||
|
||||
Copyright (c) 2010-2015 Max Stepin
|
||||
maxst at users.sourceforge.net
|
||||
|
||||
zlib license
|
||||
------------
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
5
third-party/libjxl/libjxl/lib/extras/README.md
vendored
Normal file
5
third-party/libjxl/libjxl/lib/extras/README.md
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
## JPEG XL "extras"
|
||||
|
||||
The files in this directory do not form part of the library or codec and are
|
||||
only used by tests or specific internal tools that have access to the internals
|
||||
of the library.
|
173
third-party/libjxl/libjxl/lib/extras/codec.cc
vendored
Normal file
173
third-party/libjxl/libjxl/lib/extras/codec.cc
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/codec.h"
|
||||
|
||||
#include <jxl/decode.h>
|
||||
#include <jxl/types.h>
|
||||
|
||||
#include "lib/extras/dec/decode.h"
|
||||
#include "lib/extras/enc/apng.h"
|
||||
#include "lib/extras/enc/exr.h"
|
||||
#include "lib/extras/enc/jpg.h"
|
||||
#include "lib/extras/enc/pgx.h"
|
||||
#include "lib/extras/enc/pnm.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/extras/packed_image_convert.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace {
|
||||
|
||||
// Any valid encoding is larger (ensures codecs can read the first few bytes)
|
||||
constexpr size_t kMinBytes = 9;
|
||||
|
||||
} // namespace
|
||||
|
||||
Status SetFromBytes(const Span<const uint8_t> bytes,
|
||||
const extras::ColorHints& color_hints, CodecInOut* io,
|
||||
ThreadPool* pool, const SizeConstraints* constraints,
|
||||
extras::Codec* orig_codec) {
|
||||
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
|
||||
|
||||
extras::PackedPixelFile ppf;
|
||||
if (extras::DecodeBytes(bytes, color_hints, &ppf, constraints, orig_codec)) {
|
||||
return ConvertPackedPixelFileToCodecInOut(ppf, pool, io);
|
||||
}
|
||||
return JXL_FAILURE("Codecs failed to decode");
|
||||
}
|
||||
|
||||
Status Encode(const CodecInOut& io, const extras::Codec codec,
|
||||
const ColorEncoding& c_desired, size_t bits_per_sample,
|
||||
std::vector<uint8_t>* bytes, ThreadPool* pool) {
|
||||
bytes->clear();
|
||||
JXL_CHECK(!io.Main().c_current().ICC().empty());
|
||||
JXL_CHECK(!c_desired.ICC().empty());
|
||||
io.CheckMetadata();
|
||||
if (io.Main().IsJPEG()) {
|
||||
JXL_WARNING("Writing JPEG data as pixels");
|
||||
}
|
||||
JxlPixelFormat format = {
|
||||
0, // num_channels is ignored by the converter
|
||||
bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16, JXL_BIG_ENDIAN,
|
||||
0};
|
||||
const bool floating_point = bits_per_sample > 16;
|
||||
std::unique_ptr<extras::Encoder> encoder;
|
||||
std::ostringstream os;
|
||||
switch (codec) {
|
||||
case extras::Codec::kPNG:
|
||||
encoder = extras::GetAPNGEncoder();
|
||||
if (encoder) {
|
||||
break;
|
||||
} else {
|
||||
return JXL_FAILURE("JPEG XL was built without (A)PNG support");
|
||||
}
|
||||
case extras::Codec::kJPG:
|
||||
format.data_type = JXL_TYPE_UINT8;
|
||||
encoder = extras::GetJPEGEncoder();
|
||||
if (encoder) {
|
||||
os << io.jpeg_quality;
|
||||
encoder->SetOption("q", os.str());
|
||||
break;
|
||||
} else {
|
||||
return JXL_FAILURE("JPEG XL was built without JPEG support");
|
||||
}
|
||||
case extras::Codec::kPNM:
|
||||
if (io.Main().HasAlpha()) {
|
||||
encoder = extras::GetPAMEncoder();
|
||||
} else if (io.Main().IsGray()) {
|
||||
encoder = extras::GetPGMEncoder();
|
||||
} else if (!floating_point) {
|
||||
encoder = extras::GetPPMEncoder();
|
||||
} else {
|
||||
format.data_type = JXL_TYPE_FLOAT;
|
||||
format.endianness = JXL_LITTLE_ENDIAN;
|
||||
encoder = extras::GetPFMEncoder();
|
||||
}
|
||||
break;
|
||||
case extras::Codec::kPGX:
|
||||
encoder = extras::GetPGXEncoder();
|
||||
break;
|
||||
case extras::Codec::kGIF:
|
||||
return JXL_FAILURE("Encoding to GIF is not implemented");
|
||||
case extras::Codec::kEXR:
|
||||
format.data_type = JXL_TYPE_FLOAT;
|
||||
encoder = extras::GetEXREncoder();
|
||||
if (encoder) {
|
||||
break;
|
||||
} else {
|
||||
return JXL_FAILURE("JPEG XL was built without OpenEXR support");
|
||||
}
|
||||
case extras::Codec::kJXL:
|
||||
return JXL_FAILURE("TODO: encode using Codec::kJXL");
|
||||
|
||||
case extras::Codec::kUnknown:
|
||||
return JXL_FAILURE("Cannot encode using Codec::kUnknown");
|
||||
}
|
||||
|
||||
if (!encoder) {
|
||||
return JXL_FAILURE("Invalid codec.");
|
||||
}
|
||||
|
||||
extras::PackedPixelFile ppf;
|
||||
JXL_RETURN_IF_ERROR(
|
||||
ConvertCodecInOutToPackedPixelFile(io, format, c_desired, pool, &ppf));
|
||||
ppf.info.bits_per_sample = bits_per_sample;
|
||||
if (format.data_type == JXL_TYPE_FLOAT) {
|
||||
ppf.info.bits_per_sample = 32;
|
||||
ppf.info.exponent_bits_per_sample = 8;
|
||||
}
|
||||
extras::EncodedImage encoded_image;
|
||||
JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded_image, pool));
|
||||
JXL_ASSERT(encoded_image.bitstreams.size() == 1);
|
||||
*bytes = encoded_image.bitstreams[0];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Status Encode(const CodecInOut& io, const ColorEncoding& c_desired,
|
||||
size_t bits_per_sample, const std::string& pathname,
|
||||
std::vector<uint8_t>* bytes, ThreadPool* pool) {
|
||||
std::string extension;
|
||||
const extras::Codec codec = extras::CodecFromPath(
|
||||
pathname, &bits_per_sample, /* basename */ nullptr, &extension);
|
||||
|
||||
// Warn about incorrect usage of PGM/PGX/PPM - only the latter supports
|
||||
// color, but CodecFromPath lumps them all together.
|
||||
if (codec == extras::Codec::kPNM && extension != ".pfm") {
|
||||
if (io.Main().HasAlpha() && extension != ".pam") {
|
||||
JXL_WARNING(
|
||||
"For images with alpha, the filename should end with .pam.\n");
|
||||
} else if (!io.Main().IsGray() && extension == ".pgm") {
|
||||
JXL_WARNING("For color images, the filename should end with .ppm.\n");
|
||||
} else if (io.Main().IsGray() && extension == ".ppm") {
|
||||
JXL_WARNING(
|
||||
"For grayscale images, the filename should not end with .ppm.\n");
|
||||
}
|
||||
if (bits_per_sample > 16) {
|
||||
JXL_WARNING("PPM only supports up to 16 bits per sample");
|
||||
bits_per_sample = 16;
|
||||
}
|
||||
} else if (codec == extras::Codec::kPGX && !io.Main().IsGray()) {
|
||||
JXL_WARNING("Storing color image to PGX - use .ppm extension instead.\n");
|
||||
}
|
||||
if (bits_per_sample > 16 && codec == extras::Codec::kPNG) {
|
||||
JXL_WARNING("PNG only supports up to 16 bits per sample");
|
||||
bits_per_sample = 16;
|
||||
}
|
||||
|
||||
return Encode(io, codec, c_desired, bits_per_sample, bytes, pool);
|
||||
}
|
||||
|
||||
Status Encode(const CodecInOut& io, const std::string& pathname,
|
||||
std::vector<uint8_t>* bytes, ThreadPool* pool) {
|
||||
// TODO(lode): need to take the floating_point_sample field into account
|
||||
return Encode(io, io.metadata.m.color_encoding,
|
||||
io.metadata.m.bit_depth.bits_per_sample, pathname, bytes, pool);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
63
third-party/libjxl/libjxl/lib/extras/codec.h
vendored
Normal file
63
third-party/libjxl/libjxl/lib/extras/codec.h
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_CODEC_H_
|
||||
#define LIB_EXTRAS_CODEC_H_
|
||||
|
||||
// Facade for image encoders/decoders (PNG, PNM, ...).
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lib/extras/dec/color_hints.h"
|
||||
#include "lib/extras/dec/decode.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/codec_in_out.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/jxl/field_encodings.h" // MakeBit
|
||||
|
||||
namespace jxl {
|
||||
|
||||
struct SizeConstraints;
|
||||
|
||||
// Decodes "bytes" and sets io->metadata.m.
|
||||
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
|
||||
Status SetFromBytes(Span<const uint8_t> bytes,
|
||||
const extras::ColorHints& color_hints, CodecInOut* io,
|
||||
ThreadPool* pool = nullptr,
|
||||
const SizeConstraints* constraints = nullptr,
|
||||
extras::Codec* orig_codec = nullptr);
|
||||
// Helper function to use no color_space_hint.
|
||||
JXL_INLINE Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io,
|
||||
ThreadPool* pool = nullptr,
|
||||
const SizeConstraints* constraints = nullptr,
|
||||
extras::Codec* orig_codec = nullptr) {
|
||||
return SetFromBytes(bytes, extras::ColorHints(), io, pool, constraints,
|
||||
orig_codec);
|
||||
}
|
||||
|
||||
// Replaces "bytes" with an encoding of pixels transformed from c_current
|
||||
// color space to c_desired.
|
||||
Status Encode(const CodecInOut& io, extras::Codec codec,
|
||||
const ColorEncoding& c_desired, size_t bits_per_sample,
|
||||
std::vector<uint8_t>* bytes, ThreadPool* pool = nullptr);
|
||||
|
||||
// Deduces codec, calls Encode and writes to file.
|
||||
Status Encode(const CodecInOut& io, const ColorEncoding& c_desired,
|
||||
size_t bits_per_sample, const std::string& pathname,
|
||||
std::vector<uint8_t>* bytes, ThreadPool* pool = nullptr);
|
||||
// Same, but defaults to metadata.original color_encoding and bits_per_sample.
|
||||
Status Encode(const CodecInOut& io, const std::string& pathname,
|
||||
std::vector<uint8_t>* bytes, ThreadPool* pool = nullptr);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_CODEC_H_
|
450
third-party/libjxl/libjxl/lib/extras/codec_test.cc
vendored
Normal file
450
third-party/libjxl/libjxl/lib/extras/codec_test.cc
vendored
Normal file
@ -0,0 +1,450 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/codec.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/dec/decode.h"
|
||||
#include "lib/extras/dec/pnm.h"
|
||||
#include "lib/extras/enc/encode.h"
|
||||
#include "lib/jxl/base/random.h"
|
||||
#include "lib/jxl/test_utils.h"
|
||||
#include "lib/jxl/testing.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
using test::ThreadPoolForTests;
|
||||
|
||||
namespace extras {
|
||||
namespace {
|
||||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::Contains;
|
||||
using ::testing::Field;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::SizeIs;
|
||||
|
||||
std::string ExtensionFromCodec(Codec codec, const bool is_gray,
|
||||
const bool has_alpha,
|
||||
const size_t bits_per_sample) {
|
||||
switch (codec) {
|
||||
case Codec::kJPG:
|
||||
return ".jpg";
|
||||
case Codec::kPGX:
|
||||
return ".pgx";
|
||||
case Codec::kPNG:
|
||||
return ".png";
|
||||
case Codec::kPNM:
|
||||
if (bits_per_sample == 32) return ".pfm";
|
||||
if (has_alpha) return ".pam";
|
||||
return is_gray ? ".pgm" : ".ppm";
|
||||
case Codec::kEXR:
|
||||
return ".exr";
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
void VerifySameImage(const PackedImage& im0, size_t bits_per_sample0,
|
||||
const PackedImage& im1, size_t bits_per_sample1,
|
||||
bool lossless = true) {
|
||||
ASSERT_EQ(im0.xsize, im1.xsize);
|
||||
ASSERT_EQ(im0.ysize, im1.ysize);
|
||||
ASSERT_EQ(im0.format.num_channels, im1.format.num_channels);
|
||||
auto get_factor = [](JxlPixelFormat f, size_t bits) -> double {
|
||||
return 1.0 / ((1u << std::min(test::GetPrecision(f.data_type), bits)) - 1);
|
||||
};
|
||||
double factor0 = get_factor(im0.format, bits_per_sample0);
|
||||
double factor1 = get_factor(im1.format, bits_per_sample1);
|
||||
auto pixels0 = static_cast<const uint8_t*>(im0.pixels());
|
||||
auto pixels1 = static_cast<const uint8_t*>(im1.pixels());
|
||||
auto rgba0 =
|
||||
test::ConvertToRGBA32(pixels0, im0.xsize, im0.ysize, im0.format, factor0);
|
||||
auto rgba1 =
|
||||
test::ConvertToRGBA32(pixels1, im1.xsize, im1.ysize, im1.format, factor1);
|
||||
double tolerance =
|
||||
lossless ? 0.5 * std::min(factor0, factor1) : 3.0f / 255.0f;
|
||||
if (bits_per_sample0 == 32 || bits_per_sample1 == 32) {
|
||||
tolerance = 0.5 * std::max(factor0, factor1);
|
||||
}
|
||||
for (size_t y = 0; y < im0.ysize; ++y) {
|
||||
for (size_t x = 0; x < im0.xsize; ++x) {
|
||||
for (size_t c = 0; c < im0.format.num_channels; ++c) {
|
||||
size_t ix = (y * im0.xsize + x) * 4 + c;
|
||||
double val0 = rgba0[ix];
|
||||
double val1 = rgba1[ix];
|
||||
ASSERT_NEAR(val1, val0, tolerance)
|
||||
<< "y = " << y << " x = " << x << " c = " << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JxlColorEncoding CreateTestColorEncoding(bool is_gray) {
|
||||
JxlColorEncoding c;
|
||||
c.color_space = is_gray ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB;
|
||||
c.white_point = JXL_WHITE_POINT_D65;
|
||||
c.primaries = JXL_PRIMARIES_P3;
|
||||
c.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
|
||||
c.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
|
||||
// Roundtrip through internal color encoding to fill in primaries and white
|
||||
// point CIE xy coordinates.
|
||||
ColorEncoding c_internal;
|
||||
JXL_CHECK(ConvertExternalToInternalColorEncoding(c, &c_internal));
|
||||
ConvertInternalToExternalColorEncoding(c_internal, &c);
|
||||
return c;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GenerateICC(JxlColorEncoding color_encoding) {
|
||||
ColorEncoding c;
|
||||
JXL_CHECK(ConvertExternalToInternalColorEncoding(color_encoding, &c));
|
||||
JXL_CHECK(c.CreateICC());
|
||||
PaddedBytes icc = c.ICC();
|
||||
return std::vector<uint8_t>(icc.begin(), icc.end());
|
||||
}
|
||||
|
||||
void StoreRandomValue(uint8_t* out, Rng* rng, JxlPixelFormat format,
|
||||
size_t bits_per_sample) {
|
||||
uint64_t max_val = (1ull << bits_per_sample) - 1;
|
||||
if (format.data_type == JXL_TYPE_UINT8) {
|
||||
*out = rng->UniformU(0, max_val);
|
||||
} else if (format.data_type == JXL_TYPE_UINT16) {
|
||||
uint32_t val = rng->UniformU(0, max_val);
|
||||
if (format.endianness == JXL_BIG_ENDIAN) {
|
||||
StoreBE16(val, out);
|
||||
} else {
|
||||
StoreLE16(val, out);
|
||||
}
|
||||
} else {
|
||||
ASSERT_EQ(format.data_type, JXL_TYPE_FLOAT);
|
||||
float val = rng->UniformF(0.0, 1.0);
|
||||
uint32_t uval;
|
||||
memcpy(&uval, &val, 4);
|
||||
if (format.endianness == JXL_BIG_ENDIAN) {
|
||||
StoreBE32(uval, out);
|
||||
} else {
|
||||
StoreLE32(uval, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillPackedImage(size_t bits_per_sample, PackedImage* image) {
|
||||
JxlPixelFormat format = image->format;
|
||||
size_t bytes_per_channel = PackedImage::BitsPerChannel(format.data_type) / 8;
|
||||
uint8_t* out = static_cast<uint8_t*>(image->pixels());
|
||||
size_t stride = image->xsize * format.num_channels * bytes_per_channel;
|
||||
ASSERT_EQ(image->pixels_size, image->ysize * stride);
|
||||
Rng rng(129);
|
||||
for (size_t y = 0; y < image->ysize; ++y) {
|
||||
for (size_t x = 0; x < image->xsize; ++x) {
|
||||
for (size_t c = 0; c < format.num_channels; ++c) {
|
||||
StoreRandomValue(out, &rng, format, bits_per_sample);
|
||||
out += bytes_per_channel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TestImageParams {
|
||||
Codec codec;
|
||||
size_t xsize;
|
||||
size_t ysize;
|
||||
size_t bits_per_sample;
|
||||
bool is_gray;
|
||||
bool add_alpha;
|
||||
bool big_endian;
|
||||
bool add_extra_channels;
|
||||
|
||||
bool ShouldTestRoundtrip() const {
|
||||
if (codec == Codec::kPNG) {
|
||||
return bits_per_sample <= 16;
|
||||
} else if (codec == Codec::kPNM) {
|
||||
// TODO(szabadka) Make PNM encoder endianness-aware.
|
||||
return ((bits_per_sample <= 16 && big_endian) ||
|
||||
(bits_per_sample == 32 && !add_alpha && !big_endian));
|
||||
} else if (codec == Codec::kPGX) {
|
||||
return ((bits_per_sample == 8 || bits_per_sample == 16) && is_gray &&
|
||||
!add_alpha);
|
||||
} else if (codec == Codec::kEXR) {
|
||||
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
|
||||
defined(THREAD_SANITIZER)
|
||||
// OpenEXR 2.3 has a memory leak in IlmThread_2_3::ThreadPool
|
||||
return false;
|
||||
#else
|
||||
return bits_per_sample == 32 && !is_gray;
|
||||
#endif
|
||||
} else if (codec == Codec::kJPG) {
|
||||
return bits_per_sample == 8 && !add_alpha;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JxlPixelFormat PixelFormat() const {
|
||||
JxlPixelFormat format;
|
||||
format.num_channels = (is_gray ? 1 : 3) + (add_alpha ? 1 : 0);
|
||||
format.data_type = (bits_per_sample == 32 ? JXL_TYPE_FLOAT
|
||||
: bits_per_sample > 8 ? JXL_TYPE_UINT16
|
||||
: JXL_TYPE_UINT8);
|
||||
format.endianness = big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN;
|
||||
format.align = 0;
|
||||
return format;
|
||||
}
|
||||
|
||||
std::string DebugString() const {
|
||||
std::ostringstream os;
|
||||
os << "bps:" << bits_per_sample << " gr:" << is_gray << " al:" << add_alpha
|
||||
<< " be: " << big_endian << " ec: " << add_extra_channels;
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
void CreateTestImage(const TestImageParams& params, PackedPixelFile* ppf) {
|
||||
ppf->info.xsize = params.xsize;
|
||||
ppf->info.ysize = params.ysize;
|
||||
ppf->info.bits_per_sample = params.bits_per_sample;
|
||||
ppf->info.exponent_bits_per_sample = params.bits_per_sample == 32 ? 8 : 0;
|
||||
ppf->info.num_color_channels = params.is_gray ? 1 : 3;
|
||||
ppf->info.alpha_bits = params.add_alpha ? params.bits_per_sample : 0;
|
||||
ppf->info.alpha_premultiplied = (params.codec == Codec::kEXR);
|
||||
|
||||
JxlColorEncoding color_encoding = CreateTestColorEncoding(params.is_gray);
|
||||
ppf->icc = GenerateICC(color_encoding);
|
||||
ppf->color_encoding = color_encoding;
|
||||
|
||||
PackedFrame frame(params.xsize, params.ysize, params.PixelFormat());
|
||||
FillPackedImage(params.bits_per_sample, &frame.color);
|
||||
if (params.add_extra_channels) {
|
||||
for (size_t i = 0; i < 7; ++i) {
|
||||
JxlPixelFormat ec_format = params.PixelFormat();
|
||||
ec_format.num_channels = 1;
|
||||
PackedImage ec(params.xsize, params.ysize, ec_format);
|
||||
FillPackedImage(params.bits_per_sample, &ec);
|
||||
frame.extra_channels.emplace_back(std::move(ec));
|
||||
PackedExtraChannel pec;
|
||||
pec.ec_info.bits_per_sample = params.bits_per_sample;
|
||||
pec.ec_info.type = static_cast<JxlExtraChannelType>(i);
|
||||
ppf->extra_channels_info.emplace_back(std::move(pec));
|
||||
}
|
||||
}
|
||||
ppf->frames.emplace_back(std::move(frame));
|
||||
}
|
||||
|
||||
// Ensures reading a newly written file leads to the same image pixels.
|
||||
void TestRoundTrip(const TestImageParams& params, ThreadPool* pool) {
|
||||
if (!params.ShouldTestRoundtrip()) return;
|
||||
|
||||
std::string extension = ExtensionFromCodec(
|
||||
params.codec, params.is_gray, params.add_alpha, params.bits_per_sample);
|
||||
printf("Codec %s %s\n", extension.c_str(), params.DebugString().c_str());
|
||||
|
||||
PackedPixelFile ppf_in;
|
||||
CreateTestImage(params, &ppf_in);
|
||||
|
||||
EncodedImage encoded;
|
||||
auto encoder = Encoder::FromExtension(extension);
|
||||
if (!encoder) {
|
||||
fprintf(stderr, "Skipping test because of missing codec support.\n");
|
||||
return;
|
||||
}
|
||||
ASSERT_TRUE(encoder->Encode(ppf_in, &encoded, pool));
|
||||
ASSERT_EQ(encoded.bitstreams.size(), 1);
|
||||
|
||||
PackedPixelFile ppf_out;
|
||||
ColorHints color_hints;
|
||||
if (params.codec == Codec::kPNM || params.codec == Codec::kPGX) {
|
||||
color_hints.Add("color_space",
|
||||
params.is_gray ? "Gra_D65_Rel_SRG" : "RGB_D65_SRG_Rel_SRG");
|
||||
}
|
||||
ASSERT_TRUE(DecodeBytes(Span<const uint8_t>(encoded.bitstreams[0]),
|
||||
color_hints, &ppf_out));
|
||||
if (params.codec == Codec::kPNG && ppf_out.icc.empty()) {
|
||||
// Decoding a PNG may drop the ICC profile if there's a valid cICP chunk.
|
||||
// Rendering intent is not preserved in this case.
|
||||
EXPECT_EQ(ppf_in.color_encoding.color_space,
|
||||
ppf_out.color_encoding.color_space);
|
||||
EXPECT_EQ(ppf_in.color_encoding.white_point,
|
||||
ppf_out.color_encoding.white_point);
|
||||
if (ppf_in.color_encoding.color_space != JXL_COLOR_SPACE_GRAY) {
|
||||
EXPECT_EQ(ppf_in.color_encoding.primaries,
|
||||
ppf_out.color_encoding.primaries);
|
||||
}
|
||||
EXPECT_EQ(ppf_in.color_encoding.transfer_function,
|
||||
ppf_out.color_encoding.transfer_function);
|
||||
EXPECT_EQ(ppf_out.color_encoding.rendering_intent,
|
||||
JXL_RENDERING_INTENT_RELATIVE);
|
||||
} else if (params.codec != Codec::kPNM && params.codec != Codec::kPGX &&
|
||||
params.codec != Codec::kEXR) {
|
||||
EXPECT_EQ(ppf_in.icc, ppf_out.icc);
|
||||
}
|
||||
|
||||
ASSERT_EQ(ppf_out.frames.size(), 1);
|
||||
const auto& frame_in = ppf_in.frames[0];
|
||||
const auto& frame_out = ppf_out.frames[0];
|
||||
VerifySameImage(frame_in.color, ppf_in.info.bits_per_sample, frame_out.color,
|
||||
ppf_out.info.bits_per_sample,
|
||||
/*lossless=*/params.codec != Codec::kJPG);
|
||||
ASSERT_EQ(frame_in.extra_channels.size(), frame_out.extra_channels.size());
|
||||
ASSERT_EQ(ppf_out.extra_channels_info.size(),
|
||||
frame_out.extra_channels.size());
|
||||
for (size_t i = 0; i < frame_in.extra_channels.size(); ++i) {
|
||||
VerifySameImage(frame_in.extra_channels[i], ppf_in.info.bits_per_sample,
|
||||
frame_out.extra_channels[i], ppf_out.info.bits_per_sample,
|
||||
/*lossless=*/true);
|
||||
EXPECT_EQ(ppf_out.extra_channels_info[i].ec_info.type,
|
||||
ppf_in.extra_channels_info[i].ec_info.type);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CodecTest, TestRoundTrip) {
|
||||
ThreadPoolForTests pool(12);
|
||||
|
||||
TestImageParams params;
|
||||
params.xsize = 7;
|
||||
params.ysize = 4;
|
||||
|
||||
for (Codec codec :
|
||||
{Codec::kPNG, Codec::kPNM, Codec::kPGX, Codec::kEXR, Codec::kJPG}) {
|
||||
for (int bits_per_sample : {4, 8, 10, 12, 16, 32}) {
|
||||
for (bool is_gray : {false, true}) {
|
||||
for (bool add_alpha : {false, true}) {
|
||||
for (bool big_endian : {false, true}) {
|
||||
params.codec = codec;
|
||||
params.bits_per_sample = static_cast<size_t>(bits_per_sample);
|
||||
params.is_gray = is_gray;
|
||||
params.add_alpha = add_alpha;
|
||||
params.big_endian = big_endian;
|
||||
params.add_extra_channels = false;
|
||||
TestRoundTrip(params, &pool);
|
||||
if (codec == Codec::kPNM && add_alpha) {
|
||||
params.add_extra_channels = true;
|
||||
TestRoundTrip(params, &pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CodecTest, LosslessPNMRoundtrip) {
|
||||
ThreadPoolForTests pool(12);
|
||||
|
||||
static const char* kChannels[] = {"", "g", "ga", "rgb", "rgba"};
|
||||
static const char* kExtension[] = {"", ".pgm", ".pam", ".ppm", ".pam"};
|
||||
for (size_t bit_depth = 1; bit_depth <= 16; ++bit_depth) {
|
||||
for (size_t channels = 1; channels <= 4; ++channels) {
|
||||
if (bit_depth == 1 && (channels == 2 || channels == 4)) continue;
|
||||
std::string extension(kExtension[channels]);
|
||||
std::string filename = "jxl/flower/flower_small." +
|
||||
std::string(kChannels[channels]) + ".depth" +
|
||||
std::to_string(bit_depth) + extension;
|
||||
const PaddedBytes orig = jxl::test::ReadTestData(filename);
|
||||
|
||||
PackedPixelFile ppf;
|
||||
ColorHints color_hints;
|
||||
color_hints.Add("color_space",
|
||||
channels < 3 ? "Gra_D65_Rel_SRG" : "RGB_D65_SRG_Rel_SRG");
|
||||
ASSERT_TRUE(DecodeBytes(Span<const uint8_t>(orig.data(), orig.size()),
|
||||
color_hints, &ppf));
|
||||
|
||||
EncodedImage encoded;
|
||||
auto encoder = Encoder::FromExtension(extension);
|
||||
ASSERT_TRUE(encoder.get());
|
||||
ASSERT_TRUE(encoder->Encode(ppf, &encoded, &pool));
|
||||
ASSERT_EQ(encoded.bitstreams.size(), 1);
|
||||
ASSERT_EQ(orig.size(), encoded.bitstreams[0].size());
|
||||
EXPECT_EQ(0,
|
||||
memcmp(orig.data(), encoded.bitstreams[0].data(), orig.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CodecTest, TestPNM) { TestCodecPNM(); }
|
||||
|
||||
TEST(CodecTest, FormatNegotiation) {
|
||||
const std::vector<JxlPixelFormat> accepted_formats = {
|
||||
{/*num_channels=*/4,
|
||||
/*data_type=*/JXL_TYPE_UINT16,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0},
|
||||
{/*num_channels=*/3,
|
||||
/*data_type=*/JXL_TYPE_UINT8,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0},
|
||||
{/*num_channels=*/3,
|
||||
/*data_type=*/JXL_TYPE_UINT16,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0},
|
||||
{/*num_channels=*/1,
|
||||
/*data_type=*/JXL_TYPE_UINT8,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0},
|
||||
};
|
||||
|
||||
JxlBasicInfo info;
|
||||
JxlEncoderInitBasicInfo(&info);
|
||||
info.bits_per_sample = 12;
|
||||
info.num_color_channels = 2;
|
||||
|
||||
JxlPixelFormat format;
|
||||
EXPECT_FALSE(SelectFormat(accepted_formats, info, &format));
|
||||
|
||||
info.num_color_channels = 3;
|
||||
ASSERT_TRUE(SelectFormat(accepted_formats, info, &format));
|
||||
EXPECT_EQ(format.num_channels, info.num_color_channels);
|
||||
// 16 is the smallest accepted format that can accommodate the 12-bit data.
|
||||
EXPECT_EQ(format.data_type, JXL_TYPE_UINT16);
|
||||
}
|
||||
|
||||
TEST(CodecTest, EncodeToPNG) {
|
||||
ThreadPool* const pool = nullptr;
|
||||
|
||||
std::unique_ptr<Encoder> png_encoder = Encoder::FromExtension(".png");
|
||||
if (!png_encoder) {
|
||||
fprintf(stderr, "Skipping test because of missing codec support.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const PaddedBytes original_png = jxl::test::ReadTestData(
|
||||
"external/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
||||
PackedPixelFile ppf;
|
||||
ASSERT_TRUE(extras::DecodeBytes(Span<const uint8_t>(original_png),
|
||||
ColorHints(), &ppf));
|
||||
|
||||
const JxlPixelFormat& format = ppf.frames.front().color.format;
|
||||
ASSERT_THAT(
|
||||
png_encoder->AcceptedFormats(),
|
||||
Contains(AllOf(Field(&JxlPixelFormat::num_channels, format.num_channels),
|
||||
Field(&JxlPixelFormat::data_type, format.data_type),
|
||||
Field(&JxlPixelFormat::endianness, format.endianness))));
|
||||
EncodedImage encoded_png;
|
||||
ASSERT_TRUE(png_encoder->Encode(ppf, &encoded_png, pool));
|
||||
EXPECT_THAT(encoded_png.icc, IsEmpty());
|
||||
ASSERT_THAT(encoded_png.bitstreams, SizeIs(1));
|
||||
|
||||
PackedPixelFile decoded_ppf;
|
||||
ASSERT_TRUE(
|
||||
extras::DecodeBytes(Span<const uint8_t>(encoded_png.bitstreams.front()),
|
||||
ColorHints(), &decoded_ppf));
|
||||
|
||||
ASSERT_EQ(decoded_ppf.info.bits_per_sample, ppf.info.bits_per_sample);
|
||||
ASSERT_EQ(decoded_ppf.frames.size(), 1);
|
||||
VerifySameImage(ppf.frames[0].color, ppf.info.bits_per_sample,
|
||||
decoded_ppf.frames[0].color,
|
||||
decoded_ppf.info.bits_per_sample);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
987
third-party/libjxl/libjxl/lib/extras/dec/apng.cc
vendored
Normal file
987
third-party/libjxl/libjxl/lib/extras/dec/apng.cc
vendored
Normal file
@ -0,0 +1,987 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/dec/apng.h"
|
||||
|
||||
// Parts of this code are taken from apngdis, which has the following license:
|
||||
/* APNG Disassembler 2.8
|
||||
*
|
||||
* Deconstructs APNG files into individual frames.
|
||||
*
|
||||
* http://apngdis.sourceforge.net
|
||||
*
|
||||
* Copyright (c) 2010-2015 Max Stepin
|
||||
* maxst at users.sourceforge.net
|
||||
*
|
||||
* zlib license
|
||||
* ------------
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <jxl/codestream_header.h>
|
||||
#include <jxl/encode.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/size_constraints.h"
|
||||
#include "lib/jxl/base/byte_order.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/printf_macros.h"
|
||||
#include "lib/jxl/base/scope_guard.h"
|
||||
#include "lib/jxl/common.h"
|
||||
#include "lib/jxl/sanitizers.h"
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
#include "png.h" /* original (unpatched) libpng is ok */
|
||||
#endif
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
namespace {
|
||||
|
||||
constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69,
|
||||
0x66, 0x00, 0x00};
|
||||
|
||||
/* hIST chunk tail is not proccesed properly; skip this chunk completely;
|
||||
see https://github.com/glennrp/libpng/pull/413 */
|
||||
const png_byte kIgnoredPngChunks[] = {
|
||||
104, 73, 83, 84, '\0' /* hIST */
|
||||
};
|
||||
|
||||
// Returns floating-point value from the PNG encoding (times 10^5).
|
||||
static double F64FromU32(const uint32_t x) {
|
||||
return static_cast<int32_t>(x) * 1E-5;
|
||||
}
|
||||
|
||||
Status DecodeSRGB(const unsigned char* payload, const size_t payload_size,
|
||||
JxlColorEncoding* color_encoding) {
|
||||
if (payload_size != 1) return JXL_FAILURE("Wrong sRGB size");
|
||||
// (PNG uses the same values as ICC.)
|
||||
if (payload[0] >= 4) return JXL_FAILURE("Invalid Rendering Intent");
|
||||
color_encoding->white_point = JXL_WHITE_POINT_D65;
|
||||
color_encoding->primaries = JXL_PRIMARIES_SRGB;
|
||||
color_encoding->transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
|
||||
color_encoding->rendering_intent =
|
||||
static_cast<JxlRenderingIntent>(payload[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the cICP profile is not fully supported, return false and leave
|
||||
// color_encoding unmodified.
|
||||
Status DecodeCICP(const unsigned char* payload, const size_t payload_size,
|
||||
JxlColorEncoding* color_encoding) {
|
||||
if (payload_size != 4) return JXL_FAILURE("Wrong cICP size");
|
||||
JxlColorEncoding color_enc = *color_encoding;
|
||||
|
||||
// From https://www.itu.int/rec/T-REC-H.273-202107-I/en
|
||||
if (payload[0] == 1) {
|
||||
// IEC 61966-2-1 sRGB
|
||||
color_enc.primaries = JXL_PRIMARIES_SRGB;
|
||||
color_enc.white_point = JXL_WHITE_POINT_D65;
|
||||
} else if (payload[0] == 4) {
|
||||
// Rec. ITU-R BT.470-6 System M
|
||||
color_enc.primaries = JXL_PRIMARIES_CUSTOM;
|
||||
color_enc.primaries_red_xy[0] = 0.67;
|
||||
color_enc.primaries_red_xy[1] = 0.33;
|
||||
color_enc.primaries_green_xy[0] = 0.21;
|
||||
color_enc.primaries_green_xy[1] = 0.71;
|
||||
color_enc.primaries_blue_xy[0] = 0.14;
|
||||
color_enc.primaries_blue_xy[1] = 0.08;
|
||||
color_enc.white_point = JXL_WHITE_POINT_CUSTOM;
|
||||
color_enc.white_point_xy[0] = 0.310;
|
||||
color_enc.white_point_xy[1] = 0.316;
|
||||
} else if (payload[0] == 5) {
|
||||
// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM
|
||||
color_enc.primaries = JXL_PRIMARIES_CUSTOM;
|
||||
color_enc.primaries_red_xy[0] = 0.64;
|
||||
color_enc.primaries_red_xy[1] = 0.33;
|
||||
color_enc.primaries_green_xy[0] = 0.29;
|
||||
color_enc.primaries_green_xy[1] = 0.60;
|
||||
color_enc.primaries_blue_xy[0] = 0.15;
|
||||
color_enc.primaries_blue_xy[1] = 0.06;
|
||||
color_enc.white_point = JXL_WHITE_POINT_D65;
|
||||
} else if (payload[0] == 6 || payload[0] == 7) {
|
||||
// SMPTE ST 170 (2004) / SMPTE ST 240 (1999)
|
||||
color_enc.primaries = JXL_PRIMARIES_CUSTOM;
|
||||
color_enc.primaries_red_xy[0] = 0.630;
|
||||
color_enc.primaries_red_xy[1] = 0.340;
|
||||
color_enc.primaries_green_xy[0] = 0.310;
|
||||
color_enc.primaries_green_xy[1] = 0.595;
|
||||
color_enc.primaries_blue_xy[0] = 0.155;
|
||||
color_enc.primaries_blue_xy[1] = 0.070;
|
||||
color_enc.white_point = JXL_WHITE_POINT_D65;
|
||||
} else if (payload[0] == 8) {
|
||||
// Generic film (colour filters using Illuminant C)
|
||||
color_enc.primaries = JXL_PRIMARIES_CUSTOM;
|
||||
color_enc.primaries_red_xy[0] = 0.681;
|
||||
color_enc.primaries_red_xy[1] = 0.319;
|
||||
color_enc.primaries_green_xy[0] = 0.243;
|
||||
color_enc.primaries_green_xy[1] = 0.692;
|
||||
color_enc.primaries_blue_xy[0] = 0.145;
|
||||
color_enc.primaries_blue_xy[1] = 0.049;
|
||||
color_enc.white_point = JXL_WHITE_POINT_CUSTOM;
|
||||
color_enc.white_point_xy[0] = 0.310;
|
||||
color_enc.white_point_xy[1] = 0.316;
|
||||
} else if (payload[0] == 9) {
|
||||
// Rec. ITU-R BT.2100-2
|
||||
color_enc.primaries = JXL_PRIMARIES_2100;
|
||||
color_enc.white_point = JXL_WHITE_POINT_D65;
|
||||
} else if (payload[0] == 10) {
|
||||
// CIE 1931 XYZ
|
||||
color_enc.primaries = JXL_PRIMARIES_CUSTOM;
|
||||
color_enc.primaries_red_xy[0] = 1;
|
||||
color_enc.primaries_red_xy[1] = 0;
|
||||
color_enc.primaries_green_xy[0] = 0;
|
||||
color_enc.primaries_green_xy[1] = 1;
|
||||
color_enc.primaries_blue_xy[0] = 0;
|
||||
color_enc.primaries_blue_xy[1] = 0;
|
||||
color_enc.white_point = JXL_WHITE_POINT_E;
|
||||
} else if (payload[0] == 11) {
|
||||
// SMPTE RP 431-2 (2011)
|
||||
color_enc.primaries = JXL_PRIMARIES_P3;
|
||||
color_enc.white_point = JXL_WHITE_POINT_DCI;
|
||||
} else if (payload[0] == 12) {
|
||||
// SMPTE EG 432-1 (2010)
|
||||
color_enc.primaries = JXL_PRIMARIES_P3;
|
||||
color_enc.white_point = JXL_WHITE_POINT_D65;
|
||||
} else if (payload[0] == 22) {
|
||||
color_enc.primaries = JXL_PRIMARIES_CUSTOM;
|
||||
color_enc.primaries_red_xy[0] = 0.630;
|
||||
color_enc.primaries_red_xy[1] = 0.340;
|
||||
color_enc.primaries_green_xy[0] = 0.295;
|
||||
color_enc.primaries_green_xy[1] = 0.605;
|
||||
color_enc.primaries_blue_xy[0] = 0.155;
|
||||
color_enc.primaries_blue_xy[1] = 0.077;
|
||||
color_enc.white_point = JXL_WHITE_POINT_D65;
|
||||
} else {
|
||||
JXL_WARNING("Unsupported primaries specified in cICP chunk: %d",
|
||||
static_cast<int>(payload[0]));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (payload[1] == 1 || payload[1] == 6 || payload[1] == 14 ||
|
||||
payload[1] == 15) {
|
||||
// Rec. ITU-R BT.709-6
|
||||
color_enc.transfer_function = JXL_TRANSFER_FUNCTION_709;
|
||||
} else if (payload[1] == 4) {
|
||||
// Rec. ITU-R BT.1700-0 625 PAL and 625 SECAM
|
||||
color_enc.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
|
||||
color_enc.gamma = 1 / 2.2;
|
||||
} else if (payload[1] == 5) {
|
||||
// Rec. ITU-R BT.470-6 System B, G
|
||||
color_enc.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
|
||||
color_enc.gamma = 1 / 2.8;
|
||||
} else if (payload[1] == 8 || payload[1] == 13 || payload[1] == 16 ||
|
||||
payload[1] == 17 || payload[1] == 18) {
|
||||
// These codes all match the corresponding JXL enum values
|
||||
color_enc.transfer_function = static_cast<JxlTransferFunction>(payload[1]);
|
||||
} else {
|
||||
JXL_WARNING("Unsupported transfer function specified in cICP chunk: %d",
|
||||
static_cast<int>(payload[1]));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (payload[2] != 0) {
|
||||
JXL_WARNING("Unsupported color space specified in cICP chunk: %d",
|
||||
static_cast<int>(payload[2]));
|
||||
return false;
|
||||
}
|
||||
if (payload[3] != 1) {
|
||||
JXL_WARNING("Unsupported full-range flag specified in cICP chunk: %d",
|
||||
static_cast<int>(payload[3]));
|
||||
return false;
|
||||
}
|
||||
// cICP has no rendering intent, so use the default
|
||||
color_enc.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
|
||||
*color_encoding = color_enc;
|
||||
return true;
|
||||
}
|
||||
|
||||
Status DecodeGAMA(const unsigned char* payload, const size_t payload_size,
|
||||
JxlColorEncoding* color_encoding) {
|
||||
if (payload_size != 4) return JXL_FAILURE("Wrong gAMA size");
|
||||
color_encoding->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
|
||||
color_encoding->gamma = F64FromU32(LoadBE32(payload));
|
||||
return true;
|
||||
}
|
||||
|
||||
Status DecodeCHRM(const unsigned char* payload, const size_t payload_size,
|
||||
JxlColorEncoding* color_encoding) {
|
||||
if (payload_size != 32) return JXL_FAILURE("Wrong cHRM size");
|
||||
|
||||
color_encoding->white_point = JXL_WHITE_POINT_CUSTOM;
|
||||
color_encoding->white_point_xy[0] = F64FromU32(LoadBE32(payload + 0));
|
||||
color_encoding->white_point_xy[1] = F64FromU32(LoadBE32(payload + 4));
|
||||
|
||||
color_encoding->primaries = JXL_PRIMARIES_CUSTOM;
|
||||
color_encoding->primaries_red_xy[0] = F64FromU32(LoadBE32(payload + 8));
|
||||
color_encoding->primaries_red_xy[1] = F64FromU32(LoadBE32(payload + 12));
|
||||
color_encoding->primaries_green_xy[0] = F64FromU32(LoadBE32(payload + 16));
|
||||
color_encoding->primaries_green_xy[1] = F64FromU32(LoadBE32(payload + 20));
|
||||
color_encoding->primaries_blue_xy[0] = F64FromU32(LoadBE32(payload + 24));
|
||||
color_encoding->primaries_blue_xy[1] = F64FromU32(LoadBE32(payload + 28));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retrieves XMP and EXIF/IPTC from itext and text.
|
||||
class BlobsReaderPNG {
|
||||
public:
|
||||
static Status Decode(const png_text_struct& info, PackedMetadata* metadata) {
|
||||
// We trust these are properly null-terminated by libpng.
|
||||
const char* key = info.key;
|
||||
const char* value = info.text;
|
||||
if (strstr(key, "XML:com.adobe.xmp")) {
|
||||
metadata->xmp.resize(strlen(value)); // safe, see above
|
||||
memcpy(metadata->xmp.data(), value, metadata->xmp.size());
|
||||
}
|
||||
|
||||
std::string type;
|
||||
std::vector<uint8_t> bytes;
|
||||
|
||||
// Handle text chunks annotated with key "Raw profile type ####", with
|
||||
// #### a type, which may contain metadata.
|
||||
const char* kKey = "Raw profile type ";
|
||||
if (strncmp(key, kKey, strlen(kKey)) != 0) return false;
|
||||
|
||||
if (!MaybeDecodeBase16(key, value, &type, &bytes)) {
|
||||
JXL_WARNING("Couldn't parse 'Raw format type' text chunk");
|
||||
return false;
|
||||
}
|
||||
if (type == "exif") {
|
||||
// Remove "Exif\0\0" prefix if present
|
||||
if (bytes.size() >= sizeof kExifSignature &&
|
||||
memcmp(bytes.data(), kExifSignature, sizeof kExifSignature) == 0) {
|
||||
bytes.erase(bytes.begin(), bytes.begin() + sizeof kExifSignature);
|
||||
}
|
||||
if (!metadata->exif.empty()) {
|
||||
JXL_WARNING("overwriting EXIF (%" PRIuS " bytes) with base16 (%" PRIuS
|
||||
" bytes)",
|
||||
metadata->exif.size(), bytes.size());
|
||||
}
|
||||
metadata->exif = std::move(bytes);
|
||||
} else if (type == "iptc") {
|
||||
// TODO (jon): Deal with IPTC in some way
|
||||
} else if (type == "8bim") {
|
||||
// TODO (jon): Deal with 8bim in some way
|
||||
} else if (type == "xmp") {
|
||||
if (!metadata->xmp.empty()) {
|
||||
JXL_WARNING("overwriting XMP (%" PRIuS " bytes) with base16 (%" PRIuS
|
||||
" bytes)",
|
||||
metadata->xmp.size(), bytes.size());
|
||||
}
|
||||
metadata->xmp = std::move(bytes);
|
||||
} else {
|
||||
JXL_WARNING("Unknown type in 'Raw format type' text chunk: %s: %" PRIuS
|
||||
" bytes",
|
||||
type.c_str(), bytes.size());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns false if invalid.
|
||||
static JXL_INLINE Status DecodeNibble(const char c,
|
||||
uint32_t* JXL_RESTRICT nibble) {
|
||||
if ('a' <= c && c <= 'f') {
|
||||
*nibble = 10 + c - 'a';
|
||||
} else if ('0' <= c && c <= '9') {
|
||||
*nibble = c - '0';
|
||||
} else {
|
||||
*nibble = 0;
|
||||
return JXL_FAILURE("Invalid metadata nibble");
|
||||
}
|
||||
JXL_ASSERT(*nibble < 16);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns false if invalid.
|
||||
static JXL_INLINE Status DecodeDecimal(const char** pos, const char* end,
|
||||
uint32_t* JXL_RESTRICT value) {
|
||||
size_t len = 0;
|
||||
*value = 0;
|
||||
while (*pos < end) {
|
||||
char next = **pos;
|
||||
if (next >= '0' && next <= '9') {
|
||||
*value = (*value * 10) + static_cast<uint32_t>(next - '0');
|
||||
len++;
|
||||
if (len > 8) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Do not consume terminator (non-decimal digit).
|
||||
break;
|
||||
}
|
||||
(*pos)++;
|
||||
}
|
||||
if (len == 0 || len > 8) {
|
||||
return JXL_FAILURE("Failed to parse decimal");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses a PNG text chunk with key of the form "Raw profile type ####", with
|
||||
// #### a type.
|
||||
// Returns whether it could successfully parse the content.
|
||||
// We trust key and encoded are null-terminated because they come from
|
||||
// libpng.
|
||||
static Status MaybeDecodeBase16(const char* key, const char* encoded,
|
||||
std::string* type,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
const char* encoded_end = encoded + strlen(encoded);
|
||||
|
||||
const char* kKey = "Raw profile type ";
|
||||
if (strncmp(key, kKey, strlen(kKey)) != 0) return false;
|
||||
*type = key + strlen(kKey);
|
||||
const size_t kMaxTypeLen = 20;
|
||||
if (type->length() > kMaxTypeLen) return false; // Type too long
|
||||
|
||||
// Header: freeform string and number of bytes
|
||||
// Expected format is:
|
||||
// \n
|
||||
// profile name/description\n
|
||||
// 40\n (the number of bytes after hex-decoding)
|
||||
// 01234566789abcdef....\n (72 bytes per line max).
|
||||
// 012345667\n (last line)
|
||||
const char* pos = encoded;
|
||||
|
||||
if (*(pos++) != '\n') return false;
|
||||
while (pos < encoded_end && *pos != '\n') {
|
||||
pos++;
|
||||
}
|
||||
if (pos == encoded_end) return false;
|
||||
// We parsed so far a \n, some number of non \n characters and are now
|
||||
// pointing at a \n.
|
||||
if (*(pos++) != '\n') return false;
|
||||
// Skip leading spaces
|
||||
while (pos < encoded_end && *pos == ' ') {
|
||||
pos++;
|
||||
}
|
||||
uint32_t bytes_to_decode = 0;
|
||||
JXL_RETURN_IF_ERROR(DecodeDecimal(&pos, encoded_end, &bytes_to_decode));
|
||||
|
||||
// We need 2*bytes for the hex values plus 1 byte every 36 values,
|
||||
// plus terminal \n for length.
|
||||
const unsigned long needed_bytes =
|
||||
bytes_to_decode * 2 + 1 + DivCeil(bytes_to_decode, 36);
|
||||
if (needed_bytes != static_cast<size_t>(encoded_end - pos)) {
|
||||
return JXL_FAILURE("Not enough bytes to parse %d bytes in hex",
|
||||
bytes_to_decode);
|
||||
}
|
||||
JXL_ASSERT(bytes->empty());
|
||||
bytes->reserve(bytes_to_decode);
|
||||
|
||||
// Encoding: base16 with newline after 72 chars.
|
||||
// pos points to the \n before the first line of hex values.
|
||||
for (size_t i = 0; i < bytes_to_decode; ++i) {
|
||||
if (i % 36 == 0) {
|
||||
if (pos + 1 >= encoded_end) return false; // Truncated base16 1
|
||||
if (*pos != '\n') return false; // Expected newline
|
||||
++pos;
|
||||
}
|
||||
|
||||
if (pos + 2 >= encoded_end) return false; // Truncated base16 2;
|
||||
uint32_t nibble0, nibble1;
|
||||
JXL_RETURN_IF_ERROR(DecodeNibble(pos[0], &nibble0));
|
||||
JXL_RETURN_IF_ERROR(DecodeNibble(pos[1], &nibble1));
|
||||
bytes->push_back(static_cast<uint8_t>((nibble0 << 4) + nibble1));
|
||||
pos += 2;
|
||||
}
|
||||
if (pos + 1 != encoded_end) return false; // Too many encoded bytes
|
||||
if (pos[0] != '\n') return false; // Incorrect metadata terminator
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool isAbc(char c) {
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
||||
}
|
||||
|
||||
constexpr uint32_t kId_IHDR = 0x52444849;
|
||||
constexpr uint32_t kId_acTL = 0x4C546361;
|
||||
constexpr uint32_t kId_fcTL = 0x4C546366;
|
||||
constexpr uint32_t kId_IDAT = 0x54414449;
|
||||
constexpr uint32_t kId_fdAT = 0x54416466;
|
||||
constexpr uint32_t kId_IEND = 0x444E4549;
|
||||
constexpr uint32_t kId_cICP = 0x50434963;
|
||||
constexpr uint32_t kId_iCCP = 0x50434369;
|
||||
constexpr uint32_t kId_sRGB = 0x42475273;
|
||||
constexpr uint32_t kId_gAMA = 0x414D4167;
|
||||
constexpr uint32_t kId_cHRM = 0x4D524863;
|
||||
constexpr uint32_t kId_eXIf = 0x66495865;
|
||||
|
||||
struct APNGFrame {
|
||||
std::vector<uint8_t> pixels;
|
||||
std::vector<uint8_t*> rows;
|
||||
unsigned int w, h, delay_num, delay_den;
|
||||
};
|
||||
|
||||
struct Reader {
|
||||
const uint8_t* next;
|
||||
const uint8_t* last;
|
||||
bool Read(void* data, size_t len) {
|
||||
size_t cap = last - next;
|
||||
size_t to_copy = std::min(cap, len);
|
||||
memcpy(data, next, to_copy);
|
||||
next += to_copy;
|
||||
return (len == to_copy);
|
||||
}
|
||||
bool Eof() { return next == last; }
|
||||
};
|
||||
|
||||
const unsigned long cMaxPNGSize = 1000000UL;
|
||||
const size_t kMaxPNGChunkSize = 1lu << 30; // 1 GB
|
||||
|
||||
void info_fn(png_structp png_ptr, png_infop info_ptr) {
|
||||
png_set_expand(png_ptr);
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
(void)png_set_interlace_handling(png_ptr);
|
||||
png_read_update_info(png_ptr, info_ptr);
|
||||
}
|
||||
|
||||
void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num,
|
||||
int pass) {
|
||||
APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr);
|
||||
JXL_CHECK(frame);
|
||||
JXL_CHECK(row_num < frame->rows.size());
|
||||
JXL_CHECK(frame->rows[row_num] < frame->pixels.data() + frame->pixels.size());
|
||||
png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row);
|
||||
}
|
||||
|
||||
inline unsigned int read_chunk(Reader* r, std::vector<uint8_t>* pChunk) {
|
||||
unsigned char len[4];
|
||||
if (r->Read(&len, 4)) {
|
||||
const auto size = png_get_uint_32(len);
|
||||
// Check first, to avoid overflow.
|
||||
if (size > kMaxPNGChunkSize) {
|
||||
JXL_WARNING("APNG chunk size is too big");
|
||||
return 0;
|
||||
}
|
||||
pChunk->resize(size + 12);
|
||||
memcpy(pChunk->data(), len, 4);
|
||||
if (r->Read(pChunk->data() + 4, pChunk->size() - 4)) {
|
||||
return LoadLE32(pChunk->data() + 4);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
|
||||
bool hasInfo, std::vector<uint8_t>& chunkIHDR,
|
||||
std::vector<std::vector<uint8_t>>& chunksInfo) {
|
||||
unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10};
|
||||
|
||||
// Cleanup prior decoder, if any.
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
|
||||
// Just in case. Not all versions on libpng wipe-out the pointers.
|
||||
png_ptr = nullptr;
|
||||
info_ptr = nullptr;
|
||||
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!png_ptr || !info_ptr) return 1;
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
png_set_keep_unknown_chunks(png_ptr, 1, kIgnoredPngChunks,
|
||||
(int)sizeof(kIgnoredPngChunks) / 5);
|
||||
|
||||
png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
||||
png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, NULL);
|
||||
|
||||
png_process_data(png_ptr, info_ptr, header, 8);
|
||||
png_process_data(png_ptr, info_ptr, chunkIHDR.data(), chunkIHDR.size());
|
||||
|
||||
if (hasInfo) {
|
||||
for (unsigned int i = 0; i < chunksInfo.size(); i++) {
|
||||
png_process_data(png_ptr, info_ptr, chunksInfo[i].data(),
|
||||
chunksInfo[i].size());
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int processing_data(png_structp png_ptr, png_infop info_ptr, unsigned char* p,
|
||||
unsigned int size) {
|
||||
if (!png_ptr || !info_ptr) return 1;
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
png_process_data(png_ptr, info_ptr, p, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int processing_finish(png_structp png_ptr, png_infop info_ptr,
|
||||
PackedMetadata* metadata) {
|
||||
unsigned char footer[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130};
|
||||
|
||||
if (!png_ptr || !info_ptr) return 1;
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
png_process_data(png_ptr, info_ptr, footer, 12);
|
||||
// before destroying: check if we encountered any metadata chunks
|
||||
png_textp text_ptr;
|
||||
int num_text;
|
||||
png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
|
||||
for (int i = 0; i < num_text; i++) {
|
||||
(void)BlobsReaderPNG::Decode(text_ptr[i], metadata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
bool CanDecodeAPNG() {
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
||||
const ColorHints& color_hints, PackedPixelFile* ppf,
|
||||
const SizeConstraints* constraints) {
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
Reader r;
|
||||
unsigned int id, j, w, h, w0, h0, x0, y0;
|
||||
unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize;
|
||||
unsigned char sig[8];
|
||||
png_structp png_ptr = nullptr;
|
||||
png_infop info_ptr = nullptr;
|
||||
std::vector<uint8_t> chunk;
|
||||
std::vector<uint8_t> chunkIHDR;
|
||||
std::vector<std::vector<uint8_t>> chunksInfo;
|
||||
bool isAnimated = false;
|
||||
bool hasInfo = false;
|
||||
bool seenFctl = false;
|
||||
APNGFrame frameRaw = {};
|
||||
uint32_t num_channels;
|
||||
JxlPixelFormat format;
|
||||
unsigned int bytes_per_pixel = 0;
|
||||
|
||||
struct FrameInfo {
|
||||
PackedImage data;
|
||||
uint32_t duration;
|
||||
size_t x0, xsize;
|
||||
size_t y0, ysize;
|
||||
uint32_t dispose_op;
|
||||
uint32_t blend_op;
|
||||
};
|
||||
|
||||
std::vector<FrameInfo> frames;
|
||||
|
||||
// Make sure png memory is released in any case.
|
||||
auto scope_guard = MakeScopeGuard([&]() {
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
|
||||
// Just in case. Not all versions on libpng wipe-out the pointers.
|
||||
png_ptr = nullptr;
|
||||
info_ptr = nullptr;
|
||||
});
|
||||
|
||||
r = {bytes.data(), bytes.data() + bytes.size()};
|
||||
// Not a PNG => not an error
|
||||
unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
|
||||
if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) {
|
||||
return false;
|
||||
}
|
||||
id = read_chunk(&r, &chunkIHDR);
|
||||
|
||||
ppf->info.exponent_bits_per_sample = 0;
|
||||
ppf->info.alpha_exponent_bits = 0;
|
||||
ppf->info.orientation = JXL_ORIENT_IDENTITY;
|
||||
|
||||
ppf->frames.clear();
|
||||
|
||||
bool have_color = false;
|
||||
bool have_cicp = false, have_iccp = false, have_srgb = false;
|
||||
bool errorstate = true;
|
||||
if (id == kId_IHDR && chunkIHDR.size() == 25) {
|
||||
x0 = 0;
|
||||
y0 = 0;
|
||||
delay_num = 1;
|
||||
delay_den = 10;
|
||||
dop = 0;
|
||||
bop = 0;
|
||||
|
||||
w0 = w = png_get_uint_32(chunkIHDR.data() + 8);
|
||||
h0 = h = png_get_uint_32(chunkIHDR.data() + 12);
|
||||
if (w > cMaxPNGSize || h > cMaxPNGSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// default settings in case e.g. only gAMA is given
|
||||
ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB;
|
||||
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
|
||||
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
|
||||
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
|
||||
ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
|
||||
|
||||
if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
|
||||
chunkIHDR, chunksInfo)) {
|
||||
while (!r.Eof()) {
|
||||
id = read_chunk(&r, &chunk);
|
||||
if (!id) break;
|
||||
seenFctl |= (id == kId_fcTL);
|
||||
|
||||
if (id == kId_acTL && !hasInfo && !isAnimated) {
|
||||
isAnimated = true;
|
||||
ppf->info.have_animation = true;
|
||||
ppf->info.animation.tps_numerator = 1000;
|
||||
ppf->info.animation.tps_denominator = 1;
|
||||
} else if (id == kId_IEND ||
|
||||
(id == kId_fcTL && (!hasInfo || isAnimated))) {
|
||||
if (hasInfo) {
|
||||
if (!processing_finish(png_ptr, info_ptr, &ppf->metadata)) {
|
||||
// Allocates the frame buffer.
|
||||
uint32_t duration = delay_num * 1000 / delay_den;
|
||||
frames.push_back(FrameInfo{PackedImage(w0, h0, format), duration,
|
||||
x0, w0, y0, h0, dop, bop});
|
||||
auto& frame = frames.back().data;
|
||||
for (size_t y = 0; y < h0; ++y) {
|
||||
memcpy(static_cast<uint8_t*>(frame.pixels()) + frame.stride * y,
|
||||
frameRaw.rows[y], bytes_per_pixel * w0);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (id == kId_IEND) {
|
||||
errorstate = false;
|
||||
break;
|
||||
}
|
||||
if (chunk.size() < 34) {
|
||||
return JXL_FAILURE("Received a chunk that is too small (%" PRIuS
|
||||
"B)",
|
||||
chunk.size());
|
||||
}
|
||||
// At this point the old frame is done. Let's start a new one.
|
||||
w0 = png_get_uint_32(chunk.data() + 12);
|
||||
h0 = png_get_uint_32(chunk.data() + 16);
|
||||
x0 = png_get_uint_32(chunk.data() + 20);
|
||||
y0 = png_get_uint_32(chunk.data() + 24);
|
||||
delay_num = png_get_uint_16(chunk.data() + 28);
|
||||
delay_den = png_get_uint_16(chunk.data() + 30);
|
||||
dop = chunk[32];
|
||||
bop = chunk[33];
|
||||
|
||||
if (!delay_den) delay_den = 100;
|
||||
|
||||
if (w0 > cMaxPNGSize || h0 > cMaxPNGSize || x0 > cMaxPNGSize ||
|
||||
y0 > cMaxPNGSize || x0 + w0 > w || y0 + h0 > h || dop > 2 ||
|
||||
bop > 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasInfo) {
|
||||
memcpy(chunkIHDR.data() + 8, chunk.data() + 12, 8);
|
||||
if (processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
|
||||
chunkIHDR, chunksInfo)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (id == kId_IDAT) {
|
||||
// First IDAT chunk means we now have all header info
|
||||
if (seenFctl) {
|
||||
// `fcTL` chunk must appear after all `IDAT` chunks
|
||||
return JXL_FAILURE("IDAT chunk after fcTL chunk");
|
||||
}
|
||||
hasInfo = true;
|
||||
JXL_CHECK(w == png_get_image_width(png_ptr, info_ptr));
|
||||
JXL_CHECK(h == png_get_image_height(png_ptr, info_ptr));
|
||||
int colortype = png_get_color_type(png_ptr, info_ptr);
|
||||
int png_bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||
ppf->info.bits_per_sample = png_bit_depth;
|
||||
png_color_8p sigbits = NULL;
|
||||
png_get_sBIT(png_ptr, info_ptr, &sigbits);
|
||||
if (colortype & 1) {
|
||||
// palette will actually be 8-bit regardless of the index bitdepth
|
||||
ppf->info.bits_per_sample = 8;
|
||||
}
|
||||
if (colortype & 2) {
|
||||
ppf->info.num_color_channels = 3;
|
||||
ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB;
|
||||
if (sigbits && sigbits->red == sigbits->green &&
|
||||
sigbits->green == sigbits->blue)
|
||||
ppf->info.bits_per_sample = sigbits->red;
|
||||
} else {
|
||||
ppf->info.num_color_channels = 1;
|
||||
ppf->color_encoding.color_space = JXL_COLOR_SPACE_GRAY;
|
||||
if (sigbits) ppf->info.bits_per_sample = sigbits->gray;
|
||||
}
|
||||
if (colortype & 4 ||
|
||||
png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||
ppf->info.alpha_bits = ppf->info.bits_per_sample;
|
||||
if (sigbits) {
|
||||
if (sigbits->alpha &&
|
||||
sigbits->alpha != ppf->info.bits_per_sample) {
|
||||
return JXL_FAILURE("Unsupported alpha bit-depth");
|
||||
}
|
||||
ppf->info.alpha_bits = sigbits->alpha;
|
||||
}
|
||||
} else {
|
||||
ppf->info.alpha_bits = 0;
|
||||
}
|
||||
ppf->color_encoding.color_space =
|
||||
(ppf->info.num_color_channels == 1 ? JXL_COLOR_SPACE_GRAY
|
||||
: JXL_COLOR_SPACE_RGB);
|
||||
ppf->info.xsize = w;
|
||||
ppf->info.ysize = h;
|
||||
JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, w, h));
|
||||
num_channels =
|
||||
ppf->info.num_color_channels + (ppf->info.alpha_bits ? 1 : 0);
|
||||
format = {
|
||||
/*num_channels=*/num_channels,
|
||||
/*data_type=*/ppf->info.bits_per_sample > 8 ? JXL_TYPE_UINT16
|
||||
: JXL_TYPE_UINT8,
|
||||
/*endianness=*/JXL_BIG_ENDIAN,
|
||||
/*align=*/0,
|
||||
};
|
||||
if (png_bit_depth > 8 && format.data_type == JXL_TYPE_UINT8) {
|
||||
png_set_strip_16(png_ptr);
|
||||
}
|
||||
bytes_per_pixel =
|
||||
num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1);
|
||||
rowbytes = w * bytes_per_pixel;
|
||||
imagesize = h * rowbytes;
|
||||
frameRaw.pixels.resize(imagesize);
|
||||
frameRaw.rows.resize(h);
|
||||
for (j = 0; j < h; j++)
|
||||
frameRaw.rows[j] = frameRaw.pixels.data() + j * rowbytes;
|
||||
|
||||
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
|
||||
break;
|
||||
}
|
||||
} else if (id == kId_fdAT && isAnimated) {
|
||||
if (!hasInfo) {
|
||||
return JXL_FAILURE("fDAT chunk before iDAT");
|
||||
}
|
||||
png_save_uint_32(chunk.data() + 4, chunk.size() - 16);
|
||||
memcpy(chunk.data() + 8, "IDAT", 4);
|
||||
if (processing_data(png_ptr, info_ptr, chunk.data() + 4,
|
||||
chunk.size() - 4)) {
|
||||
break;
|
||||
}
|
||||
} else if (id == kId_cICP) {
|
||||
// Color profile chunks: cICP has the highest priority, followed by
|
||||
// iCCP and sRGB (which shouldn't co-exist, but if they do, we use
|
||||
// iCCP), followed finally by gAMA and cHRM.
|
||||
if (DecodeCICP(chunk.data() + 8, chunk.size() - 12,
|
||||
&ppf->color_encoding)) {
|
||||
have_cicp = true;
|
||||
have_color = true;
|
||||
ppf->icc.clear();
|
||||
}
|
||||
} else if (!have_cicp && id == kId_iCCP) {
|
||||
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
|
||||
JXL_WARNING("Corrupt iCCP chunk");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(jon): catch special case of PQ and synthesize color encoding
|
||||
// in that case
|
||||
int compression_type;
|
||||
png_bytep profile;
|
||||
png_charp name;
|
||||
png_uint_32 proflen = 0;
|
||||
auto ok = png_get_iCCP(png_ptr, info_ptr, &name, &compression_type,
|
||||
&profile, &proflen);
|
||||
if (ok && proflen) {
|
||||
ppf->icc.assign(profile, profile + proflen);
|
||||
have_color = true;
|
||||
have_iccp = true;
|
||||
} else {
|
||||
// TODO(eustas): JXL_WARNING?
|
||||
}
|
||||
} else if (!have_cicp && !have_iccp && id == kId_sRGB) {
|
||||
JXL_RETURN_IF_ERROR(DecodeSRGB(chunk.data() + 8, chunk.size() - 12,
|
||||
&ppf->color_encoding));
|
||||
have_srgb = true;
|
||||
have_color = true;
|
||||
} else if (!have_cicp && !have_srgb && !have_iccp && id == kId_gAMA) {
|
||||
JXL_RETURN_IF_ERROR(DecodeGAMA(chunk.data() + 8, chunk.size() - 12,
|
||||
&ppf->color_encoding));
|
||||
have_color = true;
|
||||
} else if (!have_cicp && !have_srgb && !have_iccp && id == kId_cHRM) {
|
||||
JXL_RETURN_IF_ERROR(DecodeCHRM(chunk.data() + 8, chunk.size() - 12,
|
||||
&ppf->color_encoding));
|
||||
have_color = true;
|
||||
} else if (id == kId_eXIf) {
|
||||
ppf->metadata.exif.resize(chunk.size() - 12);
|
||||
memcpy(ppf->metadata.exif.data(), chunk.data() + 8,
|
||||
chunk.size() - 12);
|
||||
} else if (!isAbc(chunk[4]) || !isAbc(chunk[5]) || !isAbc(chunk[6]) ||
|
||||
!isAbc(chunk[7])) {
|
||||
break;
|
||||
} else {
|
||||
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
|
||||
break;
|
||||
}
|
||||
if (!hasInfo) {
|
||||
chunksInfo.push_back(chunk);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JXL_RETURN_IF_ERROR(ApplyColorHints(
|
||||
color_hints, have_color, ppf->info.num_color_channels == 1, ppf));
|
||||
}
|
||||
|
||||
if (errorstate) return false;
|
||||
|
||||
bool has_nontrivial_background = false;
|
||||
bool previous_frame_should_be_cleared = false;
|
||||
enum {
|
||||
DISPOSE_OP_NONE = 0,
|
||||
DISPOSE_OP_BACKGROUND = 1,
|
||||
DISPOSE_OP_PREVIOUS = 2,
|
||||
};
|
||||
enum {
|
||||
BLEND_OP_SOURCE = 0,
|
||||
BLEND_OP_OVER = 1,
|
||||
};
|
||||
for (size_t i = 0; i < frames.size(); i++) {
|
||||
auto& frame = frames[i];
|
||||
JXL_ASSERT(frame.data.xsize == frame.xsize);
|
||||
JXL_ASSERT(frame.data.ysize == frame.ysize);
|
||||
|
||||
// Before encountering a DISPOSE_OP_NONE frame, the canvas is filled with 0,
|
||||
// so DISPOSE_OP_BACKGROUND and DISPOSE_OP_PREVIOUS are equivalent.
|
||||
if (frame.dispose_op == DISPOSE_OP_NONE) {
|
||||
has_nontrivial_background = true;
|
||||
}
|
||||
bool should_blend = frame.blend_op == BLEND_OP_OVER;
|
||||
bool use_for_next_frame =
|
||||
has_nontrivial_background && frame.dispose_op != DISPOSE_OP_PREVIOUS;
|
||||
size_t x0 = frame.x0;
|
||||
size_t y0 = frame.y0;
|
||||
size_t xsize = frame.data.xsize;
|
||||
size_t ysize = frame.data.ysize;
|
||||
if (previous_frame_should_be_cleared) {
|
||||
size_t px0 = frames[i - 1].x0;
|
||||
size_t py0 = frames[i - 1].y0;
|
||||
size_t pxs = frames[i - 1].xsize;
|
||||
size_t pys = frames[i - 1].ysize;
|
||||
if (px0 >= x0 && py0 >= y0 && px0 + pxs <= x0 + xsize &&
|
||||
py0 + pys <= y0 + ysize && frame.blend_op == BLEND_OP_SOURCE &&
|
||||
use_for_next_frame) {
|
||||
// If the previous frame is entirely contained in the current frame and
|
||||
// we are using BLEND_OP_SOURCE, nothing special needs to be done.
|
||||
ppf->frames.emplace_back(std::move(frame.data));
|
||||
} else if (px0 == x0 && py0 == y0 && px0 + pxs == x0 + xsize &&
|
||||
py0 + pys == y0 + ysize && use_for_next_frame) {
|
||||
// If the new frame has the same size as the old one, but we are
|
||||
// blending, we can instead just not blend.
|
||||
should_blend = false;
|
||||
ppf->frames.emplace_back(std::move(frame.data));
|
||||
} else if (px0 <= x0 && py0 <= y0 && px0 + pxs >= x0 + xsize &&
|
||||
py0 + pys >= y0 + ysize && use_for_next_frame) {
|
||||
// If the new frame is contained within the old frame, we can pad the
|
||||
// new frame with zeros and not blend.
|
||||
PackedImage new_data(pxs, pys, frame.data.format);
|
||||
memset(new_data.pixels(), 0, new_data.pixels_size);
|
||||
for (size_t y = 0; y < ysize; y++) {
|
||||
size_t bytes_per_pixel =
|
||||
PackedImage::BitsPerChannel(new_data.format.data_type) *
|
||||
new_data.format.num_channels / 8;
|
||||
memcpy(static_cast<uint8_t*>(new_data.pixels()) +
|
||||
new_data.stride * (y + y0 - py0) +
|
||||
bytes_per_pixel * (x0 - px0),
|
||||
static_cast<const uint8_t*>(frame.data.pixels()) +
|
||||
frame.data.stride * y,
|
||||
xsize * bytes_per_pixel);
|
||||
}
|
||||
|
||||
x0 = px0;
|
||||
y0 = py0;
|
||||
xsize = pxs;
|
||||
ysize = pys;
|
||||
should_blend = false;
|
||||
ppf->frames.emplace_back(std::move(new_data));
|
||||
} else {
|
||||
// If all else fails, insert a dummy blank frame with kReplace.
|
||||
PackedImage blank(pxs, pys, frame.data.format);
|
||||
memset(blank.pixels(), 0, blank.pixels_size);
|
||||
ppf->frames.emplace_back(std::move(blank));
|
||||
auto& pframe = ppf->frames.back();
|
||||
pframe.frame_info.layer_info.crop_x0 = px0;
|
||||
pframe.frame_info.layer_info.crop_y0 = py0;
|
||||
pframe.frame_info.layer_info.xsize = pxs;
|
||||
pframe.frame_info.layer_info.ysize = pys;
|
||||
pframe.frame_info.duration = 0;
|
||||
bool is_full_size = px0 == 0 && py0 == 0 && pxs == ppf->info.xsize &&
|
||||
pys == ppf->info.ysize;
|
||||
pframe.frame_info.layer_info.have_crop = is_full_size ? 0 : 1;
|
||||
pframe.frame_info.layer_info.blend_info.blendmode = JXL_BLEND_REPLACE;
|
||||
pframe.frame_info.layer_info.blend_info.source = 1;
|
||||
pframe.frame_info.layer_info.save_as_reference = 1;
|
||||
ppf->frames.emplace_back(std::move(frame.data));
|
||||
}
|
||||
} else {
|
||||
ppf->frames.emplace_back(std::move(frame.data));
|
||||
}
|
||||
|
||||
auto& pframe = ppf->frames.back();
|
||||
pframe.frame_info.layer_info.crop_x0 = x0;
|
||||
pframe.frame_info.layer_info.crop_y0 = y0;
|
||||
pframe.frame_info.layer_info.xsize = xsize;
|
||||
pframe.frame_info.layer_info.ysize = ysize;
|
||||
pframe.frame_info.duration = frame.duration;
|
||||
pframe.frame_info.layer_info.blend_info.blendmode =
|
||||
should_blend ? JXL_BLEND_BLEND : JXL_BLEND_REPLACE;
|
||||
bool is_full_size = x0 == 0 && y0 == 0 && xsize == ppf->info.xsize &&
|
||||
ysize == ppf->info.ysize;
|
||||
pframe.frame_info.layer_info.have_crop = is_full_size ? 0 : 1;
|
||||
pframe.frame_info.layer_info.blend_info.source = 1;
|
||||
pframe.frame_info.layer_info.blend_info.alpha = 0;
|
||||
pframe.frame_info.layer_info.save_as_reference = use_for_next_frame ? 1 : 0;
|
||||
|
||||
previous_frame_should_be_cleared =
|
||||
has_nontrivial_background && frame.dispose_op == DISPOSE_OP_BACKGROUND;
|
||||
}
|
||||
if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded");
|
||||
ppf->frames.back().frame_info.is_last = true;
|
||||
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
36
third-party/libjxl/libjxl/lib/extras/dec/apng.h
vendored
Normal file
36
third-party/libjxl/libjxl/lib/extras/dec/apng.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_DEC_APNG_H_
|
||||
#define LIB_EXTRAS_DEC_APNG_H_
|
||||
|
||||
// Decodes APNG images in memory.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/extras/dec/color_hints.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
struct SizeConstraints;
|
||||
|
||||
namespace extras {
|
||||
|
||||
bool CanDecodeAPNG();
|
||||
|
||||
// Decodes `bytes` into `ppf`.
|
||||
Status DecodeImageAPNG(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||
PackedPixelFile* ppf,
|
||||
const SizeConstraints* constraints = nullptr);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_DEC_APNG_H_
|
218
third-party/libjxl/libjxl/lib/extras/dec/color_description.cc
vendored
Normal file
218
third-party/libjxl/libjxl/lib/extras/dec/color_description.cc
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/dec/color_description.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace jxl {
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
struct EnumName {
|
||||
const char* name;
|
||||
T value;
|
||||
};
|
||||
|
||||
const EnumName<JxlColorSpace> kJxlColorSpaceNames[] = {
|
||||
{"RGB", JXL_COLOR_SPACE_RGB},
|
||||
{"Gra", JXL_COLOR_SPACE_GRAY},
|
||||
{"XYB", JXL_COLOR_SPACE_XYB},
|
||||
{"CS?", JXL_COLOR_SPACE_UNKNOWN},
|
||||
};
|
||||
|
||||
const EnumName<JxlWhitePoint> kJxlWhitePointNames[] = {
|
||||
{"D65", JXL_WHITE_POINT_D65},
|
||||
{"Cst", JXL_WHITE_POINT_CUSTOM},
|
||||
{"EER", JXL_WHITE_POINT_E},
|
||||
{"DCI", JXL_WHITE_POINT_DCI},
|
||||
};
|
||||
|
||||
const EnumName<JxlPrimaries> kJxlPrimariesNames[] = {
|
||||
{"SRG", JXL_PRIMARIES_SRGB},
|
||||
{"Cst", JXL_PRIMARIES_CUSTOM},
|
||||
{"202", JXL_PRIMARIES_2100},
|
||||
{"DCI", JXL_PRIMARIES_P3},
|
||||
};
|
||||
|
||||
const EnumName<JxlTransferFunction> kJxlTransferFunctionNames[] = {
|
||||
{"709", JXL_TRANSFER_FUNCTION_709},
|
||||
{"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN},
|
||||
{"Lin", JXL_TRANSFER_FUNCTION_LINEAR},
|
||||
{"SRG", JXL_TRANSFER_FUNCTION_SRGB},
|
||||
{"PeQ", JXL_TRANSFER_FUNCTION_PQ},
|
||||
{"DCI", JXL_TRANSFER_FUNCTION_DCI},
|
||||
{"HLG", JXL_TRANSFER_FUNCTION_HLG},
|
||||
{"", JXL_TRANSFER_FUNCTION_GAMMA},
|
||||
};
|
||||
|
||||
const EnumName<JxlRenderingIntent> kJxlRenderingIntentNames[] = {
|
||||
{"Per", JXL_RENDERING_INTENT_PERCEPTUAL},
|
||||
{"Rel", JXL_RENDERING_INTENT_RELATIVE},
|
||||
{"Sat", JXL_RENDERING_INTENT_SATURATION},
|
||||
{"Abs", JXL_RENDERING_INTENT_ABSOLUTE},
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Status ParseEnum(const std::string& token, const EnumName<T>* enum_values,
|
||||
size_t enum_len, T* value) {
|
||||
for (size_t i = 0; i < enum_len; i++) {
|
||||
if (enum_values[i].name == token) {
|
||||
*value = enum_values[i].value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
|
||||
#define PARSE_ENUM(type, token, value) \
|
||||
ParseEnum<type>(token, k##type##Names, ARRAY_SIZE(k##type##Names), value)
|
||||
|
||||
class Tokenizer {
|
||||
public:
|
||||
Tokenizer(const std::string* input, char separator)
|
||||
: input_(input), separator_(separator) {}
|
||||
|
||||
Status Next(std::string* next) {
|
||||
const size_t end = input_->find(separator_, start_);
|
||||
if (end == std::string::npos) {
|
||||
*next = input_->substr(start_); // rest of string
|
||||
} else {
|
||||
*next = input_->substr(start_, end - start_);
|
||||
}
|
||||
if (next->empty()) return JXL_FAILURE("Missing token");
|
||||
start_ = end + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string* const input_; // not owned
|
||||
const char separator_;
|
||||
size_t start_ = 0; // of next token
|
||||
};
|
||||
|
||||
Status ParseDouble(const std::string& num, double* d) {
|
||||
char* end;
|
||||
errno = 0;
|
||||
*d = strtod(num.c_str(), &end);
|
||||
if (*d == 0.0 && end == num.c_str()) {
|
||||
return JXL_FAILURE("Invalid double: %s", num.c_str());
|
||||
}
|
||||
if (std::isnan(*d)) {
|
||||
return JXL_FAILURE("Invalid double: %s", num.c_str());
|
||||
}
|
||||
if (errno == ERANGE) {
|
||||
return JXL_FAILURE("Double out of range: %s", num.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ParseDouble(Tokenizer* tokenizer, double* d) {
|
||||
std::string num;
|
||||
JXL_RETURN_IF_ERROR(tokenizer->Next(&num));
|
||||
return ParseDouble(num, d);
|
||||
}
|
||||
|
||||
Status ParseColorSpace(Tokenizer* tokenizer, JxlColorEncoding* c) {
|
||||
std::string str;
|
||||
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
|
||||
JxlColorSpace cs;
|
||||
if (PARSE_ENUM(JxlColorSpace, str, &cs)) {
|
||||
c->color_space = cs;
|
||||
return true;
|
||||
}
|
||||
|
||||
return JXL_FAILURE("Unknown ColorSpace %s", str.c_str());
|
||||
}
|
||||
|
||||
Status ParseWhitePoint(Tokenizer* tokenizer, JxlColorEncoding* c) {
|
||||
if (c->color_space == JXL_COLOR_SPACE_XYB) {
|
||||
// Implicit white point.
|
||||
c->white_point = JXL_WHITE_POINT_D65;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string str;
|
||||
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
|
||||
if (PARSE_ENUM(JxlWhitePoint, str, &c->white_point)) return true;
|
||||
|
||||
Tokenizer xy_tokenizer(&str, ';');
|
||||
c->white_point = JXL_WHITE_POINT_CUSTOM;
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->white_point_xy + 0));
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->white_point_xy + 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) {
|
||||
if (c->color_space == JXL_COLOR_SPACE_GRAY ||
|
||||
c->color_space == JXL_COLOR_SPACE_XYB) {
|
||||
// No primaries case.
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string str;
|
||||
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
|
||||
if (PARSE_ENUM(JxlPrimaries, str, &c->primaries)) return true;
|
||||
|
||||
Tokenizer xy_tokenizer(&str, ';');
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 0));
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 1));
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_green_xy + 0));
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_green_xy + 1));
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_blue_xy + 0));
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_blue_xy + 1));
|
||||
c->primaries = JXL_PRIMARIES_CUSTOM;
|
||||
|
||||
return JXL_FAILURE("Invalid primaries %s", str.c_str());
|
||||
}
|
||||
|
||||
Status ParseRenderingIntent(Tokenizer* tokenizer, JxlColorEncoding* c) {
|
||||
std::string str;
|
||||
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
|
||||
if (PARSE_ENUM(JxlRenderingIntent, str, &c->rendering_intent)) return true;
|
||||
|
||||
return JXL_FAILURE("Invalid RenderingIntent %s\n", str.c_str());
|
||||
}
|
||||
|
||||
Status ParseTransferFunction(Tokenizer* tokenizer, JxlColorEncoding* c) {
|
||||
if (c->color_space == JXL_COLOR_SPACE_XYB) {
|
||||
// Implicit TF.
|
||||
c->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
|
||||
c->gamma = 1 / 3.;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string str;
|
||||
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
|
||||
if (PARSE_ENUM(JxlTransferFunction, str, &c->transfer_function)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (str[0] == 'g') {
|
||||
JXL_RETURN_IF_ERROR(ParseDouble(str.substr(1), &c->gamma));
|
||||
c->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
|
||||
return true;
|
||||
}
|
||||
|
||||
return JXL_FAILURE("Invalid gamma %s", str.c_str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status ParseDescription(const std::string& description, JxlColorEncoding* c) {
|
||||
*c = {};
|
||||
Tokenizer tokenizer(&description, '_');
|
||||
JXL_RETURN_IF_ERROR(ParseColorSpace(&tokenizer, c));
|
||||
JXL_RETURN_IF_ERROR(ParseWhitePoint(&tokenizer, c));
|
||||
JXL_RETURN_IF_ERROR(ParsePrimaries(&tokenizer, c));
|
||||
JXL_RETURN_IF_ERROR(ParseRenderingIntent(&tokenizer, c));
|
||||
JXL_RETURN_IF_ERROR(ParseTransferFunction(&tokenizer, c));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace jxl
|
23
third-party/libjxl/libjxl/lib/extras/dec/color_description.h
vendored
Normal file
23
third-party/libjxl/libjxl/lib/extras/dec/color_description.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_COLOR_DESCRIPTION_H_
|
||||
#define LIB_EXTRAS_COLOR_DESCRIPTION_H_
|
||||
|
||||
#include <jxl/color_encoding.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
// Parse the color description into a JxlColorEncoding "RGB_D65_SRG_Rel_Lin".
|
||||
Status ParseDescription(const std::string& description,
|
||||
JxlColorEncoding* JXL_RESTRICT c);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_COLOR_DESCRIPTION_H_
|
38
third-party/libjxl/libjxl/lib/extras/dec/color_description_test.cc
vendored
Normal file
38
third-party/libjxl/libjxl/lib/extras/dec/color_description_test.cc
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/dec/color_description.h"
|
||||
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/jxl/test_utils.h"
|
||||
#include "lib/jxl/testing.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
// Verify ParseDescription(Description) yields the same ColorEncoding
|
||||
TEST(ColorDescriptionTest, RoundTripAll) {
|
||||
for (const auto& cdesc : test::AllEncodings()) {
|
||||
const ColorEncoding c_original = test::ColorEncodingFromDescriptor(cdesc);
|
||||
const std::string description = Description(c_original);
|
||||
printf("%s\n", description.c_str());
|
||||
|
||||
JxlColorEncoding c_external = {};
|
||||
EXPECT_TRUE(ParseDescription(description, &c_external));
|
||||
ColorEncoding c_internal;
|
||||
EXPECT_TRUE(
|
||||
ConvertExternalToInternalColorEncoding(c_external, &c_internal));
|
||||
EXPECT_TRUE(c_original.SameColorEncoding(c_internal))
|
||||
<< "Where c_original=" << c_original
|
||||
<< " and c_internal=" << c_internal;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ColorDescriptionTest, NanGamma) {
|
||||
const std::string description = "Gra_2_Per_gnan";
|
||||
JxlColorEncoding c;
|
||||
EXPECT_FALSE(ParseDescription(description, &c));
|
||||
}
|
||||
|
||||
} // namespace jxl
|
78
third-party/libjxl/libjxl/lib/extras/dec/color_hints.cc
vendored
Normal file
78
third-party/libjxl/libjxl/lib/extras/dec/color_hints.cc
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/dec/color_hints.h"
|
||||
|
||||
#include <jxl/encode.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/dec/color_description.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
Status ApplyColorHints(const ColorHints& color_hints,
|
||||
const bool color_already_set, const bool is_gray,
|
||||
PackedPixelFile* ppf) {
|
||||
bool got_color_space = color_already_set;
|
||||
|
||||
JXL_RETURN_IF_ERROR(color_hints.Foreach(
|
||||
[color_already_set, is_gray, ppf, &got_color_space](
|
||||
const std::string& key, const std::string& value) -> Status {
|
||||
if (color_already_set && (key == "color_space" || key == "icc")) {
|
||||
JXL_WARNING("Decoder ignoring %s hint", key.c_str());
|
||||
return true;
|
||||
}
|
||||
if (key == "color_space") {
|
||||
JxlColorEncoding c_original_external;
|
||||
if (!ParseDescription(value, &c_original_external)) {
|
||||
return JXL_FAILURE("Failed to apply color_space");
|
||||
}
|
||||
ppf->color_encoding = c_original_external;
|
||||
|
||||
if (is_gray !=
|
||||
(ppf->color_encoding.color_space == JXL_COLOR_SPACE_GRAY)) {
|
||||
return JXL_FAILURE("mismatch between file and color_space hint");
|
||||
}
|
||||
|
||||
got_color_space = true;
|
||||
} else if (key == "icc") {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
|
||||
std::vector<uint8_t> icc(data, data + value.size());
|
||||
ppf->icc.swap(icc);
|
||||
got_color_space = true;
|
||||
} else if (key == "exif") {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
|
||||
std::vector<uint8_t> blob(data, data + value.size());
|
||||
ppf->metadata.exif.swap(blob);
|
||||
} else if (key == "xmp") {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
|
||||
std::vector<uint8_t> blob(data, data + value.size());
|
||||
ppf->metadata.xmp.swap(blob);
|
||||
} else if (key == "jumbf") {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
|
||||
std::vector<uint8_t> blob(data, data + value.size());
|
||||
ppf->metadata.jumbf.swap(blob);
|
||||
} else {
|
||||
JXL_WARNING("Ignoring %s hint", key.c_str());
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
|
||||
if (!got_color_space) {
|
||||
ppf->color_encoding.color_space =
|
||||
is_gray ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB;
|
||||
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
|
||||
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
|
||||
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
74
third-party/libjxl/libjxl/lib/extras/dec/color_hints.h
vendored
Normal file
74
third-party/libjxl/libjxl/lib/extras/dec/color_hints.h
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_COLOR_HINTS_H_
|
||||
#define LIB_EXTRAS_COLOR_HINTS_H_
|
||||
|
||||
// Not all the formats implemented in the extras lib support bundling color
|
||||
// information into the file, and those that support it may not have it.
|
||||
// To allow attaching color information to those file formats the caller can
|
||||
// define these color hints.
|
||||
// Besides color space information, 'ColorHints' may also include other
|
||||
// additional information such as Exif, XMP and JUMBF metadata.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
class ColorHints {
|
||||
public:
|
||||
// key=color_space, value=Description(c/pp): specify the ColorEncoding of
|
||||
// the pixels for decoding. Otherwise, if the codec did not obtain an ICC
|
||||
// profile from the image, assume sRGB.
|
||||
//
|
||||
// Strings are taken from the command line, so avoid spaces for convenience.
|
||||
void Add(const std::string& key, const std::string& value) {
|
||||
kv_.emplace_back(key, value);
|
||||
}
|
||||
|
||||
// Calls `func(key, value)` for each key/value in the order they were added,
|
||||
// returning false immediately if `func` returns false.
|
||||
template <class Func>
|
||||
Status Foreach(const Func& func) const {
|
||||
for (const KeyValue& kv : kv_) {
|
||||
Status ok = func(kv.key, kv.value);
|
||||
if (!ok) {
|
||||
return JXL_FAILURE("ColorHints::Foreach returned false");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Splitting into key/value avoids parsing in each codec.
|
||||
struct KeyValue {
|
||||
KeyValue(std::string key, std::string value)
|
||||
: key(std::move(key)), value(std::move(value)) {}
|
||||
|
||||
std::string key;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
std::vector<KeyValue> kv_;
|
||||
};
|
||||
|
||||
// Apply the color hints to the decoded image in PackedPixelFile if any.
|
||||
// color_already_set tells whether the color encoding was already set, in which
|
||||
// case the hints are ignored if any hint is passed.
|
||||
Status ApplyColorHints(const ColorHints& color_hints, bool color_already_set,
|
||||
bool is_gray, PackedPixelFile* ppf);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_COLOR_HINTS_H_
|
156
third-party/libjxl/libjxl/lib/extras/dec/decode.cc
vendored
Normal file
156
third-party/libjxl/libjxl/lib/extras/dec/decode.cc
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/dec/decode.h"
|
||||
|
||||
#include <locale>
|
||||
|
||||
#include "lib/extras/dec/apng.h"
|
||||
#include "lib/extras/dec/exr.h"
|
||||
#include "lib/extras/dec/gif.h"
|
||||
#include "lib/extras/dec/jpg.h"
|
||||
#include "lib/extras/dec/jxl.h"
|
||||
#include "lib/extras/dec/pgx.h"
|
||||
#include "lib/extras/dec/pnm.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
namespace {
|
||||
|
||||
// Any valid encoding is larger (ensures codecs can read the first few bytes)
|
||||
constexpr size_t kMinBytes = 9;
|
||||
|
||||
void BasenameAndExtension(std::string path, std::string* basename,
|
||||
std::string* extension) {
|
||||
// Pattern: file.jxl
|
||||
size_t pos = path.find_last_of('.');
|
||||
if (pos < path.size()) {
|
||||
*basename = path.substr(0, pos);
|
||||
*extension = path.substr(pos);
|
||||
return;
|
||||
}
|
||||
// Pattern: jxl:-
|
||||
pos = path.find_first_of(':');
|
||||
if (pos < path.size()) {
|
||||
*basename = path.substr(pos + 1);
|
||||
*extension = "." + path.substr(0, pos);
|
||||
return;
|
||||
}
|
||||
// Extension not found
|
||||
*basename = path;
|
||||
*extension = "";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Codec CodecFromPath(std::string path, size_t* JXL_RESTRICT bits_per_sample,
|
||||
std::string* basename, std::string* extension) {
|
||||
std::string base;
|
||||
std::string ext;
|
||||
BasenameAndExtension(path, &base, &ext);
|
||||
if (basename) *basename = base;
|
||||
if (extension) *extension = ext;
|
||||
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) {
|
||||
return std::tolower(c, std::locale::classic());
|
||||
});
|
||||
if (ext == ".png") return Codec::kPNG;
|
||||
|
||||
if (ext == ".jpg") return Codec::kJPG;
|
||||
if (ext == ".jpeg") return Codec::kJPG;
|
||||
|
||||
if (ext == ".pgx") return Codec::kPGX;
|
||||
|
||||
if (ext == ".pam") return Codec::kPNM;
|
||||
if (ext == ".pnm") return Codec::kPNM;
|
||||
if (ext == ".pgm") return Codec::kPNM;
|
||||
if (ext == ".ppm") return Codec::kPNM;
|
||||
if (ext == ".pfm") {
|
||||
if (bits_per_sample != nullptr) *bits_per_sample = 32;
|
||||
return Codec::kPNM;
|
||||
}
|
||||
|
||||
if (ext == ".gif") return Codec::kGIF;
|
||||
|
||||
if (ext == ".exr") return Codec::kEXR;
|
||||
|
||||
return Codec::kUnknown;
|
||||
}
|
||||
|
||||
bool CanDecode(Codec codec) {
|
||||
switch (codec) {
|
||||
case Codec::kEXR:
|
||||
return CanDecodeEXR();
|
||||
case Codec::kGIF:
|
||||
return CanDecodeGIF();
|
||||
case Codec::kJPG:
|
||||
return CanDecodeJPG();
|
||||
case Codec::kPNG:
|
||||
return CanDecodeAPNG();
|
||||
case Codec::kPNM:
|
||||
case Codec::kPGX:
|
||||
case Codec::kJXL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Status DecodeBytes(const Span<const uint8_t> bytes,
|
||||
const ColorHints& color_hints, extras::PackedPixelFile* ppf,
|
||||
const SizeConstraints* constraints, Codec* orig_codec) {
|
||||
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
|
||||
|
||||
*ppf = extras::PackedPixelFile();
|
||||
|
||||
// Default values when not set by decoders.
|
||||
ppf->info.uses_original_profile = true;
|
||||
ppf->info.orientation = JXL_ORIENT_IDENTITY;
|
||||
|
||||
const auto choose_codec = [&]() -> Codec {
|
||||
if (DecodeImageAPNG(bytes, color_hints, ppf, constraints)) {
|
||||
return Codec::kPNG;
|
||||
}
|
||||
if (DecodeImagePGX(bytes, color_hints, ppf, constraints)) {
|
||||
return Codec::kPGX;
|
||||
}
|
||||
if (DecodeImagePNM(bytes, color_hints, ppf, constraints)) {
|
||||
return Codec::kPNM;
|
||||
}
|
||||
JXLDecompressParams dparams = {};
|
||||
for (const uint32_t num_channels : {1, 2, 3, 4}) {
|
||||
dparams.accepted_formats.push_back(
|
||||
{num_channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, /*align=*/0});
|
||||
}
|
||||
size_t decoded_bytes;
|
||||
if (DecodeImageJXL(bytes.data(), bytes.size(), dparams, &decoded_bytes,
|
||||
ppf) &&
|
||||
ApplyColorHints(color_hints, true, ppf->info.num_color_channels == 1,
|
||||
ppf)) {
|
||||
return Codec::kJXL;
|
||||
}
|
||||
if (DecodeImageGIF(bytes, color_hints, ppf, constraints)) {
|
||||
return Codec::kGIF;
|
||||
}
|
||||
if (DecodeImageJPG(bytes, color_hints, ppf, constraints)) {
|
||||
return Codec::kJPG;
|
||||
}
|
||||
if (DecodeImageEXR(bytes, color_hints, ppf, constraints)) {
|
||||
return Codec::kEXR;
|
||||
}
|
||||
return Codec::kUnknown;
|
||||
};
|
||||
|
||||
Codec codec = choose_codec();
|
||||
if (codec == Codec::kUnknown) {
|
||||
return JXL_FAILURE("Codecs failed to decode");
|
||||
}
|
||||
if (orig_codec) *orig_codec = codec;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
58
third-party/libjxl/libjxl/lib/extras/dec/decode.h
vendored
Normal file
58
third-party/libjxl/libjxl/lib/extras/dec/decode.h
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_DEC_DECODE_H_
|
||||
#define LIB_EXTRAS_DEC_DECODE_H_
|
||||
|
||||
// Facade for image decoders (PNG, PNM, ...).
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/dec/color_hints.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
struct SizeConstraints;
|
||||
|
||||
namespace extras {
|
||||
|
||||
// Codecs supported by DecodeBytes.
|
||||
enum class Codec : uint32_t {
|
||||
kUnknown, // for CodecFromPath
|
||||
kPNG,
|
||||
kPNM,
|
||||
kPGX,
|
||||
kJPG,
|
||||
kGIF,
|
||||
kEXR,
|
||||
kJXL
|
||||
};
|
||||
|
||||
bool CanDecode(Codec codec);
|
||||
|
||||
// If and only if extension is ".pfm", *bits_per_sample is updated to 32 so
|
||||
// that Encode() would encode to PFM instead of PPM.
|
||||
Codec CodecFromPath(std::string path,
|
||||
size_t* JXL_RESTRICT bits_per_sample = nullptr,
|
||||
std::string* basename = nullptr,
|
||||
std::string* extension = nullptr);
|
||||
|
||||
// Decodes "bytes" info *ppf.
|
||||
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
|
||||
Status DecodeBytes(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||
extras::PackedPixelFile* ppf,
|
||||
const SizeConstraints* constraints = nullptr,
|
||||
Codec* orig_codec = nullptr);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_DEC_DECODE_H_
|
201
third-party/libjxl/libjxl/lib/extras/dec/exr.cc
vendored
Normal file
201
third-party/libjxl/libjxl/lib/extras/dec/exr.cc
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/dec/exr.h"
|
||||
|
||||
#if JPEGXL_ENABLE_EXR
|
||||
#include <ImfChromaticitiesAttribute.h>
|
||||
#include <ImfIO.h>
|
||||
#include <ImfRgbaFile.h>
|
||||
#include <ImfStandardAttributes.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
#if JPEGXL_ENABLE_EXR
|
||||
namespace {
|
||||
|
||||
namespace OpenEXR = OPENEXR_IMF_NAMESPACE;
|
||||
|
||||
// OpenEXR::Int64 is deprecated in favor of using uint64_t directly, but using
|
||||
// uint64_t as recommended causes build failures with previous OpenEXR versions
|
||||
// on macOS, where the definition for OpenEXR::Int64 was actually not equivalent
|
||||
// to uint64_t. This alternative should work in all cases.
|
||||
using ExrInt64 = decltype(std::declval<OpenEXR::IStream>().tellg());
|
||||
|
||||
constexpr int kExrBitsPerSample = 16;
|
||||
constexpr int kExrAlphaBits = 16;
|
||||
|
||||
class InMemoryIStream : public OpenEXR::IStream {
|
||||
public:
|
||||
// The data pointed to by `bytes` must outlive the InMemoryIStream.
|
||||
explicit InMemoryIStream(const Span<const uint8_t> bytes)
|
||||
: IStream(/*fileName=*/""), bytes_(bytes) {}
|
||||
|
||||
bool isMemoryMapped() const override { return true; }
|
||||
char* readMemoryMapped(const int n) override {
|
||||
JXL_ASSERT(pos_ + n <= bytes_.size());
|
||||
char* const result =
|
||||
const_cast<char*>(reinterpret_cast<const char*>(bytes_.data() + pos_));
|
||||
pos_ += n;
|
||||
return result;
|
||||
}
|
||||
bool read(char c[], const int n) override {
|
||||
std::copy_n(readMemoryMapped(n), n, c);
|
||||
return pos_ < bytes_.size();
|
||||
}
|
||||
|
||||
ExrInt64 tellg() override { return pos_; }
|
||||
void seekg(const ExrInt64 pos) override {
|
||||
JXL_ASSERT(pos + 1 <= bytes_.size());
|
||||
pos_ = pos;
|
||||
}
|
||||
|
||||
private:
|
||||
const Span<const uint8_t> bytes_;
|
||||
size_t pos_ = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
bool CanDecodeEXR() {
|
||||
#if JPEGXL_ENABLE_EXR
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||
PackedPixelFile* ppf,
|
||||
const SizeConstraints* constraints) {
|
||||
#if JPEGXL_ENABLE_EXR
|
||||
InMemoryIStream is(bytes);
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
std::unique_ptr<OpenEXR::RgbaInputFile> input_ptr;
|
||||
try {
|
||||
input_ptr.reset(new OpenEXR::RgbaInputFile(is));
|
||||
} catch (...) {
|
||||
// silently return false if it is not an EXR file
|
||||
return false;
|
||||
}
|
||||
OpenEXR::RgbaInputFile& input = *input_ptr;
|
||||
#else
|
||||
OpenEXR::RgbaInputFile input(is);
|
||||
#endif
|
||||
|
||||
if ((input.channels() & OpenEXR::RgbaChannels::WRITE_RGB) !=
|
||||
OpenEXR::RgbaChannels::WRITE_RGB) {
|
||||
return JXL_FAILURE("only RGB OpenEXR files are supported");
|
||||
}
|
||||
const bool has_alpha = (input.channels() & OpenEXR::RgbaChannels::WRITE_A) ==
|
||||
OpenEXR::RgbaChannels::WRITE_A;
|
||||
|
||||
const float intensity_target = OpenEXR::hasWhiteLuminance(input.header())
|
||||
? OpenEXR::whiteLuminance(input.header())
|
||||
: 0;
|
||||
|
||||
auto image_size = input.displayWindow().size();
|
||||
// Size is computed as max - min, but both bounds are inclusive.
|
||||
++image_size.x;
|
||||
++image_size.y;
|
||||
|
||||
ppf->info.xsize = image_size.x;
|
||||
ppf->info.ysize = image_size.y;
|
||||
ppf->info.num_color_channels = 3;
|
||||
|
||||
const JxlDataType data_type =
|
||||
kExrBitsPerSample == 16 ? JXL_TYPE_FLOAT16 : JXL_TYPE_FLOAT;
|
||||
const JxlPixelFormat format{
|
||||
/*num_channels=*/3u + (has_alpha ? 1u : 0u),
|
||||
/*data_type=*/data_type,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0,
|
||||
};
|
||||
ppf->frames.clear();
|
||||
// Allocates the frame buffer.
|
||||
ppf->frames.emplace_back(image_size.x, image_size.y, format);
|
||||
const auto& frame = ppf->frames.back();
|
||||
|
||||
const int row_size = input.dataWindow().size().x + 1;
|
||||
// Number of rows to read at a time.
|
||||
// https://www.openexr.com/documentation/ReadingAndWritingImageFiles.pdf
|
||||
// recommends reading the whole file at once.
|
||||
const int y_chunk_size = input.displayWindow().size().y + 1;
|
||||
std::vector<OpenEXR::Rgba> input_rows(row_size * y_chunk_size);
|
||||
for (int start_y =
|
||||
std::max(input.dataWindow().min.y, input.displayWindow().min.y);
|
||||
start_y <=
|
||||
std::min(input.dataWindow().max.y, input.displayWindow().max.y);
|
||||
start_y += y_chunk_size) {
|
||||
// Inclusive.
|
||||
const int end_y = std::min(
|
||||
start_y + y_chunk_size - 1,
|
||||
std::min(input.dataWindow().max.y, input.displayWindow().max.y));
|
||||
input.setFrameBuffer(
|
||||
input_rows.data() - input.dataWindow().min.x - start_y * row_size,
|
||||
/*xStride=*/1, /*yStride=*/row_size);
|
||||
input.readPixels(start_y, end_y);
|
||||
for (int exr_y = start_y; exr_y <= end_y; ++exr_y) {
|
||||
const int image_y = exr_y - input.displayWindow().min.y;
|
||||
const OpenEXR::Rgba* const JXL_RESTRICT input_row =
|
||||
&input_rows[(exr_y - start_y) * row_size];
|
||||
uint8_t* row = static_cast<uint8_t*>(frame.color.pixels()) +
|
||||
frame.color.stride * image_y;
|
||||
const uint32_t pixel_size =
|
||||
(3 + (has_alpha ? 1 : 0)) * kExrBitsPerSample / 8;
|
||||
for (int exr_x =
|
||||
std::max(input.dataWindow().min.x, input.displayWindow().min.x);
|
||||
exr_x <=
|
||||
std::min(input.dataWindow().max.x, input.displayWindow().max.x);
|
||||
++exr_x) {
|
||||
const int image_x = exr_x - input.displayWindow().min.x;
|
||||
// TODO(eustas): UB: OpenEXR::Rgba is not TriviallyCopyable
|
||||
memcpy(row + image_x * pixel_size,
|
||||
input_row + (exr_x - input.dataWindow().min.x), pixel_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
|
||||
ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB;
|
||||
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
|
||||
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
|
||||
if (OpenEXR::hasChromaticities(input.header())) {
|
||||
ppf->color_encoding.primaries = JXL_PRIMARIES_CUSTOM;
|
||||
ppf->color_encoding.white_point = JXL_WHITE_POINT_CUSTOM;
|
||||
const auto& chromaticities = OpenEXR::chromaticities(input.header());
|
||||
ppf->color_encoding.primaries_red_xy[0] = chromaticities.red.x;
|
||||
ppf->color_encoding.primaries_red_xy[1] = chromaticities.red.y;
|
||||
ppf->color_encoding.primaries_green_xy[0] = chromaticities.green.x;
|
||||
ppf->color_encoding.primaries_green_xy[1] = chromaticities.green.y;
|
||||
ppf->color_encoding.primaries_blue_xy[0] = chromaticities.blue.x;
|
||||
ppf->color_encoding.primaries_blue_xy[1] = chromaticities.blue.y;
|
||||
ppf->color_encoding.white_point_xy[0] = chromaticities.white.x;
|
||||
ppf->color_encoding.white_point_xy[1] = chromaticities.white.y;
|
||||
}
|
||||
|
||||
// EXR uses binary16 or binary32 floating point format.
|
||||
ppf->info.bits_per_sample = kExrBitsPerSample;
|
||||
ppf->info.exponent_bits_per_sample = kExrBitsPerSample == 16 ? 5 : 8;
|
||||
if (has_alpha) {
|
||||
ppf->info.alpha_bits = kExrAlphaBits;
|
||||
ppf->info.alpha_exponent_bits = ppf->info.exponent_bits_per_sample;
|
||||
ppf->info.alpha_premultiplied = true;
|
||||
}
|
||||
ppf->info.intensity_target = intensity_target;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
34
third-party/libjxl/libjxl/lib/extras/dec/exr.h
vendored
Normal file
34
third-party/libjxl/libjxl/lib/extras/dec/exr.h
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_DEC_EXR_H_
|
||||
#define LIB_EXTRAS_DEC_EXR_H_
|
||||
|
||||
// Decodes OpenEXR images in memory.
|
||||
|
||||
#include "lib/extras/dec/color_hints.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
struct SizeConstraints;
|
||||
|
||||
namespace extras {
|
||||
|
||||
bool CanDecodeEXR();
|
||||
|
||||
// Decodes `bytes` into `ppf`. color_hints are ignored.
|
||||
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||
PackedPixelFile* ppf,
|
||||
const SizeConstraints* constraints = nullptr);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_DEC_EXR_H_
|
415
third-party/libjxl/libjxl/lib/extras/dec/gif.cc
vendored
Normal file
415
third-party/libjxl/libjxl/lib/extras/dec/gif.cc
vendored
Normal file
@ -0,0 +1,415 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/dec/gif.h"
|
||||
|
||||
#if JPEGXL_ENABLE_GIF
|
||||
#include <gif_lib.h>
|
||||
#endif
|
||||
#include <jxl/codestream_header.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/size_constraints.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/sanitizers.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
#if JPEGXL_ENABLE_GIF
|
||||
namespace {
|
||||
|
||||
struct ReadState {
|
||||
Span<const uint8_t> bytes;
|
||||
};
|
||||
|
||||
struct DGifCloser {
|
||||
void operator()(GifFileType* const ptr) const { DGifCloseFile(ptr, nullptr); }
|
||||
};
|
||||
using GifUniquePtr = std::unique_ptr<GifFileType, DGifCloser>;
|
||||
|
||||
struct PackedRgba {
|
||||
uint8_t r, g, b, a;
|
||||
};
|
||||
|
||||
struct PackedRgb {
|
||||
uint8_t r, g, b;
|
||||
};
|
||||
|
||||
void ensure_have_alpha(PackedFrame* frame) {
|
||||
if (!frame->extra_channels.empty()) return;
|
||||
const JxlPixelFormat alpha_format{
|
||||
/*num_channels=*/1u,
|
||||
/*data_type=*/JXL_TYPE_UINT8,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0,
|
||||
};
|
||||
frame->extra_channels.emplace_back(frame->color.xsize, frame->color.ysize,
|
||||
alpha_format);
|
||||
// We need to set opaque-by-default.
|
||||
std::fill_n(static_cast<uint8_t*>(frame->extra_channels[0].pixels()),
|
||||
frame->color.xsize * frame->color.ysize, 255u);
|
||||
}
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
bool CanDecodeGIF() {
|
||||
#if JPEGXL_ENABLE_GIF
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||
PackedPixelFile* ppf,
|
||||
const SizeConstraints* constraints) {
|
||||
#if JPEGXL_ENABLE_GIF
|
||||
int error = GIF_OK;
|
||||
ReadState state = {bytes};
|
||||
const auto ReadFromSpan = [](GifFileType* const gif, GifByteType* const bytes,
|
||||
int n) {
|
||||
ReadState* const state = reinterpret_cast<ReadState*>(gif->UserData);
|
||||
// giflib API requires the input size `n` to be signed int.
|
||||
if (static_cast<size_t>(n) > state->bytes.size()) {
|
||||
n = state->bytes.size();
|
||||
}
|
||||
memcpy(bytes, state->bytes.data(), n);
|
||||
state->bytes.remove_prefix(n);
|
||||
return n;
|
||||
};
|
||||
GifUniquePtr gif(DGifOpen(&state, ReadFromSpan, &error));
|
||||
if (gif == nullptr) {
|
||||
if (error == D_GIF_ERR_NOT_GIF_FILE) {
|
||||
// Not an error.
|
||||
return false;
|
||||
} else {
|
||||
return JXL_FAILURE("Failed to read GIF: %s", GifErrorString(error));
|
||||
}
|
||||
}
|
||||
error = DGifSlurp(gif.get());
|
||||
if (error != GIF_OK) {
|
||||
return JXL_FAILURE("Failed to read GIF: %s", GifErrorString(gif->Error));
|
||||
}
|
||||
|
||||
msan::UnpoisonMemory(gif.get(), sizeof(*gif));
|
||||
if (gif->SColorMap) {
|
||||
msan::UnpoisonMemory(gif->SColorMap, sizeof(*gif->SColorMap));
|
||||
msan::UnpoisonMemory(
|
||||
gif->SColorMap->Colors,
|
||||
sizeof(*gif->SColorMap->Colors) * gif->SColorMap->ColorCount);
|
||||
}
|
||||
msan::UnpoisonMemory(gif->SavedImages,
|
||||
sizeof(*gif->SavedImages) * gif->ImageCount);
|
||||
|
||||
JXL_RETURN_IF_ERROR(
|
||||
VerifyDimensions<uint32_t>(constraints, gif->SWidth, gif->SHeight));
|
||||
uint64_t total_pixel_count =
|
||||
static_cast<uint64_t>(gif->SWidth) * gif->SHeight;
|
||||
for (int i = 0; i < gif->ImageCount; ++i) {
|
||||
const SavedImage& image = gif->SavedImages[i];
|
||||
uint32_t w = image.ImageDesc.Width;
|
||||
uint32_t h = image.ImageDesc.Height;
|
||||
JXL_RETURN_IF_ERROR(VerifyDimensions<uint32_t>(constraints, w, h));
|
||||
uint64_t pixel_count = static_cast<uint64_t>(w) * h;
|
||||
if (total_pixel_count + pixel_count < total_pixel_count) {
|
||||
return JXL_FAILURE("Image too big");
|
||||
}
|
||||
total_pixel_count += pixel_count;
|
||||
if (constraints && (total_pixel_count > constraints->dec_max_pixels)) {
|
||||
return JXL_FAILURE("Image too big");
|
||||
}
|
||||
}
|
||||
|
||||
if (!gif->SColorMap) {
|
||||
for (int i = 0; i < gif->ImageCount; ++i) {
|
||||
if (!gif->SavedImages[i].ImageDesc.ColorMap) {
|
||||
return JXL_FAILURE("Missing GIF color map");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gif->ImageCount > 1) {
|
||||
ppf->info.have_animation = true;
|
||||
// Delays in GIF are specified in 100ths of a second.
|
||||
ppf->info.animation.tps_numerator = 100;
|
||||
ppf->info.animation.tps_denominator = 1;
|
||||
}
|
||||
|
||||
ppf->frames.clear();
|
||||
ppf->frames.reserve(gif->ImageCount);
|
||||
|
||||
ppf->info.xsize = gif->SWidth;
|
||||
ppf->info.ysize = gif->SHeight;
|
||||
ppf->info.bits_per_sample = 8;
|
||||
ppf->info.exponent_bits_per_sample = 0;
|
||||
// alpha_bits is later set to 8 if we find a frame with transparent pixels.
|
||||
ppf->info.alpha_bits = 0;
|
||||
ppf->info.alpha_exponent_bits = 0;
|
||||
JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/false,
|
||||
/*is_gray=*/false, ppf));
|
||||
|
||||
ppf->info.num_color_channels = 3;
|
||||
|
||||
// Pixel format for the 'canvas' onto which we paint
|
||||
// the (potentially individually cropped) GIF frames
|
||||
// of an animation.
|
||||
const JxlPixelFormat canvas_format{
|
||||
/*num_channels=*/4u,
|
||||
/*data_type=*/JXL_TYPE_UINT8,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0,
|
||||
};
|
||||
|
||||
// Pixel format for the JXL PackedFrame that goes into the
|
||||
// PackedPixelFile. Here, we use 3 color channels, and provide
|
||||
// the alpha channel as an extra_channel wherever it is used.
|
||||
const JxlPixelFormat packed_frame_format{
|
||||
/*num_channels=*/3u,
|
||||
/*data_type=*/JXL_TYPE_UINT8,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0,
|
||||
};
|
||||
|
||||
GifColorType background_color;
|
||||
if (gif->SColorMap == nullptr ||
|
||||
gif->SBackGroundColor >= gif->SColorMap->ColorCount) {
|
||||
background_color = {0, 0, 0};
|
||||
} else {
|
||||
background_color = gif->SColorMap->Colors[gif->SBackGroundColor];
|
||||
}
|
||||
const PackedRgba background_rgba{background_color.Red, background_color.Green,
|
||||
background_color.Blue, 0};
|
||||
PackedFrame canvas(gif->SWidth, gif->SHeight, canvas_format);
|
||||
std::fill_n(static_cast<PackedRgba*>(canvas.color.pixels()),
|
||||
canvas.color.xsize * canvas.color.ysize, background_rgba);
|
||||
Rect canvas_rect{0, 0, canvas.color.xsize, canvas.color.ysize};
|
||||
|
||||
Rect previous_rect_if_restore_to_background;
|
||||
|
||||
bool replace = true;
|
||||
bool last_base_was_none = true;
|
||||
for (int i = 0; i < gif->ImageCount; ++i) {
|
||||
const SavedImage& image = gif->SavedImages[i];
|
||||
msan::UnpoisonMemory(image.RasterBits, sizeof(*image.RasterBits) *
|
||||
image.ImageDesc.Width *
|
||||
image.ImageDesc.Height);
|
||||
const Rect image_rect(image.ImageDesc.Left, image.ImageDesc.Top,
|
||||
image.ImageDesc.Width, image.ImageDesc.Height);
|
||||
|
||||
Rect total_rect;
|
||||
if (previous_rect_if_restore_to_background.xsize() != 0 ||
|
||||
previous_rect_if_restore_to_background.ysize() != 0) {
|
||||
const size_t xbegin = std::min(
|
||||
image_rect.x0(), previous_rect_if_restore_to_background.x0());
|
||||
const size_t ybegin = std::min(
|
||||
image_rect.y0(), previous_rect_if_restore_to_background.y0());
|
||||
const size_t xend =
|
||||
std::max(image_rect.x0() + image_rect.xsize(),
|
||||
previous_rect_if_restore_to_background.x0() +
|
||||
previous_rect_if_restore_to_background.xsize());
|
||||
const size_t yend =
|
||||
std::max(image_rect.y0() + image_rect.ysize(),
|
||||
previous_rect_if_restore_to_background.y0() +
|
||||
previous_rect_if_restore_to_background.ysize());
|
||||
total_rect = Rect(xbegin, ybegin, xend - xbegin, yend - ybegin);
|
||||
previous_rect_if_restore_to_background = Rect();
|
||||
replace = true;
|
||||
} else {
|
||||
total_rect = image_rect;
|
||||
replace = false;
|
||||
}
|
||||
if (!image_rect.IsInside(canvas_rect)) {
|
||||
return JXL_FAILURE("GIF frame extends outside of the canvas");
|
||||
}
|
||||
|
||||
// Allocates the frame buffer.
|
||||
ppf->frames.emplace_back(total_rect.xsize(), total_rect.ysize(),
|
||||
packed_frame_format);
|
||||
PackedFrame* frame = &ppf->frames.back();
|
||||
|
||||
// We cannot tell right from the start whether there will be a
|
||||
// need for an alpha channel. This is discovered only as soon as
|
||||
// we see a transparent pixel. We hence initialize alpha lazily.
|
||||
auto set_pixel_alpha = [&frame](size_t x, size_t y, uint8_t a) {
|
||||
// If we do not have an alpha-channel and a==255 (fully opaque),
|
||||
// we can skip setting this pixel-value and rely on
|
||||
// "no alpha channel = no transparency".
|
||||
if (a == 255 && !frame->extra_channels.empty()) return;
|
||||
ensure_have_alpha(frame);
|
||||
static_cast<uint8_t*>(
|
||||
frame->extra_channels[0].pixels())[y * frame->color.xsize + x] = a;
|
||||
};
|
||||
|
||||
const ColorMapObject* const color_map =
|
||||
image.ImageDesc.ColorMap ? image.ImageDesc.ColorMap : gif->SColorMap;
|
||||
JXL_CHECK(color_map);
|
||||
msan::UnpoisonMemory(color_map, sizeof(*color_map));
|
||||
msan::UnpoisonMemory(color_map->Colors,
|
||||
sizeof(*color_map->Colors) * color_map->ColorCount);
|
||||
GraphicsControlBlock gcb;
|
||||
DGifSavedExtensionToGCB(gif.get(), i, &gcb);
|
||||
msan::UnpoisonMemory(&gcb, sizeof(gcb));
|
||||
bool is_full_size = total_rect.x0() == 0 && total_rect.y0() == 0 &&
|
||||
total_rect.xsize() == canvas.color.xsize &&
|
||||
total_rect.ysize() == canvas.color.ysize;
|
||||
if (ppf->info.have_animation) {
|
||||
frame->frame_info.duration = gcb.DelayTime;
|
||||
frame->frame_info.layer_info.have_crop = static_cast<int>(!is_full_size);
|
||||
frame->frame_info.layer_info.crop_x0 = total_rect.x0();
|
||||
frame->frame_info.layer_info.crop_y0 = total_rect.y0();
|
||||
frame->frame_info.layer_info.xsize = frame->color.xsize;
|
||||
frame->frame_info.layer_info.ysize = frame->color.ysize;
|
||||
if (last_base_was_none) {
|
||||
replace = true;
|
||||
}
|
||||
frame->frame_info.layer_info.blend_info.blendmode =
|
||||
replace ? JXL_BLEND_REPLACE : JXL_BLEND_BLEND;
|
||||
// We always only reference at most the last frame
|
||||
frame->frame_info.layer_info.blend_info.source =
|
||||
last_base_was_none ? 0u : 1u;
|
||||
frame->frame_info.layer_info.blend_info.clamp = 1;
|
||||
frame->frame_info.layer_info.blend_info.alpha = 0;
|
||||
// TODO(veluca): this could in principle be implemented.
|
||||
if (last_base_was_none &&
|
||||
(total_rect.x0() != 0 || total_rect.y0() != 0 ||
|
||||
total_rect.xsize() != canvas.color.xsize ||
|
||||
total_rect.ysize() != canvas.color.ysize || !replace)) {
|
||||
return JXL_FAILURE(
|
||||
"GIF with dispose-to-0 is not supported for non-full or "
|
||||
"blended frames");
|
||||
}
|
||||
switch (gcb.DisposalMode) {
|
||||
case DISPOSE_DO_NOT:
|
||||
case DISPOSE_BACKGROUND:
|
||||
frame->frame_info.layer_info.save_as_reference = 1u;
|
||||
last_base_was_none = false;
|
||||
break;
|
||||
case DISPOSE_PREVIOUS:
|
||||
frame->frame_info.layer_info.save_as_reference = 0u;
|
||||
break;
|
||||
default:
|
||||
frame->frame_info.layer_info.save_as_reference = 0u;
|
||||
last_base_was_none = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the canvas by creating a copy first.
|
||||
PackedImage new_canvas_image(canvas.color.xsize, canvas.color.ysize,
|
||||
canvas.color.format);
|
||||
memcpy(new_canvas_image.pixels(), canvas.color.pixels(),
|
||||
new_canvas_image.pixels_size);
|
||||
for (size_t y = 0, byte_index = 0; y < image_rect.ysize(); ++y) {
|
||||
// Assumes format.align == 0. row points to the beginning of the y row in
|
||||
// the image_rect.
|
||||
PackedRgba* row = static_cast<PackedRgba*>(new_canvas_image.pixels()) +
|
||||
(y + image_rect.y0()) * new_canvas_image.xsize +
|
||||
image_rect.x0();
|
||||
for (size_t x = 0; x < image_rect.xsize(); ++x, ++byte_index) {
|
||||
const GifByteType byte = image.RasterBits[byte_index];
|
||||
if (byte >= color_map->ColorCount) {
|
||||
return JXL_FAILURE("GIF color is out of bounds");
|
||||
}
|
||||
|
||||
if (byte == gcb.TransparentColor) continue;
|
||||
GifColorType color = color_map->Colors[byte];
|
||||
row[x].r = color.Red;
|
||||
row[x].g = color.Green;
|
||||
row[x].b = color.Blue;
|
||||
row[x].a = 255;
|
||||
}
|
||||
}
|
||||
const PackedImage& sub_frame_image = frame->color;
|
||||
if (replace) {
|
||||
// Copy from the new canvas image to the subframe
|
||||
for (size_t y = 0; y < total_rect.ysize(); ++y) {
|
||||
const PackedRgba* row_in =
|
||||
static_cast<const PackedRgba*>(new_canvas_image.pixels()) +
|
||||
(y + total_rect.y0()) * new_canvas_image.xsize + total_rect.x0();
|
||||
PackedRgb* row_out = static_cast<PackedRgb*>(sub_frame_image.pixels()) +
|
||||
y * sub_frame_image.xsize;
|
||||
for (size_t x = 0; x < sub_frame_image.xsize; ++x) {
|
||||
row_out[x].r = row_in[x].r;
|
||||
row_out[x].g = row_in[x].g;
|
||||
row_out[x].b = row_in[x].b;
|
||||
set_pixel_alpha(x, y, row_in[x].a);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t y = 0, byte_index = 0; y < image_rect.ysize(); ++y) {
|
||||
// Assumes format.align == 0
|
||||
PackedRgb* row = static_cast<PackedRgb*>(sub_frame_image.pixels()) +
|
||||
y * sub_frame_image.xsize;
|
||||
for (size_t x = 0; x < image_rect.xsize(); ++x, ++byte_index) {
|
||||
const GifByteType byte = image.RasterBits[byte_index];
|
||||
if (byte > color_map->ColorCount) {
|
||||
return JXL_FAILURE("GIF color is out of bounds");
|
||||
}
|
||||
if (byte == gcb.TransparentColor) {
|
||||
row[x].r = 0;
|
||||
row[x].g = 0;
|
||||
row[x].b = 0;
|
||||
set_pixel_alpha(x, y, 0);
|
||||
continue;
|
||||
}
|
||||
GifColorType color = color_map->Colors[byte];
|
||||
row[x].r = color.Red;
|
||||
row[x].g = color.Green;
|
||||
row[x].b = color.Blue;
|
||||
set_pixel_alpha(x, y, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!frame->extra_channels.empty()) {
|
||||
ppf->info.alpha_bits = 8;
|
||||
}
|
||||
|
||||
switch (gcb.DisposalMode) {
|
||||
case DISPOSE_DO_NOT:
|
||||
canvas.color = std::move(new_canvas_image);
|
||||
break;
|
||||
|
||||
case DISPOSE_BACKGROUND:
|
||||
std::fill_n(static_cast<PackedRgba*>(canvas.color.pixels()),
|
||||
canvas.color.xsize * canvas.color.ysize, background_rgba);
|
||||
previous_rect_if_restore_to_background = image_rect;
|
||||
break;
|
||||
|
||||
case DISPOSE_PREVIOUS:
|
||||
break;
|
||||
|
||||
case DISPOSAL_UNSPECIFIED:
|
||||
default:
|
||||
std::fill_n(static_cast<PackedRgba*>(canvas.color.pixels()),
|
||||
canvas.color.xsize * canvas.color.ysize, background_rgba);
|
||||
}
|
||||
}
|
||||
// Finally, if any frame has an alpha-channel, every frame will need
|
||||
// to have an alpha-channel.
|
||||
bool seen_alpha = false;
|
||||
for (const PackedFrame& frame : ppf->frames) {
|
||||
if (!frame.extra_channels.empty()) {
|
||||
seen_alpha = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (seen_alpha) {
|
||||
for (PackedFrame& frame : ppf->frames) {
|
||||
ensure_have_alpha(&frame);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
35
third-party/libjxl/libjxl/lib/extras/dec/gif.h
vendored
Normal file
35
third-party/libjxl/libjxl/lib/extras/dec/gif.h
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_DEC_GIF_H_
|
||||
#define LIB_EXTRAS_DEC_GIF_H_
|
||||
|
||||
// Decodes GIF images in memory.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/extras/dec/color_hints.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
struct SizeConstraints;
|
||||
|
||||
namespace extras {
|
||||
|
||||
bool CanDecodeGIF();
|
||||
|
||||
// Decodes `bytes` into `ppf`. color_hints are ignored.
|
||||
Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||
PackedPixelFile* ppf,
|
||||
const SizeConstraints* constraints = nullptr);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_DEC_GIF_H_
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user