Skip to content

Commit 467a753

Browse files
authored
feat: Add Core Text graphics context provider and text graphics context providers (#149)
- Add Core Text graphics context provider implementation - Add protocol for NSTextGraphicsContext - Add protocol for NSTextGraphicsContextProvider
1 parent 19da4fe commit 467a753

File tree

7 files changed

+278
-5
lines changed

7 files changed

+278
-5
lines changed

Sources/OpenSwiftUI_SPI/Overlay/CoreGraphics/OpenSwiftUICoreGraphicsContext.m

+37-3
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,47 @@
99

1010
#if OPENSWIFTUI_TARGET_OS_DARWIN
1111
#include <Foundation/Foundation.h>
12+
#include "OpenSwiftUICoreTextGraphicsContextProvider.h"
13+
#include "Shims/UIFoundation/NSTextGraphicsContextInternal.h"
14+
15+
#if OPENSWIFTUI_TARGET_OS_OSX
16+
#include <AppKit/AppKit.h>
17+
#endif
1218

1319
static _Thread_local __unsafe_unretained OpenSwiftUICoreGraphicsContext * _current = NULL;
1420

21+
dispatch_once_t _once;
22+
#if OPENSWIFTUI_TARGET_OS_OSX
23+
Class _nsGraphicsContextClass;
24+
#else
1525
IMP _pushContextIMP;
1626
IMP _popContextIMP;
27+
#endif
1728

1829
@interface OpenSwiftUICoreGraphicsContext () {
1930
OpenSwiftUICoreGraphicsContext *_next;
2031
CGContextRef _ctx;
2132
}
33+
#if !OPENSWIFTUI_TARGET_OS_OSX
2234
- (id)__createsImages;
35+
#endif
2336
@end
2437

2538
@implementation OpenSwiftUICoreGraphicsContext
2639

2740
- (instancetype)initWithCGContext:(CGContextRef)ctx {
28-
static dispatch_once_t __once;
29-
dispatch_once(&__once, ^{
41+
dispatch_once(&_once, ^{
42+
#if OPENSWIFTUI_TARGET_OS_OSX
43+
_nsGraphicsContextClass = NSClassFromString(@"NSGraphicsContext");
44+
#else
3045
Class renderClass = NSClassFromString(@"UIGraphicsRenderer");
3146
if (renderClass) {
3247
_pushContextIMP = [renderClass instanceMethodForSelector:@selector(pushContext:)];
3348
_popContextIMP = [renderClass instanceMethodForSelector:@selector(popContext:)];
3449
} else {
35-
// TODO: CoreTextGraphicsContextProvider.sharedProvider
50+
InitializeCoreTextGraphicsContextProvider();
3651
}
52+
#endif
3753
});
3854
self = [super init];
3955
if (self) {
@@ -43,25 +59,43 @@ - (instancetype)initWithCGContext:(CGContextRef)ctx {
4359
}
4460

4561
- (void)push {
62+
#if OPENSWIFTUI_TARGET_OS_OSX
63+
_next = _current;
64+
_current = self;
65+
if (_nsGraphicsContextClass) {
66+
[_nsGraphicsContextClass saveGraphicsState];
67+
[_nsGraphicsContextClass graphicsContextWithCGContext: _ctx flipped: YES];
68+
NSGraphicsContext *graphicsContext = [_nsGraphicsContextClass graphicsContextWithCGContext: _ctx flipped: YES];
69+
[_nsGraphicsContextClass setCurrentContext: graphicsContext];
70+
}
71+
#else
4672
_next = _current;
4773
_current = self;
4874
if (_pushContextIMP != NULL && _popContextIMP != NULL) {
4975
typedef BOOL (*FUNC)(id, SEL, OpenSwiftUICoreGraphicsContext *);
5076
((FUNC)(_pushContextIMP))(NULL, @selector(pushContext:), _current);
5177
}
78+
#endif
5279
}
5380

5481
- (void)pop {
82+
#if OPENSWIFTUI_TARGET_OS_OSX
83+
_current = _next;
84+
[_nsGraphicsContextClass restoreGraphicsState];
85+
#else
5586
_current = _next;
5687
if (_pushContextIMP != NULL && _popContextIMP != NULL) {
5788
typedef BOOL (*FUNC)(id, SEL, OpenSwiftUICoreGraphicsContext *);
5889
((FUNC)(_popContextIMP))(NULL, @selector(popContext:), _current);
5990
}
91+
#endif
6092
}
6193

94+
#if !OPENSWIFTUI_TARGET_OS_OSX
6295
- (id)__createsImages {
6396
return nil;
6497
}
98+
#endif
6599

66100
- (CGContextRef)CGContext {
67101
return _ctx;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// OpenSwiftUICoreTextGraphicsContextProvider.h
3+
// OpenSwiftUI_SPI
4+
//
5+
// Audited for RELEASE_2024
6+
// Status: Complete
7+
8+
#ifndef OpenSwiftUICoreTextGraphicsContextProvider_h
9+
#define OpenSwiftUICoreTextGraphicsContextProvider_h
10+
11+
#include "OpenSwiftUIBase.h"
12+
13+
#if OPENSWIFTUI_TARGET_OS_DARWIN
14+
15+
#include "Shims/UIFoundation/NSTextGraphicsContext.h"
16+
#include "Shims/UIFoundation/NSTextGraphicsContextProvider.h"
17+
18+
void InitializeCoreTextGraphicsContextProvider(void);
19+
20+
@interface OpenSwiftUICoreTextGraphicsContextProvider : NSObject<NSTextGraphicsContextProvider, NSTextGraphicsContext>
21+
22+
@property (readonly) CGContextRef CGContext;
23+
@property (readonly, getter=isFlipped) BOOL flipped;
24+
@property (readonly, getter=isDrawingToScreen) BOOL drawingToScreen;
25+
26+
+ (instancetype)sharedProvider;
27+
+ (id<NSTextGraphicsContext>)graphicsContextForApplicationFrameworkContext:(id)context;
28+
+ (Class)colorClassForApplicationFrameworkContext:(id)context;
29+
30+
- (BOOL)isFlipped;
31+
- (BOOL)isDrawingToScreen;
32+
- (CGContextRef)CGContext;
33+
- (void)saveGraphicsState;
34+
- (void)restoreGraphicsState;
35+
- (void)becomeCurrentGraphicsContextDuringBlock:(void (^)(void))block;
36+
37+
@end
38+
39+
#endif
40+
41+
#endif /* OpenSwiftUICoreTextGraphicsContextProvider_h */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//
2+
// OpenSwiftUICoreTextGraphicsContextProvider.m
3+
// OpenSwiftUI_SPI
4+
//
5+
// Audited for RELEASE_2024
6+
// Status: Complete
7+
8+
#include "OpenSwiftUICoreTextGraphicsContextProvider.h"
9+
10+
#if OPENSWIFTUI_TARGET_OS_DARWIN
11+
12+
#include "OpenSwiftUICoreColor.h"
13+
#include "OpenSwiftUICoreGraphicsContext.h"
14+
15+
void InitializeCoreTextGraphicsContextProvider(void) {
16+
(void)OpenSwiftUICoreTextGraphicsContextProvider.sharedProvider;
17+
}
18+
19+
@implementation OpenSwiftUICoreTextGraphicsContextProvider
20+
21+
+ (instancetype)sharedProvider {
22+
static OpenSwiftUICoreTextGraphicsContextProvider *sharedPvdr = nil;
23+
static dispatch_once_t once;
24+
dispatch_once(&once, ^{
25+
sharedPvdr = [[OpenSwiftUICoreTextGraphicsContextProvider alloc] init];
26+
[NSTextGraphicsContextProvider setTextGraphicsContextProviderClass:OpenSwiftUICoreTextGraphicsContextProvider.class];
27+
});
28+
return sharedPvdr;
29+
}
30+
31+
+ (id<NSTextGraphicsContext>)graphicsContextForApplicationFrameworkContext:(id)context {
32+
return OpenSwiftUICoreTextGraphicsContextProvider.sharedProvider;
33+
}
34+
35+
+ (Class)colorClassForApplicationFrameworkContext:(id)context {
36+
return OpenSwiftUICoreColor.class;
37+
}
38+
39+
- (BOOL)isFlipped {
40+
return YES;
41+
}
42+
43+
- (BOOL)isDrawingToScreen {
44+
return YES;
45+
}
46+
47+
- (CGContextRef)CGContext {
48+
return OpenSwiftUICoreGraphicsContext.current.CGContext;
49+
}
50+
51+
- (void)saveGraphicsState {
52+
CGContextRef context = OpenSwiftUICoreGraphicsContext.current.CGContext;
53+
if (context) {
54+
CGContextSaveGState(context);
55+
}
56+
}
57+
58+
- (void)restoreGraphicsState {
59+
CGContextRef context = OpenSwiftUICoreGraphicsContext.current.CGContext;
60+
if (context) {
61+
CGContextRestoreGState(context);
62+
}
63+
}
64+
65+
- (void)becomeCurrentGraphicsContextDuringBlock:(void (^)(void))block {
66+
block();
67+
}
68+
69+
@end
70+
71+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// NSTextGraphicsContext.h
3+
// OpenSwiftUI_SPI
4+
//
5+
// Audited for RELEASE_2024
6+
// Status: Complete
7+
8+
#ifndef NSTextGraphicsContext_h
9+
#define NSTextGraphicsContext_h
10+
11+
#include "OpenSwiftUIBase.h"
12+
13+
#if OPENSWIFTUI_TARGET_OS_DARWIN
14+
15+
#include <Foundation/Foundation.h>
16+
#include <CoreGraphics/CoreGraphics.h>
17+
18+
@protocol NSTextGraphicsContext <NSObject>
19+
20+
@required
21+
@property (readonly) CGContextRef CGContext;
22+
@property (readonly, getter=isFlipped) BOOL flipped;
23+
@property (readonly, getter=isDrawingToScreen) BOOL drawingToScreen;
24+
+ (id<NSTextGraphicsContext>)graphicsContextForApplicationFrameworkContext:(id)context NS_SWIFT_NAME(graphicsContext(forApplicationFrameworkContext:));
25+
26+
- (CGContextRef)CGContext;
27+
- (BOOL)isFlipped;
28+
- (BOOL)isDrawingToScreen;
29+
@end
30+
31+
#endif
32+
33+
#endif /* NSTextGraphicsContext_h */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// NSTextGraphicsContextInternal.h
3+
// OpenSwiftUI_SPI
4+
//
5+
// Audited for RELEASE_2024
6+
// Status: Complete
7+
8+
#ifndef NSTextGraphicsContextInternal_h
9+
#define NSTextGraphicsContextInternal_h
10+
11+
#include "OpenSwiftUIBase.h"
12+
13+
#if OPENSWIFTUI_TARGET_OS_DARWIN
14+
15+
#include "NSTextGraphicsContext.h"
16+
17+
@protocol NSTextGraphicsContextInternal <NSTextGraphicsContext>
18+
@required
19+
-(void)saveGraphicsState;
20+
-(void)restoreGraphicsState;
21+
@optional
22+
- (void)becomeCurrentGraphicsContextDuringBlock:(void (^)(void))block;
23+
@end
24+
25+
#endif
26+
27+
#endif /* NSTextGraphicsContextInternal_h */
28+
29+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// NSTextGraphicsContextProvider.h
3+
// OpenSwiftUI_SPI
4+
//
5+
// Audited for RELEASE_2024
6+
// Status: Complete
7+
8+
#ifndef NSTextGraphicsContextProvider_h
9+
#define NSTextGraphicsContextProvider_h
10+
11+
#include "OpenSwiftUIBase.h"
12+
13+
#if OPENSWIFTUI_TARGET_OS_DARWIN
14+
15+
#include "NSTextGraphicsContext.h"
16+
17+
@protocol NSTextGraphicsContextProvider <NSObject>
18+
19+
@required
20+
+ (id<NSTextGraphicsContext>)graphicsContextForApplicationFrameworkContext:(id)context;
21+
@optional
22+
+ (Class)colorClassForApplicationFrameworkContext:(id)context; // FIXME
23+
@end
24+
25+
@interface NSTextGraphicsContextProvider : NSObject
26+
+ (Class)textGraphicsContextProviderClass;
27+
+ (void)setTextGraphicsContextProviderClass:(Class)cls;
28+
+ (BOOL)textGraphicsContextProviderClassRespondsToColorQuery;
29+
+ (Class)__defaultColorClass;
30+
+ (Class)textGraphicsContextClass;
31+
+ (void)setTextGraphicsContextClass:(Class)cls;
32+
+ (void)setCurrentTextGraphicsContext:(id)context duringBlock:(void (^)(void))block;
33+
@end
34+
35+
#endif
36+
37+
#endif /* NSTextGraphicsContextProvider_h */
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,42 @@
11
//
2-
// OpenSwiftUICoreGraphicsContext.swift
2+
// OpenSwiftUICoreGraphicsContextTests.swift
33
// OpenSwiftUI_SPITests
44

55
import OpenSwiftUI_SPI
66
import Testing
77

88
#if canImport(Darwin)
9+
import CoreGraphics
10+
import Foundation
911

10-
struct OpenSwiftUICoreGraphicsContext {
12+
@MainActor
13+
struct OpenSwiftUICoreGraphicsContextTests {
14+
@Test
15+
func example() async throws {
16+
let width = 200
17+
let height = 200
18+
let colorSpace = CGColorSpaceCreateDeviceRGB()
19+
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue
1120

21+
guard let cgContext = CGContext(
22+
data: nil,
23+
width: width,
24+
height: height,
25+
bitsPerComponent: 8,
26+
bytesPerRow: 4 * width,
27+
space: colorSpace,
28+
bitmapInfo: bitmapInfo
29+
) else {
30+
fatalError("Could not create CGContext")
31+
}
32+
33+
let context = OpenSwiftUICoreGraphicsContext(cgContext: cgContext)
34+
#expect(OpenSwiftUICoreGraphicsContext.current == nil)
35+
context.push()
36+
#expect(OpenSwiftUICoreGraphicsContext.current != nil)
37+
context.pop()
38+
#expect(OpenSwiftUICoreGraphicsContext.current == nil)
39+
}
1240
}
1341

1442
#endif

0 commit comments

Comments
 (0)