Skip to content

Commit

Permalink
Merged SVG support
Browse files Browse the repository at this point in the history
  • Loading branch information
insidegui committed Aug 10, 2024
1 parent b26c2d6 commit cbb9817
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 34 deletions.
13 changes: 13 additions & 0 deletions ACS/Private Headers/CoreSVG.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//

#ifndef CoreSVG_h
#define CoreSVG_h

struct CGSVGDocument;

typedef struct CGSVGDocument *CGSVGDocumentRef;

int CGSVGDocumentWriteToData(CGSVGDocumentRef, CFDataRef, CFDictionaryRef) __attribute__((weak_import));;
void CGContextDrawSVGDocument(CGContextRef, CGSVGDocumentRef) __attribute__((weak_import));;
CGSize CGSVGDocumentGetCanvasSize(CGSVGDocumentRef) __attribute__((weak_import));;
#endif /* CoreSVG_h */
35 changes: 35 additions & 0 deletions ACS/Private Headers/CoreUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//

#import <Cocoa/Cocoa.h>
#import "CoreSVG.h"

@interface CUINamedData : NSObject

Expand Down Expand Up @@ -73,6 +74,8 @@ struct _renditionkeytoken {
@property (readonly) long long themeSize;
@property (readonly) long long themeElement;
@property (readonly) long long themePart;
@property (readonly) long long themeGlyphWeight;
@property (readonly) long long themeGlyphSize;

@end

Expand All @@ -82,6 +85,9 @@ struct _renditionkeytoken {
@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) NSData *data;
@property (nonatomic, readonly) CGImageRef unslicedImage;
@property (nonatomic, readonly) BOOL isVectorBased;
@property (nonatomic, readonly) struct CGSVGDocument *svgDocument;
@property (nonatomic, readonly) long long vectorGlyphRenderingMode;

@end

Expand Down Expand Up @@ -132,3 +138,32 @@ struct _renditionkeytoken {
- (CUIStructuredThemeStore *)_themeStore;

@end

typedef NS_ENUM(NSInteger, UIImageSymbolScale) {
UIImageSymbolScaleDefault = -1, // use the system default size
UIImageSymbolScaleUnspecified = 0, // allow the system to pick a size based on the context
UIImageSymbolScaleSmall = 1,
UIImageSymbolScaleMedium,
UIImageSymbolScaleLarge,
};

typedef NS_ENUM(NSInteger, UIImageSymbolWeight) {
UIImageSymbolWeightUnspecified = 0,
UIImageSymbolWeightUltraLight = 1,
UIImageSymbolWeightThin,
UIImageSymbolWeightLight,
UIImageSymbolWeightRegular,
UIImageSymbolWeightMedium,
UIImageSymbolWeightSemibold,
UIImageSymbolWeightBold,
UIImageSymbolWeightHeavy,
UIImageSymbolWeightBlack
};

// This is a made up enum
typedef NS_ENUM(NSInteger, UIImageSymbolRenderingMode) {
UIImageSymbolRenderingModeAutomatic,
UIImageSymbolRenderingModeTemplate,
UIImageSymbolRenderingModeMulticolor,
UIImageSymbolRenderingModeHierarchical,
};
4 changes: 2 additions & 2 deletions ACS/Source/AssetCatalogReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ extern NSString *__nonnull const kACSImageKey;
extern NSString *__nonnull const kACSThumbnailKey;
/// An NSString with the suggested filename for the asset
extern NSString *__nonnull const kACSFilenameKey;
/// An NSData containing PNG image data for the asset
extern NSString *__nonnull const kACSPNGDataKey;
/// An NSData containing "image" data for the asset (PNG, SVG, etc)
extern NSString *__nonnull const kACSContentsDataKey;
/// An NSBitmapImageRep containing a bitmap representation of the asset
extern NSString *__nonnull const kACSImageRepKey;

Expand Down
130 changes: 113 additions & 17 deletions ACS/Source/AssetCatalogReader.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
NSString * const kACSImageKey = @"image";
NSString * const kACSThumbnailKey = @"thumbnail";
NSString * const kACSFilenameKey = @"filename";
NSString * const kACSPNGDataKey = @"png";
NSString * const kACSContentsDataKey = @"data";
NSString * const kACSImageRepKey = @"imagerep";

NSString * const kAssetCatalogReaderErrorDomain = @"br.com.guilhermerambo.AssetCatalogReader";
Expand Down Expand Up @@ -191,11 +191,14 @@ - (void)readWithCompletionHandler:(void (^__nonnull)(void))callback progressHand
if ([self catalogHasRetinaContent] && weakSelf.resourceConstrained && namedImage.scale < 2) {
continue;
}

NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:image];
imageRep.size = namedImage.size;

NSDictionary *desc = [self imageDescriptionWithName:namedImage.name filename:filename representation:imageRep];

NSDictionary *desc = [self imageDescriptionWithName:namedImage.name filename:filename representation:imageRep contentsData:^NSData *{
return [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{NSImageInterlaced:@(NO)}];
}];

if (!desc) {
loadedItemCount++;
return;
Expand Down Expand Up @@ -259,27 +262,48 @@ - (void)readThemeStoreWithCompletionHandler:(void (^__nonnull)(void))callback pr
}

NSString *filename = [self filenameForAssetNamed:[self cleanupRenditionName:rendition.name] scale:rendition.scale presentationState:key.themeState];

if (rendition.unslicedImage) {

const BOOL coreSVGPresent = CGSVGDocumentGetCanvasSize != NULL && CGContextDrawSVGDocument != NULL && CGSVGDocumentWriteToData != NULL;
const BOOL isSVG = coreSVGPresent && rendition.isVectorBased && rendition.svgDocument;

if (isSVG) {
NSCustomImageRep *imageRep = [[NSCustomImageRep alloc] initWithSize:CGSVGDocumentGetCanvasSize(rendition.svgDocument) flipped:YES drawingHandler:^BOOL(NSRect dstRect) {
CGContextRef context = NSGraphicsContext.currentContext.CGContext;
if (context && rendition.svgDocument) {
CGContextDrawSVGDocument(context, rendition.svgDocument);
}
return YES;
}];
NSString *const filename = [self filenameForVectorAssetNamed:[self cleanupRenditionName:rendition.name] renderingMode:rendition.vectorGlyphRenderingMode weight:key.themeGlyphWeight size:key.themeGlyphSize];
NSDictionary *desc = [self imageDescriptionWithName:rendition.name filename:filename representation:imageRep contentsData:^NSData *{
NSMutableData *data = [NSMutableData new];
CGSVGDocumentWriteToData(rendition.svgDocument, (__bridge CFMutableDataRef)data, NULL);
return data;
}];
if (self.cancelled) return;
[self.mutableImages addObject:desc];
} else if (rendition.unslicedImage) {
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:rendition.unslicedImage];
imageRep.size = NSMakeSize(CGImageGetWidth(rendition.unslicedImage), CGImageGetHeight(rendition.unslicedImage));

NSDictionary *desc = [self imageDescriptionWithName:rendition.name filename:filename representation:imageRep];


NSDictionary *desc = [self imageDescriptionWithName:rendition.name filename:filename representation:imageRep contentsData:^NSData *{
return [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{NSImageInterlaced:@(NO)}];
}];

BOOL ignore = [filename containsString:@"ZZPackedAsset"] && self.ignorePackedAssets;

if (!desc || ignore) {
loadedItemCount++;
return;
}

if (self.cancelled) return;

[self.mutableImages addObject:desc];
} else {
NSLog(@"The rendition %@ doesn't have an image, It is probably an effect or material.", rendition.name);
}

loadedItemCount++;
} @catch (NSException *exception) {
NSLog(@"Exception while reading theme store: %@", exception);
Expand Down Expand Up @@ -350,7 +374,7 @@ - (BOOL)isProThemeStoreAtPath:(NSString *)path
return images;
}

- (NSDictionary *)imageDescriptionWithName:(NSString *)name filename:(NSString *)filename representation:(NSBitmapImageRep *)imageRep
- (NSDictionary *)imageDescriptionWithName:(NSString *)name filename:(NSString *)filename representation:(NSImageRep *)imageRep contentsData:(NSData *(^)(void))contentsData
{
if (_resourceConstrained) {
return @{
Expand All @@ -359,8 +383,8 @@ - (NSDictionary *)imageDescriptionWithName:(NSString *)name filename:(NSString *
kACSImageRepKey: imageRep
};
} else {
NSData *pngData = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{NSImageInterlaced:@(NO)}];
NSData *pngData = contentsData();

if (!pngData.length) {
NSLog(@"Unable to get PNG data from rendition named %@", name);
return nil;
Expand All @@ -374,7 +398,7 @@ - (NSDictionary *)imageDescriptionWithName:(NSString *)name filename:(NSString *
kACSImageKey : originalImage,
kACSThumbnailKey: thumbnail,
kACSFilenameKey: filename,
kACSPNGDataKey: pngData
kACSContentsDataKey: pngData
};
}
}
Expand Down Expand Up @@ -403,6 +427,78 @@ - (NSString *)filenameForAssetNamed:(NSString *)name scale:(CGFloat)scale presen
}
}

- (NSString *)filenameForVectorAssetNamed:(NSString *)name renderingMode:(UIImageSymbolRenderingMode)renderingMode weight:(UIImageSymbolWeight)weight size:(UIImageSymbolScale)size {
NSString *weightName;
switch (weight) {
case UIImageSymbolWeightUnspecified:
weightName = @"unspecified";
break;
case UIImageSymbolWeightUltraLight:
weightName = @"ultraLight";
break;
case UIImageSymbolWeightThin:
weightName = @"thin";
break;
case UIImageSymbolWeightLight:
weightName = @"light";
break;
case UIImageSymbolWeightRegular:
weightName = @"regular";
break;
case UIImageSymbolWeightMedium:
weightName = @"medium";
break;
case UIImageSymbolWeightSemibold:
weightName = @"semibold";
break;
case UIImageSymbolWeightBold:
weightName = @"bold";
break;
case UIImageSymbolWeightHeavy:
weightName = @"heavy";
break;
case UIImageSymbolWeightBlack:
weightName = @"black";
break;
}

NSString *sizeName;
switch (size) {
case UIImageSymbolScaleDefault:
sizeName = @"default";
break;
case UIImageSymbolScaleUnspecified:
sizeName = @"unspecified";
break;
case UIImageSymbolScaleSmall:
sizeName = @"small";
break;
case UIImageSymbolScaleMedium:
sizeName = @"medium";
break;
case UIImageSymbolScaleLarge:
sizeName = @"large";
break;
}

NSString *renderingModeName;
switch (renderingMode) {
case UIImageSymbolRenderingModeAutomatic:
renderingModeName = @"automatic";
break;
case UIImageSymbolRenderingModeTemplate:
renderingModeName = @"template";
break;
case UIImageSymbolRenderingModeMulticolor:
renderingModeName = @"multicolor";
break;
case UIImageSymbolRenderingModeHierarchical:
renderingModeName = @"hierarchical";
break;
}
return [NSString stringWithFormat:@"%@_%@_%@_%@.svg", name, weightName, sizeName, renderingModeName];
}

- (BOOL)catalogHasRetinaContent
{
if (!_computedCatalogHasRetinaContent) {
Expand Down
4 changes: 2 additions & 2 deletions ACS/Source/ImageExporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ public struct ImageExporter {
public func export(toDirectoryAt url: URL, completionHandler: (() -> Void)? = nil) {
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
images.forEach { image in
guard let filename = image["filename"] as? String else { return }
guard let filename = image[kACSFilenameKey] as? String else { return }

var pathComponents = url.pathComponents

pathComponents.append(filename)

guard let pngData = image["png"] as? Data else { return }
guard let pngData = image[kACSContentsDataKey] as? Data else { return }

let path = self.nextAvailablePath(filePath: NSString.path(withComponents: pathComponents) as String)
do {
Expand Down
8 changes: 8 additions & 0 deletions Asset Catalog Tinkerer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
DDDDE5C91CA96EF400B353D3 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDDE5C81CA96EF400B353D3 /* DocumentController.swift */; };
DDDDE5CB1CA97C0300B353D3 /* QuickLookableCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDDDE5CA1CA97C0300B353D3 /* QuickLookableCollectionView.swift */; };
DDF7E41F22CC0AB600EF1965 /* NSPasteboard+Filenames.m in Sources */ = {isa = PBXBuildFile; fileRef = DDF7E41E22CC0AB600EF1965 /* NSPasteboard+Filenames.m */; };
F44011EE2C67AD1A00E47EC9 /* CoreSVG.h in Headers */ = {isa = PBXBuildFile; fileRef = F44011ED2C67AD1A00E47EC9 /* CoreSVG.h */; };
F44011F02C67AFD600E47EC9 /* CoreSVG.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F44011EF2C67AFD600E47EC9 /* CoreSVG.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
F450A901275A46E700C5DB9B /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F450A900275A46E700C5DB9B /* UniformTypeIdentifiers.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
F45DB91C2B9FAF6700A1A4BD /* AssetCatalogTinkererCLI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F45DB91B2B9FAF6700A1A4BD /* AssetCatalogTinkererCLI.swift */; };
F45DB9232B9FAF8B00A1A4BD /* act in Copy CLI Executable */ = {isa = PBXBuildFile; fileRef = F45DB9192B9FAF6700A1A4BD /* act */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
Expand Down Expand Up @@ -170,6 +172,8 @@
DDDDE5CA1CA97C0300B353D3 /* QuickLookableCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickLookableCollectionView.swift; sourceTree = "<group>"; };
DDF7E41D22CC0AB600EF1965 /* NSPasteboard+Filenames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSPasteboard+Filenames.h"; sourceTree = "<group>"; };
DDF7E41E22CC0AB600EF1965 /* NSPasteboard+Filenames.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSPasteboard+Filenames.m"; sourceTree = "<group>"; };
F44011ED2C67AD1A00E47EC9 /* CoreSVG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreSVG.h; sourceTree = "<group>"; };
F44011EF2C67AFD600E47EC9 /* CoreSVG.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSVG.framework; path = ../../../../../System/Library/PrivateFrameworks/CoreSVG.framework; sourceTree = "<group>"; };
F450A900275A46E700C5DB9B /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
F45DB9192B9FAF6700A1A4BD /* act */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = act; sourceTree = BUILT_PRODUCTS_DIR; };
F45DB91B2B9FAF6700A1A4BD /* AssetCatalogTinkererCLI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AssetCatalogTinkererCLI.swift; sourceTree = "<group>"; };
Expand All @@ -184,6 +188,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F44011F02C67AFD600E47EC9 /* CoreSVG.framework in Frameworks */,
DDAF8AEA1E1B11DB00A8A235 /* CoreUI.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -261,6 +266,7 @@
DDAF8ADF1E1B119A00A8A235 /* Private Headers */ = {
isa = PBXGroup;
children = (
F44011ED2C67AD1A00E47EC9 /* CoreSVG.h */,
DDAF8AE01E1B119A00A8A235 /* CoreUI+TV.h */,
DDAF8AE11E1B119A00A8A235 /* CoreUI.h */,
);
Expand All @@ -270,6 +276,7 @@
DDAF8AE81E1B11DB00A8A235 /* Frameworks */ = {
isa = PBXGroup;
children = (
F44011EF2C67AFD600E47EC9 /* CoreSVG.framework */,
F450A900275A46E700C5DB9B /* UniformTypeIdentifiers.framework */,
DDAF8AEB1E1B11DF00A8A235 /* ProKit.framework */,
DDAF8AE91E1B11DB00A8A235 /* CoreUI.framework */,
Expand Down Expand Up @@ -424,6 +431,7 @@
DDAF8AE31E1B119A00A8A235 /* CoreUI+TV.h in Headers */,
DDAF8AE41E1B119A00A8A235 /* CoreUI.h in Headers */,
DDAF8ADD1E1B117600A8A235 /* AssetCatalogReader.h in Headers */,
F44011EE2C67AD1A00E47EC9 /* CoreSVG.h in Headers */,
DDAF8ACC1E1B113400A8A235 /* ACS.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
8 changes: 4 additions & 4 deletions Asset Catalog Tinkerer/ImageCollectionViewItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,10 @@ class ImageCollectionViewItem: NSCollectionViewItem {

fileprivate func updateUI() {
guard let imageData = image , isViewLoaded else { return }
guard let image = imageData["thumbnail"] as? NSImage else { return }
let name = imageData["name"] as! String
let filename = imageData["filename"] as! String
guard let image = imageData[kACSThumbnailKey] as? NSImage else { return }
let name = imageData[kACSNameKey] as! String
let filename = imageData[kACSFilenameKey] as! String

let brightness = image.averageBrightness()

catalogImageView.image = image
Expand Down
11 changes: 6 additions & 5 deletions Asset Catalog Tinkerer/ImagesCollectionViewDataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Cocoa
import UniformTypeIdentifiers
import ACS

extension NSUserInterfaceItemIdentifier {
static let imageItemIdentifier = NSUserInterfaceItemIdentifier("ImageItemIdentifier")
Expand Down Expand Up @@ -111,9 +112,9 @@ class ImagesCollectionViewDataProvider: NSObject, NSCollectionViewDataSource, NS

let image = filteredImages[indexPath.item]

guard let filename = image["filename"] as? String else { return nil }
guard let data = image["png"] as? Data else { return nil }
guard let filename = image[kACSFilenameKey] as? String else { return nil }
guard let data = image[kACSContentsDataKey] as? Data else { return nil }

let tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(filename)

Expand All @@ -136,7 +137,7 @@ extension ImagesCollectionViewDataProvider: NSFilePromiseProviderDelegate {
return ""
}

guard let filename = image["filename"] as? String else { return "" }
guard let filename = image[kACSFilenameKey] as? String else { return "" }

return filename
}
Expand All @@ -149,7 +150,7 @@ extension ImagesCollectionViewDataProvider: NSFilePromiseProviderDelegate {
return
}

guard let data = image["png"] as? Data else {
guard let data = image[kACSContentsDataKey] as? Data else {
completionHandler(nil)
return
}
Expand Down
Loading

0 comments on commit cbb9817

Please sign in to comment.