fix: CoreUI support via Framework abstraction
This commit is contained in:
85
AssetCarReader/AssetCarReader.swift
Normal file
85
AssetCarReader/AssetCarReader.swift
Normal file
@@ -0,0 +1,85 @@
|
||||
import Foundation
|
||||
import AppKit // NSImage
|
||||
private import CoreUI // CUICatalog
|
||||
private import os // OSLog
|
||||
|
||||
private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "AssetCarReader")
|
||||
|
||||
public class CarReader {
|
||||
private let catalog: CUICatalog
|
||||
|
||||
public init?(_ data: Data) {
|
||||
do {
|
||||
catalog = try data.withUnsafeBytes { try CUICatalog(bytes: $0.baseAddress!, length: UInt64(data.count)) }
|
||||
} catch {
|
||||
os_log(.error, log: log, "[asset-car] ERROR: could not open catalog: %{public}@", error.localizedDescription)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Use `CUICatalog` to extract an image from `Assets.car`
|
||||
public func imageFromAssetsCar(_ imageName: String) -> NSImage? {
|
||||
if let validName = verifyNameExists(imageName, in: catalog) {
|
||||
if let bestImage = findHighestResolutionIcon(catalog.images(withName: validName)) {
|
||||
os_log(.debug, log: log, "[asset-car] using Assets.car with key %{public}@", validName)
|
||||
return NSImage(cgImage: bestImage.image, size: bestImage.size)
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
/// Helper method to check available icon names. Will return a valid name or `nil` if no image with that key is found.
|
||||
private func verifyNameExists(_ imageName: String, in catalog: CUICatalog) -> String? {
|
||||
if let availableNames = catalog.allImageNames(), !availableNames.contains(imageName) {
|
||||
// Theoretically this should never happen. Assuming the image name is found in an image file.
|
||||
os_log(.info, log: log, "[asset-car] WARN: key '%{public}@' does not match any available key", imageName)
|
||||
|
||||
if let alternativeName = searchAlternativeName(imageName, inAvailable: availableNames) {
|
||||
os_log(.info, log: log, "[asset-car] falling back to '%{public}@'", alternativeName)
|
||||
return alternativeName
|
||||
}
|
||||
os_log(.debug, log: log, "[asset-car] available keys: %{public}@", catalog.allImageNames() ?? [])
|
||||
return nil
|
||||
}
|
||||
return imageName;
|
||||
}
|
||||
|
||||
/// If exact name does not exist in catalog, search for a name that shares the same prefix.
|
||||
/// E.g., "AppIcon60x60" may match "AppIcon" or "AppIcon60x60_small"
|
||||
private func searchAlternativeName(_ originalName: String, inAvailable availableNames: [String]) -> String? {
|
||||
var bestOption: String? = nil
|
||||
var bestDiff: Int = 999
|
||||
|
||||
for option in availableNames {
|
||||
if option.hasPrefix(originalName) || originalName.hasPrefix(option) {
|
||||
let thisDiff = max(originalName.count, option.count) - min(originalName.count, option.count)
|
||||
if thisDiff < bestDiff {
|
||||
bestDiff = thisDiff
|
||||
bestOption = option
|
||||
}
|
||||
}
|
||||
}
|
||||
return bestOption
|
||||
}
|
||||
|
||||
/// Given a list of `CUINamedImage`, return the one with the highest resolution. Vector graphics are ignored.
|
||||
private func findHighestResolutionIcon(_ availableImages: [CUINamedImage]) -> CUINamedImage? {
|
||||
var largestWidth: CGFloat = 0
|
||||
var largestImage: CUINamedImage? = nil
|
||||
// cast to NSArray is necessary as otherwise this will crash
|
||||
for img in availableImages as NSArray {
|
||||
guard let img = img as? CUINamedImage else {
|
||||
continue // ignore CUINamedMultisizeImageSet
|
||||
}
|
||||
let w = img.size.width
|
||||
if w > largestWidth {
|
||||
largestWidth = w
|
||||
largestImage = img
|
||||
}
|
||||
}
|
||||
return largestImage
|
||||
}
|
||||
}
|
||||
9
AssetCarReader/AssetCarReader.xcconfig
Normal file
9
AssetCarReader/AssetCarReader.xcconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
FRAMEWORK_SEARCH_PATHS = $(PROJECT_DIR)/AssetCarReader/PrivateFrameworks
|
||||
SYSTEM_FRAMEWORK_SEARCH_PATHS = $(SYSTEM_LIBRARY_DIR)/PrivateFrameworks
|
||||
SWIFT_INCLUDE_PATHS = $(SRCROOT)/AssetCarReader/PrivateFrameworks/CoreUI.framework
|
||||
|
||||
DYLIB_COMPATIBILITY_VERSION = 1
|
||||
DYLIB_CURRENT_VERSION = 42
|
||||
1
AssetCarReader/PrivateFrameworks/CoreUI.framework/Headers
Symbolic link
1
AssetCarReader/PrivateFrameworks/CoreUI.framework/Headers
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Headers/
|
||||
@@ -0,0 +1,54 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class NSBundle, NSCache, NSMapTable, NSString;
|
||||
@class CUINamedImage, CUIStructuredThemeStore;
|
||||
|
||||
@interface CUICatalog: NSObject {
|
||||
NSString * _assetStoreName;
|
||||
NSBundle * _bundle;
|
||||
unsigned int _fileHasDisplayGamutInKeySpace;
|
||||
NSCache * _localObjectCache;
|
||||
NSCache * _lookupCache;
|
||||
NSCache * _negativeCache;
|
||||
unsigned short _preferredLocalization;
|
||||
unsigned int _purgeWhenFinished;
|
||||
unsigned int _reserved;
|
||||
NSMapTable * _storageMapTable;
|
||||
unsigned long long _storageRef;
|
||||
NSDictionary * _vibrantColorMatrixTints;
|
||||
}
|
||||
|
||||
- (CUIStructuredThemeStore *)_themeStore;
|
||||
|
||||
+ (id)defaultUICatalogForBundle:(id)arg1;
|
||||
|
||||
- (id)initWithBytes:(const void*)arg1 length:(unsigned long long)arg2 error:(NSError **)arg3;
|
||||
- (id)initWithName:(id)arg1 fromBundle:(id)arg2;
|
||||
- (id)initWithName:(id)arg1 fromBundle:(id)arg2 error:(id*)arg3;
|
||||
- (id)initWithURL:(id)arg1 error:(NSError **)arg2;
|
||||
|
||||
- (BOOL)imageExistsWithName:(id)arg1;
|
||||
- (BOOL)imageExistsWithName:(id)arg1 scaleFactor:(double)arg2;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 appearanceName:(id)arg3;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 appearanceName:(id)arg4;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 appearanceName:(id)arg5;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8 appearanceName:(id)arg9;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8 memoryClass:(long long)arg9 graphicsClass:(long long)arg10;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8 memoryClass:(unsigned long long)arg9 graphicsClass:(unsigned long long)arg10 appearanceIdentifier:(long long)arg11 graphicsFallBackOrder:(id)arg12 deviceSubtypeFallBackOrder:(id)arg13;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 displayGamut:(long long)arg5 layoutDirection:(long long)arg6 sizeClassHorizontal:(long long)arg7 sizeClassVertical:(long long)arg8 memoryClass:(unsigned long long)arg9 graphicsClass:(unsigned long long)arg10 graphicsFallBackOrder:(id)arg11 deviceSubtypeFallBackOrder:(id)arg12;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 sizeClassHorizontal:(long long)arg5 sizeClassVertical:(long long)arg6;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 deviceSubtype:(unsigned long long)arg4 sizeClassHorizontal:(long long)arg5 sizeClassVertical:(long long)arg6 appearanceName:(id)arg7;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 deviceIdiom:(long long)arg3 layoutDirection:(long long)arg4 adjustRenditionKeyWithBlock:(id)arg5;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 displayGamut:(long long)arg3 layoutDirection:(long long)arg4;
|
||||
- (CUINamedImage *)imageWithName:(id)arg1 scaleFactor:(double)arg2 displayGamut:(long long)arg3 layoutDirection:(long long)arg4 appearanceName:(id)arg5;
|
||||
- (NSArray<CUINamedImage *> *)imagesWithName:(id)arg1;
|
||||
|
||||
- (NSArray<NSString *> *)allImageNames;
|
||||
- (NSArray<NSString *> *)appearanceNames;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreUI/CUINamedLookup.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
@interface CUINamedImage: CUINamedLookup {
|
||||
struct _cuiniproperties {
|
||||
unsigned int isVectorBased : 1;
|
||||
unsigned int hasSliceInformation : 1;
|
||||
unsigned int hasAlignmentInformation : 1;
|
||||
unsigned int resizingMode : 2;
|
||||
unsigned int templateRenderingMode : 3;
|
||||
unsigned int exifOrientation : 4;
|
||||
unsigned int isAlphaCropped : 1;
|
||||
unsigned int isFlippable : 1;
|
||||
unsigned int isTintable : 1;
|
||||
unsigned int preservedVectorRepresentation : 1;
|
||||
unsigned int _reserved : 16;
|
||||
} _imageProperties;
|
||||
double _scale;
|
||||
}
|
||||
|
||||
@property (readonly) CGRect NS_alignmentRect;
|
||||
@property (nonatomic, readonly) NSEdgeInsets alignmentEdgeInsets;
|
||||
@property (nonatomic, readonly) int blendMode;
|
||||
@property (nonatomic, readonly) CGImageRef croppedImage;
|
||||
@property (nonatomic, readonly) NSEdgeInsets edgeInsets;
|
||||
@property (nonatomic, readonly) int exifOrientation;
|
||||
@property (nonatomic, readonly) BOOL hasAlignmentInformation;
|
||||
@property (nonatomic, readonly) BOOL hasSliceInformation;
|
||||
@property (nonatomic, readonly) CGImageRef image;
|
||||
@property (nonatomic, readonly) long long imageType;
|
||||
@property (nonatomic, readonly) BOOL isAlphaCropped;
|
||||
@property (nonatomic, readonly) BOOL isFlippable;
|
||||
@property (nonatomic, readonly) BOOL isStructured;
|
||||
@property (nonatomic, readonly) BOOL isTemplate;
|
||||
@property (nonatomic, readonly) BOOL isVectorBased;
|
||||
@property (nonatomic, readonly) double opacity;
|
||||
@property (nonatomic, readonly) BOOL preservedVectorRepresentation;
|
||||
@property (nonatomic, readonly) long long resizingMode;
|
||||
@property (nonatomic, readonly) double scale;
|
||||
@property (nonatomic, readonly) CGSize size;
|
||||
@property (nonatomic, readonly) long long templateRenderingMode;
|
||||
|
||||
- (id)baseKey;
|
||||
- (CGRect)alphaCroppedRect;
|
||||
- (CGImageRef)createImageFromPDFRenditionWithScale:(double)arg1;
|
||||
- (CGImageRef)croppedImage;
|
||||
|
||||
- (id)initWithName:(id)arg1 usingRenditionKey:(id)arg2 fromTheme:(unsigned long long)arg3;
|
||||
|
||||
- (CGSize)originalUncroppedSize;
|
||||
- (double)positionOfSliceBoundary:(unsigned int)arg1;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class CUIRenditionKey;
|
||||
|
||||
@interface CUINamedLookup: NSObject <NSLocking> {
|
||||
unsigned int _distilledInVersion;
|
||||
CUIRenditionKey * _key;
|
||||
NSString * _name;
|
||||
unsigned int _odContent;
|
||||
NSString * _signature;
|
||||
unsigned long long _storageRef;
|
||||
}
|
||||
|
||||
- (id)initWithName:(id)arg1 usingRenditionKey:(id)arg2 fromTheme:(unsigned long long)arg3;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1 @@
|
||||
A
|
||||
@@ -0,0 +1,9 @@
|
||||
module CoreUI {
|
||||
// umbrella header "CoreUI.h"
|
||||
// Here is the list of your private headers.
|
||||
header "Headers/CUICatalog.h"
|
||||
header "Headers/CUINamedLookup.h"
|
||||
header "Headers/CUINamedImage.h"
|
||||
|
||||
export *
|
||||
}
|
||||
Reference in New Issue
Block a user