Skip to content

Add Core Text graphics context provider and text graphics context providers #149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,47 @@

#if OPENSWIFTUI_TARGET_OS_DARWIN
#include <Foundation/Foundation.h>
#include "OpenSwiftUICoreTextGraphicsContextProvider.h"
#include "Shims/UIFoundation/NSTextGraphicsContextInternal.h"

#if OPENSWIFTUI_TARGET_OS_OSX
#include <AppKit/AppKit.h>
#endif

static _Thread_local __unsafe_unretained OpenSwiftUICoreGraphicsContext * _current = NULL;

dispatch_once_t _once;
#if OPENSWIFTUI_TARGET_OS_OSX
Class _nsGraphicsContextClass;
#else
IMP _pushContextIMP;
IMP _popContextIMP;
#endif

@interface OpenSwiftUICoreGraphicsContext () {
OpenSwiftUICoreGraphicsContext *_next;
CGContextRef _ctx;
}
#if !OPENSWIFTUI_TARGET_OS_OSX
- (id)__createsImages;
#endif
@end

@implementation OpenSwiftUICoreGraphicsContext

- (instancetype)initWithCGContext:(CGContextRef)ctx {
static dispatch_once_t __once;
dispatch_once(&__once, ^{
dispatch_once(&_once, ^{
#if OPENSWIFTUI_TARGET_OS_OSX
_nsGraphicsContextClass = NSClassFromString(@"NSGraphicsContext");
#else
Class renderClass = NSClassFromString(@"UIGraphicsRenderer");
if (renderClass) {
_pushContextIMP = [renderClass instanceMethodForSelector:@selector(pushContext:)];
_popContextIMP = [renderClass instanceMethodForSelector:@selector(popContext:)];
} else {
// TODO: CoreTextGraphicsContextProvider.sharedProvider
InitializeCoreTextGraphicsContextProvider();
}
#endif
});
self = [super init];
if (self) {
Expand All @@ -43,25 +59,43 @@ - (instancetype)initWithCGContext:(CGContextRef)ctx {
}

- (void)push {
#if OPENSWIFTUI_TARGET_OS_OSX
_next = _current;
_current = self;
if (_nsGraphicsContextClass) {
[_nsGraphicsContextClass saveGraphicsState];
[_nsGraphicsContextClass graphicsContextWithCGContext: _ctx flipped: YES];
NSGraphicsContext *graphicsContext = [_nsGraphicsContextClass graphicsContextWithCGContext: _ctx flipped: YES];
[_nsGraphicsContextClass setCurrentContext: graphicsContext];
}
#else
_next = _current;
_current = self;
if (_pushContextIMP != NULL && _popContextIMP != NULL) {
typedef BOOL (*FUNC)(id, SEL, OpenSwiftUICoreGraphicsContext *);
((FUNC)(_pushContextIMP))(NULL, @selector(pushContext:), _current);
}
#endif
}

- (void)pop {
#if OPENSWIFTUI_TARGET_OS_OSX
_current = _next;
[_nsGraphicsContextClass restoreGraphicsState];
#else
_current = _next;
if (_pushContextIMP != NULL && _popContextIMP != NULL) {
typedef BOOL (*FUNC)(id, SEL, OpenSwiftUICoreGraphicsContext *);
((FUNC)(_popContextIMP))(NULL, @selector(popContext:), _current);
}
#endif
}

#if !OPENSWIFTUI_TARGET_OS_OSX
- (id)__createsImages {
return nil;
}
#endif

- (CGContextRef)CGContext {
return _ctx;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// OpenSwiftUICoreTextGraphicsContextProvider.h
// OpenSwiftUI_SPI
//
// Audited for RELEASE_2024
// Status: Complete

#ifndef OpenSwiftUICoreTextGraphicsContextProvider_h
#define OpenSwiftUICoreTextGraphicsContextProvider_h

#include "OpenSwiftUIBase.h"

#if OPENSWIFTUI_TARGET_OS_DARWIN

#include "Shims/UIFoundation/NSTextGraphicsContext.h"
#include "Shims/UIFoundation/NSTextGraphicsContextProvider.h"

void InitializeCoreTextGraphicsContextProvider(void);

@interface OpenSwiftUICoreTextGraphicsContextProvider : NSObject<NSTextGraphicsContextProvider, NSTextGraphicsContext>

@property (readonly) CGContextRef CGContext;
@property (readonly, getter=isFlipped) BOOL flipped;
@property (readonly, getter=isDrawingToScreen) BOOL drawingToScreen;

+ (instancetype)sharedProvider;
+ (id<NSTextGraphicsContext>)graphicsContextForApplicationFrameworkContext:(id)context;
+ (Class)colorClassForApplicationFrameworkContext:(id)context;

- (BOOL)isFlipped;
- (BOOL)isDrawingToScreen;
- (CGContextRef)CGContext;
- (void)saveGraphicsState;
- (void)restoreGraphicsState;
- (void)becomeCurrentGraphicsContextDuringBlock:(void (^)(void))block;

@end

#endif

#endif /* OpenSwiftUICoreTextGraphicsContextProvider_h */
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// OpenSwiftUICoreTextGraphicsContextProvider.m
// OpenSwiftUI_SPI
//
// Audited for RELEASE_2024
// Status: Complete

#include "OpenSwiftUICoreTextGraphicsContextProvider.h"

#if OPENSWIFTUI_TARGET_OS_DARWIN

#include "OpenSwiftUICoreColor.h"
#include "OpenSwiftUICoreGraphicsContext.h"

void InitializeCoreTextGraphicsContextProvider(void) {
(void)OpenSwiftUICoreTextGraphicsContextProvider.sharedProvider;
}

@implementation OpenSwiftUICoreTextGraphicsContextProvider

+ (instancetype)sharedProvider {
static OpenSwiftUICoreTextGraphicsContextProvider *sharedPvdr = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedPvdr = [[OpenSwiftUICoreTextGraphicsContextProvider alloc] init];
[NSTextGraphicsContextProvider setTextGraphicsContextProviderClass:OpenSwiftUICoreTextGraphicsContextProvider.class];
});
return sharedPvdr;
}

+ (id<NSTextGraphicsContext>)graphicsContextForApplicationFrameworkContext:(id)context {
return OpenSwiftUICoreTextGraphicsContextProvider.sharedProvider;
}

+ (Class)colorClassForApplicationFrameworkContext:(id)context {
return OpenSwiftUICoreColor.class;
}

- (BOOL)isFlipped {
return YES;
}

- (BOOL)isDrawingToScreen {
return YES;
}

- (CGContextRef)CGContext {
return OpenSwiftUICoreGraphicsContext.current.CGContext;
}

- (void)saveGraphicsState {
CGContextRef context = OpenSwiftUICoreGraphicsContext.current.CGContext;
if (context) {
CGContextSaveGState(context);
}
}

- (void)restoreGraphicsState {
CGContextRef context = OpenSwiftUICoreGraphicsContext.current.CGContext;
if (context) {
CGContextRestoreGState(context);
}
}

- (void)becomeCurrentGraphicsContextDuringBlock:(void (^)(void))block {
block();
}

@end

#endif
33 changes: 33 additions & 0 deletions Sources/OpenSwiftUI_SPI/Shims/UIFoundation/NSTextGraphicsContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// NSTextGraphicsContext.h
// OpenSwiftUI_SPI
//
// Audited for RELEASE_2024
// Status: Complete

#ifndef NSTextGraphicsContext_h
#define NSTextGraphicsContext_h

#include "OpenSwiftUIBase.h"

#if OPENSWIFTUI_TARGET_OS_DARWIN

#include <Foundation/Foundation.h>
#include <CoreGraphics/CoreGraphics.h>

@protocol NSTextGraphicsContext <NSObject>

@required
@property (readonly) CGContextRef CGContext;
@property (readonly, getter=isFlipped) BOOL flipped;
@property (readonly, getter=isDrawingToScreen) BOOL drawingToScreen;
+ (id<NSTextGraphicsContext>)graphicsContextForApplicationFrameworkContext:(id)context NS_SWIFT_NAME(graphicsContext(forApplicationFrameworkContext:));

- (CGContextRef)CGContext;
- (BOOL)isFlipped;
- (BOOL)isDrawingToScreen;
@end

#endif

#endif /* NSTextGraphicsContext_h */
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// NSTextGraphicsContextInternal.h
// OpenSwiftUI_SPI
//
// Audited for RELEASE_2024
// Status: Complete

#ifndef NSTextGraphicsContextInternal_h
#define NSTextGraphicsContextInternal_h

#include "OpenSwiftUIBase.h"

#if OPENSWIFTUI_TARGET_OS_DARWIN

#include "NSTextGraphicsContext.h"

@protocol NSTextGraphicsContextInternal <NSTextGraphicsContext>
@required
-(void)saveGraphicsState;
-(void)restoreGraphicsState;
@optional
- (void)becomeCurrentGraphicsContextDuringBlock:(void (^)(void))block;
@end

#endif

#endif /* NSTextGraphicsContextInternal_h */


Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// NSTextGraphicsContextProvider.h
// OpenSwiftUI_SPI
//
// Audited for RELEASE_2024
// Status: Complete

#ifndef NSTextGraphicsContextProvider_h
#define NSTextGraphicsContextProvider_h

#include "OpenSwiftUIBase.h"

#if OPENSWIFTUI_TARGET_OS_DARWIN

#include "NSTextGraphicsContext.h"

@protocol NSTextGraphicsContextProvider <NSObject>

@required
+ (id<NSTextGraphicsContext>)graphicsContextForApplicationFrameworkContext:(id)context;
@optional
+ (Class)colorClassForApplicationFrameworkContext:(id)context; // FIXME
@end

@interface NSTextGraphicsContextProvider : NSObject
+ (Class)textGraphicsContextProviderClass;
+ (void)setTextGraphicsContextProviderClass:(Class)cls;
+ (BOOL)textGraphicsContextProviderClassRespondsToColorQuery;
+ (Class)__defaultColorClass;
+ (Class)textGraphicsContextClass;
+ (void)setTextGraphicsContextClass:(Class)cls;
+ (void)setCurrentTextGraphicsContext:(id)context duringBlock:(void (^)(void))block;
@end

#endif

#endif /* NSTextGraphicsContextProvider_h */
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
//
// OpenSwiftUICoreGraphicsContext.swift
// OpenSwiftUICoreGraphicsContextTests.swift
// OpenSwiftUI_SPITests

import OpenSwiftUI_SPI
import Testing

#if canImport(Darwin)
import CoreGraphics
import Foundation

struct OpenSwiftUICoreGraphicsContext {
@MainActor
struct OpenSwiftUICoreGraphicsContextTests {
@Test
func example() async throws {
let width = 200
let height = 200
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue

guard let cgContext = CGContext(
data: nil,
width: width,
height: height,
bitsPerComponent: 8,
bytesPerRow: 4 * width,
space: colorSpace,
bitmapInfo: bitmapInfo
) else {
fatalError("Could not create CGContext")
}

let context = OpenSwiftUICoreGraphicsContext(cgContext: cgContext)
#expect(OpenSwiftUICoreGraphicsContext.current == nil)
context.push()
#expect(OpenSwiftUICoreGraphicsContext.current != nil)
context.pop()
#expect(OpenSwiftUICoreGraphicsContext.current == nil)
}
}

#endif
Loading