Skip to content

Commit

Permalink
Implementation of CTFrame via CFRuntimebase
Browse files Browse the repository at this point in the history
- Implemented CTFrame via Runtimebase
- Removed associated Foundation dependency
- Improvements to the code
- Performance gain

Fixes microsoft#2359
  • Loading branch information
msft-Jeyaram committed Apr 11, 2017
1 parent abf6990 commit 6438ffc
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 92 deletions.
125 changes: 58 additions & 67 deletions Frameworks/CoreText/CTFrame.mm
Original file line number Diff line number Diff line change
Expand Up @@ -28,56 +28,45 @@
const CFStringRef kCTFrameClippingPathsAttributeName = CFSTR("kCTFrameClippingPathsAttributeName");
const CFStringRef kCTFramePathClippingPathAttributeName = CFSTR("kCTFramePathClippingPathAttributeName");

@implementation _CTFrame : NSObject
- (instancetype)init {
if ([super init]) {
_lines.attach([NSMutableArray new]);
}

return self;
}

@end

/**
@Status Interoperable
@Notes
*/
CFRange CTFrameGetStringRange(CTFrameRef frame) {
_CTFrame* framePtr = static_cast<_CTFrame*>(frame);
RETURN_RESULT_IF_NULL(frame, CFRangeMake(0, 0));

CFIndex count = 0;
if (framePtr) {
for (_CTLine* line in static_cast<id<NSFastEnumeration>>(framePtr->_lines)) {
count += line->_strRange.length;
}
CFIndex len = CFArrayGetCount(frame->_lines);

for (CFIndex index = 0; index < len; ++index) {
_CTLine* line = static_cast<_CTLine*>(CFArrayGetValueAtIndex(frame->_lines, index));
count += line->_strRange.length;
}

return CFRangeMake(0, count);
}

/**
@Status Interoperable
@Notes
*/
CFRange CTFrameGetVisibleStringRange(CTFrameRef frame) {
_CTFrame* framePtr = static_cast<_CTFrame*>(frame);
RETURN_RESULT_IF_NULL(frame, CFRangeMake(0, 0));

CFIndex count = 0;
if (framePtr) {
for (size_t i = 0; i < framePtr->_lineOrigins.size(); ++i) {
if (framePtr->_lineOrigins[i].y < framePtr->_frameRect.size.height) {
_CTLine* line = static_cast<_CTLine*>([framePtr->_lines objectAtIndex:i]);
count += line->_strRange.length;
}
for (size_t index = 0; index < frame->_lineOrigins.size(); ++index) {
if (frame->_lineOrigins[index].y < frame->_frameRect.size.height) {
_CTLine* line = static_cast<_CTLine*>(CFArrayGetValueAtIndex(frame->_lines, index));
count += line->_strRange.length;
}
}
return CFRangeMake(0, count);
}

/**
@Status Interoperable
@Notes
*/
CGPathRef CTFrameGetPath(CTFrameRef frame) {
return frame ? static_cast<_CTFrame*>(frame)->_path.get() : nil;
RETURN_NULL_IF(!frame);
return frame->_path;
}

/**
Expand All @@ -93,61 +82,62 @@ CFDictionaryRef CTFrameGetFrameAttributes(CTFrameRef frame) {
@Status Interoperable
*/
CFArrayRef CTFrameGetLines(CTFrameRef frame) {
return frame ? static_cast<CFArrayRef>(static_cast<_CTFrame*>(frame)->_lines.get()) : nil;
RETURN_NULL_IF(!frame);
return frame->_lines;
}

/**
@Status Interoperable
*/
void CTFrameGetLineOrigins(CTFrameRef frameRef, CFRange range, CGPoint origins[]) {
_CTFrame* frame = static_cast<_CTFrame*>(frameRef);
if (frame) {
_boundedCopy(range, frame->_lineOrigins.size(), frame->_lineOrigins.data(), origins);
}
void CTFrameGetLineOrigins(CTFrameRef frame, CFRange range, CGPoint origins[]) {
RETURN_IF(!frame);
_boundedCopy(range, frame->_lineOrigins.size(), frame->_lineOrigins.data(), origins);
}

/**
@Status Interoperable
*/
void CTFrameDraw(CTFrameRef frameRef, CGContextRef ctx) {
_CTFrame* frame = static_cast<_CTFrame*>(frameRef);
if (frame && ctx) {
std::vector<GlyphRunData> runs;

for (size_t i = 0; i < frame->_lineOrigins.size() && (frame->_lineOrigins[i].y < frame->_frameRect.size.height); ++i) {
_CTLine* line = static_cast<_CTLine*>([frame->_lines objectAtIndex:i]);
CGPoint relativePosition = frame->_lineOrigins[i];
for (size_t j = 0; j < [line->_runs count]; ++j) {
_CTRun* curRun = [line->_runs objectAtIndex:j];
if (j > 0) {
// Adjusts x position relative to the last run drawn
relativePosition.x += curRun->_relativeXOffset;
}
runs.emplace_back(GlyphRunData{ &curRun->_dwriteGlyphRun, relativePosition, (CFDictionaryRef)curRun->_attributes.get() });
void CTFrameDraw(CTFrameRef frame, CGContextRef ctx) {
RETURN_IF(!ctx);
RETURN_IF(!frame);

std::vector<GlyphRunData> runs;

for (size_t i = 0; i < frame->_lineOrigins.size() && (frame->_lineOrigins[i].y < frame->_frameRect.size.height); ++i) {
_CTLine* line = static_cast<_CTLine*>(CFArrayGetValueAtIndex(frame->_lines, i));

CGPoint relativePosition = frame->_lineOrigins[i];

for (size_t j = 0; j < [line->_runs count]; ++j) {
_CTRun* curRun = [line->_runs objectAtIndex:j];
if (j > 0) {
// Adjusts x position relative to the last run drawn
relativePosition.x += curRun->_relativeXOffset;
}
runs.emplace_back(GlyphRunData{ &curRun->_dwriteGlyphRun, relativePosition, (CFDictionaryRef)curRun->_attributes.get() });
}
}

if (!runs.empty()) {
// Need to invert and translate coordinates so frame draws from top-left
CGRect boundingRect = CGPathGetBoundingBox(frame->_path.get());
CGContextTranslateCTM(ctx, 0, boundingRect.size.height);
if (!runs.empty()) {
// Need to invert and translate coordinates so frame draws from top-left
CGRect boundingRect = CGPathGetBoundingBox(frame->_path);
CGContextTranslateCTM(ctx, 0, boundingRect.size.height);

// Invert Text Matrix and CTM so glyphs are drawn in correct orientation and position
CGAffineTransform textMatrix = CGContextGetTextMatrix(ctx);
CGContextSetTextMatrix(ctx, CGAffineTransformMake(textMatrix.a, -textMatrix.b, textMatrix.c, -textMatrix.d, 0, 0));
CGContextScaleCTM(ctx, 1.0f, -1.0f);
// Invert Text Matrix and CTM so glyphs are drawn in correct orientation and position
CGAffineTransform textMatrix = CGContextGetTextMatrix(ctx);
CGContextSetTextMatrix(ctx, CGAffineTransformMake(textMatrix.a, -textMatrix.b, textMatrix.c, -textMatrix.d, 0, 0));
CGContextScaleCTM(ctx, 1.0f, -1.0f);

_CGContextPushBeginDraw(ctx);
auto popEnd = wil::ScopeExit([&]() {
// Restore CTM and Text Matrix to values before we modified them
CGContextSetTextMatrix(ctx, textMatrix);
CGContextScaleCTM(ctx, 1.0f, -1.0f);
CGContextTranslateCTM(ctx, 0, -boundingRect.size.height);
_CGContextPopEndDraw(ctx);
});
_CGContextPushBeginDraw(ctx);
auto popEnd = wil::ScopeExit([&]() {
// Restore CTM and Text Matrix to values before we modified them
CGContextSetTextMatrix(ctx, textMatrix);
CGContextScaleCTM(ctx, 1.0f, -1.0f);
CGContextTranslateCTM(ctx, 0, -boundingRect.size.height);
_CGContextPopEndDraw(ctx);
});

_CGContextDrawGlyphRuns(ctx, runs.data(), runs.size());
}
_CGContextDrawGlyphRuns(ctx, runs.data(), runs.size());
}
}

Expand All @@ -162,5 +152,6 @@ CFTypeID CTFrameGetTypeID() {

// Convenience private function for NSString+UIKitAdditions
CGSize _CTFrameGetSize(CTFrameRef frame) {
return frame ? static_cast<_CTFrame*>(frame)->_frameRect.size : CGSize{};
RETURN_RESULT_IF_NULL(frame, CGSize{});
return frame->_frameRect.size;
}
8 changes: 5 additions & 3 deletions Frameworks/CoreText/CTFramesetter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ CTFrameRef CTFramesetterCreateFrame(CTFramesetterRef framesetter, CFRange range,
range.length = CFAttributedStringGetLength(attributedString) - range.location;
}

StrongId<_CTFrame> ret = _DWriteGetFrame(attributedString, range, frameRect);
ret->_path.reset(CGPathRetain(path));
woc::StrongCF<__CTFrame*> ret;
ret =
const_cast<__CTFrame*>(_DWriteGetFrame(static_cast<CFAttributedStringRef>(typesetter->_attributedString.get()), range, frameRect));
ret->_path = path;
ret->_frameRect.origin = frameRect.origin;

// Trying to access attributes without any text will throw an error
Expand Down Expand Up @@ -102,7 +104,7 @@ CTFrameRef CTFramesetterCreateFrame(CTFramesetterRef framesetter, CFRange range,
(lineBreakMode == kCTLineBreakByClipping || lineBreakMode == kCTLineBreakByTruncatingHead ||
lineBreakMode == kCTLineBreakByTruncatingTail || lineBreakMode == kCTLineBreakByTruncatingMiddle)) {
for (size_t i = 0; i < ret->_lineOrigins.size(); ++i) {
_CTLine* line = [ret->_lines objectAtIndex:i];
_CTLine* line = static_cast<_CTLine*>(CFArrayGetValueAtIndex(ret->_lines, i));
if (line->_width > frameRect.size.width) {
ret->_lineOrigins[i].x -= line->_relativeXOffset;
}
Expand Down
2 changes: 1 addition & 1 deletion Frameworks/CoreText/DWriteWrapper_CoreText.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct _DWriteGlyphRunDetails {
bool _CloneDWriteGlyphRun(_In_ DWRITE_GLYPH_RUN const* src, _Outptr_ DWRITE_GLYPH_RUN* dest);

CGSize _DWriteGetFrameSize(CFAttributedStringRef string, CFRange range, CGSize maxSize, CFRange* fitRange);
_CTFrame* _DWriteGetFrame(CFAttributedStringRef string, CFRange range, CGRect frameSize);
CTFrameRef _DWriteGetFrame(CFAttributedStringRef string, CFRange range, CGRect frameSize);
_CTLine* _DWriteGetLine(CFAttributedStringRef string);

// DWriteWrapper functions relating to CTFont, CTFontDescriptor
Expand Down
31 changes: 16 additions & 15 deletions Frameworks/CoreText/DWriteWrapper_CoreText.mm
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,9 @@ HRESULT STDMETHODCALLTYPE GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _
*/
static _CTLine* _DWriteGetLine(CFAttributedStringRef string) {
CFRange range = CFRangeMake(0, CFAttributedStringGetLength(string));
_CTFrame* frame = _DWriteGetFrame(string, range, CGRectMake(0, 0, FLT_MAX, FLT_MAX));
if ([frame->_lines count] > 0) {
return [[frame->_lines firstObject] retain];
CTFrameRef frame = _DWriteGetFrame(string, range, CGRectMake(0, 0, FLT_MAX, FLT_MAX));
if (CFArrayGetCount(frame->_lines) > 0) {
return [static_cast<_CTLine*>(CFArrayGetValueAtIndex(frame->_lines, 0)) retain];
}

return [_CTLine new];
Expand All @@ -410,15 +410,15 @@ HRESULT STDMETHODCALLTYPE GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _
* @parameter range attributed string range to use.
* @parameter frameSize size parameters of the frame to fit the text into.
*
* @return _CTFrame* created using the given parameters
* @return CTFrameRef created using the given parameters
*/
static _CTFrame* _DWriteGetFrame(CFAttributedStringRef string, CFRange range, CGRect frameSize) {
static CTFrameRef _DWriteGetFrame(CFAttributedStringRef string, CFRange range, CGRect frameSize) {
RETURN_NULL_IF(!string);

_CTFrame* frame = [[_CTFrame new] autorelease];
if (range.length <= 0) {
return frame;
}
__CTFrame* frame = __CTFrame::CreateInstance(kCFAllocatorDefault);
CFAutorelease(frame);

RETURN_RESULT_IF(range.length <= 0, frame);

ComPtr<IDWriteTextLayout> textLayout;
RETURN_NULL_IF_FAILED(__DWriteTextLayoutCreate(string, range, frameSize, &textLayout));
Expand Down Expand Up @@ -446,7 +446,8 @@ HRESULT STDMETHODCALLTYPE GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _

while (i < numOfGlyphRuns) {
_CTLine* line = [[_CTLine new] autorelease];
NSMutableArray<_CTRun*>* runs = [NSMutableArray array];

auto runs = woc::MakeStrongCF<CFMutableArrayRef>(CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks));
uint32_t stringRange = 0;
uint32_t glyphCount = 0;
prevXPosForDraw = 0;
Expand Down Expand Up @@ -495,18 +496,18 @@ HRESULT STDMETHODCALLTYPE GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _
line->_width += glyphRunDetails._dwriteGlyphRun[j].glyphAdvances[index];
}

[runs addObject:run];
CFArrayAppendValue(runs, run);
stringRange += run->_range.length;
glyphCount += glyphRunDetails._dwriteGlyphRun[j].glyphCount;
}

// Fast-forward i to start on the next line
i = j;

if ([runs count] > 0) {
if (CFArrayGetCount(runs) > 0) {
prevYPosForDraw = yPos;
line->_runs = runs;
_CTRun* firstRun = static_cast<_CTRun*>(runs[0]);
line->_runs = static_cast<NSMutableArray*>(runs.get());
_CTRun* firstRun = static_cast<_CTRun*>(CFArrayGetValueAtIndex(runs, 0));
line->_strRange.location = firstRun->_range.location;
line->_strRange.length = stringRange;
line->_glyphCount = glyphCount;
Expand All @@ -521,7 +522,7 @@ HRESULT STDMETHODCALLTYPE GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _
lineOrigin = { firstRun->_glyphOrigins[0].x, firstRun->_glyphOrigins[0].y };
}

[frame->_lines addObject:line];
CFArrayAppendValue(frame->_lines, line);
frame->_lineOrigins.emplace_back(lineOrigin);
}
}
Expand Down
18 changes: 12 additions & 6 deletions Frameworks/include/CoreTextInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#import <CoreText/CoreText.h>
#import <CoreText/CTParagraphStyle.h>
#import <CFCppBase.h>
#import "Starboard.h"
#include <COMIncludes.h>
#import <DWrite.h>
Expand Down Expand Up @@ -73,14 +74,19 @@ CFAttributedStringRef _CTTypesetterGetAttributedString(CTTypesetterRef typesette
}
@end

@interface _CTFrame : NSObject {
@public
#pragma region CTFrame
struct __CTFrame : CoreFoundation::CppBase<__CTFrame> {
__CTFrame() : _lines(CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)) {
}

CGRect _frameRect;
woc::unique_cf<CGPathRef> _path;
woc::StrongCF<CGPathRef> _path;
std::vector<CGPoint> _lineOrigins;
StrongId<NSMutableArray<_CTLine*>> _lines;
}
@end
// Hold CTLineRef
woc::StrongCF<CFMutableArrayRef> _lines;
};

#pragma endregion CTFrame

// Private helper methods for UIKit
CORETEXT_EXPORT CGSize _CTFrameGetSize(CTFrameRef frame);
Expand Down

0 comments on commit 6438ffc

Please sign in to comment.