Skip to content
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

Improve perf for CTFramesetterSuggestFrameSizeWithConstraints #1603

Merged
merged 7 commits into from
Jan 12, 2017
Merged
Show file tree
Hide file tree
Changes from 6 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
73 changes: 31 additions & 42 deletions Frameworks/CoreText/CTFramesetter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,27 @@
#import "CGPathInternal.h"
#import "DWriteWrapper_CoreText.h"

using namespace std;

@implementation _CTFramesetter : NSObject
@end

static _CTFrame* __CreateFrame(_CTFramesetter* framesetter, CGRect frameRect, CFRange range) {
RETURN_NULL_IF(framesetter == nil);
/**
@Status Interoperable
*/
CTFramesetterRef CTFramesetterCreateWithAttributedString(CFAttributedStringRef string) {
_CTFramesetter* ret = [_CTFramesetter alloc];
ret->_typesetter = static_cast<_CTTypesetter*>(CTTypesetterCreateWithAttributedString(string));
return static_cast<CTFramesetterRef>(ret);
}

/**
@Status Caveat
@Notes frameAttributes parameter ignored
*/
CTFrameRef CTFramesetterCreateFrame(CTFramesetterRef framesetterRef, CFRange range, CGPathRef path, CFDictionaryRef frameAttributes) {
RETURN_NULL_IF(framesetterRef == nil || path == nullptr);
CGRect frameRect;
_CGPathGetBoundingBoxInternal(path, &frameRect);
_CTFramesetter* framesetter = static_cast<_CTFramesetter*>(framesetterRef);

// Call _DWriteWrapper to get _CTLine object list that makes up this frame
_CTTypesetter* typesetter = static_cast<_CTTypesetter*>(framesetter->_typesetter);
Expand All @@ -36,10 +50,12 @@ @implementation _CTFramesetter : NSObject
}

StrongId<_CTFrame> ret = _DWriteGetFrame(static_cast<CFAttributedStringRef>(typesetter->_attributedString.get()), range, frameRect);
ret->_path.reset(CGPathRetain(path));
ret->_frameRect.origin = frameRect.origin;

// Trying to access attributes without any text will throw an error
if (range.length <= 0L) {
return ret.detach();
return static_cast<CTFrameRef>(ret.detach());
}

CTParagraphStyleRef settings =
Expand All @@ -48,7 +64,7 @@ @implementation _CTFramesetter : NSObject
effectiveRange:nullptr]);

if (settings == nullptr) {
return ret.detach();
return static_cast<CTFrameRef>(ret.detach());
}

// DWrite only gives manual control of lineheight when it is constant through a frame
Expand Down Expand Up @@ -87,31 +103,7 @@ @implementation _CTFramesetter : NSObject
}
}

return ret.detach();
}

/**
@Status Interoperable
*/
CTFramesetterRef CTFramesetterCreateWithAttributedString(CFAttributedStringRef string) {
_CTFramesetter* ret = [_CTFramesetter alloc];
ret->_typesetter = static_cast<_CTTypesetter*>(CTTypesetterCreateWithAttributedString(string));
return static_cast<CTFramesetterRef>(ret);
}

/**
@Status Caveat
@Notes frameAttributes parameter ignored
*/
CTFrameRef CTFramesetterCreateFrame(CTFramesetterRef framesetter, CFRange stringRange, CGPathRef path, CFDictionaryRef frameAttributes) {
CGRect frameSize;
_CGPathGetBoundingBoxInternal(path, &frameSize);

_CTFrame* ret = __CreateFrame(static_cast<_CTFramesetter*>(framesetter), frameSize, stringRange);
ret->_path.reset(CGPathRetain(path));
ret->_frameRect.origin = frameSize.origin;

return static_cast<CTFrameRef>(ret);
return static_cast<CTFrameRef>(ret.detach());
}

/**
Expand All @@ -123,23 +115,20 @@ CTTypesetterRef CTFramesetterGetTypesetter(CTFramesetterRef framesetter) {
}

/**
@Status Interoperable
@Status Caveat
@Notes frameAttributes parameter ignored
@Notes
*/
CGSize CTFramesetterSuggestFrameSizeWithConstraints(
CTFramesetterRef framesetter, CFRange stringRange, CFDictionaryRef frameAttributes, CGSize constraints, CFRange* fitRange) {
CGRect frameSize = CGRectZero;
frameSize.size = constraints;

_CTFrame* frame = __CreateFrame(static_cast<_CTFramesetter*>(framesetter), frameSize, stringRange);
CGSize ret = frame ? frame->_frameRect.size : CGSizeZero;

if (fitRange) {
*fitRange = CTFrameGetVisibleStringRange(static_cast<CTFrameRef>(frame));
if (framesetter == nil) {
return CGSizeZero;
}

[frame release];
return ret;
CFAttributedStringRef string =
static_cast<CFAttributedStringRef>(static_cast<_CTFramesetter*>(framesetter)->_typesetter->_attributedString.get());

return _DWriteGetFrameSize(string, stringRange, constraints, fitRange);
}

/**
Expand Down
1 change: 1 addition & 0 deletions Frameworks/CoreText/DWriteWrapper_CoreText.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,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);
_CTLine* _DWriteGetLine(CFAttributedStringRef string);

Expand Down
58 changes: 58 additions & 0 deletions Frameworks/CoreText/DWriteWrapper_CoreText.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#import <StringHelpers.h>
#import <vector>
#import <iterator>
#import <numeric>

using namespace std;
using namespace Microsoft::WRL;
Expand Down Expand Up @@ -522,4 +523,61 @@ HRESULT STDMETHODCALLTYPE GetPixelsPerDip(_In_opt_ void* clientDrawingContext, _
}

return frame;
}

static CGSize _DWriteGetFrameSize(CFAttributedStringRef string, CFRange range, CGSize maxSize, CFRange* fitRange) {
CGSize ret = CGSizeZero;

// Treat range.length of 0 as unlimited length
if (range.length == 0L) {
range.length = CFAttributedStringGetLength(string) - range.location;
}

// No text to draw, just return CGSizeZero
if (!string || range.length <= 0L) {
return ret;
}
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"If the length portion of the range is set to 0, then the framesetter continues to add lines until it runs out of text or space."
This does not seem to match that behaviour #Closed

Copy link
Contributor Author

@aballway aballway Dec 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wut? #Closed


ComPtr<IDWriteTextLayout> textLayout;
if (FAILED(__DWriteTextLayoutCreate(string, range, { CGPointZero, maxSize }, &textLayout))) {
return ret;
}

DWRITE_TEXT_METRICS textMetrics;
if (FAILED(textLayout->GetMetrics(&textMetrics))) {
return ret;
}

// TODO:: find more precise value than 1.0 to increase width by to fully enclose frame
ret.width = std::min(maxSize.width, textMetrics.widthIncludingTrailingWhitespace + 1.0f);
ret.height = std::min(maxSize.height, textMetrics.height);

if (fitRange) {
*fitRange = { range.location, 0L };
uint32_t lineCount = 0;

// Should return E_NOT_SUFFICIENT_BUFFER and popluate lineCount
if (textLayout->GetLineMetrics(nullptr, 0, &lineCount) != E_NOT_SUFFICIENT_BUFFER) {
return ret;
}

std::vector<DWRITE_LINE_METRICS> metrics(lineCount);
Copy link
Contributor

@msft-Jeyaram msft-Jeyaram Dec 21, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

::vector<DWRITE_LINE_METRICS> metrics(lineCount); [](start = 11, length = 49)

since this is about performance, use array rather than a vector here (you will have a slight gain) #Closed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I guess you might not know the size? nvm


In reply to: 93508769 [](ancestors = 93508769)

if (FAILED(textLayout->GetLineMetrics(metrics.data(), lineCount, &lineCount))) {
return ret;
}

float totalHeight = 0;
CFIndex endPos = range.location;
for (auto metric : metrics) {
totalHeight += metric.baseline;
if (totalHeight > ret.height) {
break;
}
endPos += metric.length;
}

fitRange->length = endPos;
}

return ret;
}
2 changes: 1 addition & 1 deletion Frameworks/UIKit/NSParagraphStyle.mm
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ + (BOOL)supportsSecureCoding {
- (CTParagraphStyleRef)_createCTParagraphStyle {
CTLineBreakMode lineBreakMode = self.lineBreakMode;
CTWritingDirection writingDirection = self.baseWritingDirection;
CTTextAlignment alignment = _NSTextAlignmentToCTTextAlignment(self.alignment);
CTTextAlignment alignment = NSTextAlignmentToCTTextAlignment(self.alignment);
CTParagraphStyleSetting settings[14] =
{ { kCTParagraphStyleSpecifierAlignment, sizeof(CTTextAlignment), &alignment },
{ kCTParagraphStyleSpecifierFirstLineHeadIndent, sizeof(CGFloat), &_firstLineHeadIndent },
Expand Down
Loading