Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Changes from 1 commit
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 @@ -197,12 +197,14 @@ @interface FlutterClippingMaskView ()
// information about screen scale.
@property(nonatomic) CATransform3D reverseScreenScale;

- (void)addTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;
- (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;

@end

@implementation FlutterClippingMaskView {
CGMutablePathRef pathSoFar_;
std::vector<fml::CFRef<CGPathRef>> paths_;
BOOL containsNonRectPath_;
CGRect rectSoFar_;
}

- (instancetype)initWithFrame:(CGRect)frame {
Expand All @@ -213,7 +215,8 @@ - (instancetype)initWithFrame:(CGRect)frame screenScale:(CGFloat)screenScale {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = UIColor.clearColor;
_reverseScreenScale = CATransform3DMakeScale(1 / screenScale, 1 / screenScale, 1);
pathSoFar_ = CGPathCreateMutable();
rectSoFar_ = self.bounds;
containsNonRectPath_ = NO;
}
return self;
}
Expand All @@ -227,16 +230,13 @@ - (CAShapeLayer*)shapeLayer {
}

- (void)reset {
CGPathRelease(pathSoFar_);
pathSoFar_ = CGPathCreateMutable();
paths_.clear();
rectSoFar_ = self.bounds;
containsNonRectPath_ = NO;
[self shapeLayer].path = nil;
[self setNeedsDisplay];
}

- (void)dealloc {
CGPathRelease(pathSoFar_);
}

// In some scenarios, when we add this view as a maskView of the ChildClippingView, iOS added
// this view as a subview of the ChildClippingView.
// This results this view blocking touch events on the ChildClippingView.
Expand All @@ -246,16 +246,54 @@ - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
return NO;
}

- (void)drawRect:(CGRect)rect {
// It's hard to compute intersection of arbitrary paths.
// So we fallback to software rendering if the mask contains multiple paths and any non-rect path.
if (containsNonRectPath_ && paths_.size() > 1) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);

// For mask view, only the alpha channel is used.
CGContextSetAlpha(context, 1);

for (size_t i = 0; i < paths_.size(); i++) {
CGContextAddPath(context, paths_.at(i));
CGContextClip(context);
}
CGContextFillRect(context, rect);
CGContextRestoreGState(context);
} else {
[super drawRect:rect];
if (![self shapeLayer].path) {
if (paths_.size() == 1) {
// A single path, either rect or non-rect.
[self shapeLayer].path = paths_.at(0);
} else {
// Multiple paths, all must be rect.
[self shapeLayer].path = CGPathCreateWithRect(rectSoFar_, nil);
}
}
}
}

- (void)clipRect:(const SkRect&)clipSkRect matrix:(const SkMatrix&)matrix {
CGRect clipRect = GetCGRectFromSkRect(clipSkRect);
CGPathRef path = CGPathCreateWithRect(clipRect, nil);
// The `matrix` is based on the physical pixels, convert it to UIKit points.
CATransform3D matrixInPoints =
CATransform3DConcat(GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale);
[self addTransformedPath:path matrix:matrixInPoints];
paths_.push_back([self getTransformedPath:path matrix:matrixInPoints]);
CGAffineTransform affine = [self affineWithMatrix:matrixInPoints];
// Make sure the rect is not rotated (only translation or scaling)
if (affine.b == 0 && affine.c == 0) {
rectSoFar_ = CGRectIntersection(rectSoFar_, CGRectApplyAffineTransform(clipRect, affine));
} else {
containsNonRectPath_ = YES;
}
}

- (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const SkMatrix&)matrix {
containsNonRectPath_ = YES;
CGPathRef pathRef = nullptr;
switch (clipSkRRect.getType()) {
case SkRRect::kEmpty_Type: {
Expand Down Expand Up @@ -321,7 +359,7 @@ - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const SkMatrix&)matrix {
// TODO(cyanglaz): iOS does not seem to support hard edge on CAShapeLayer. It clearly stated that
// the CAShaperLayer will be drawn antialiased. Need to figure out a way to do the hard edge
// clipping on iOS.
[self addTransformedPath:pathRef matrix:matrixInPoints];
paths_.push_back([self getTransformedPath:pathRef matrix:matrixInPoints]);
}

- (void)clipPath:(const SkPath&)path matrix:(const SkMatrix&)matrix {
Expand All @@ -331,6 +369,7 @@ - (void)clipPath:(const SkPath&)path matrix:(const SkMatrix&)matrix {
if (path.isEmpty()) {
return;
}
containsNonRectPath_ = YES;
CGMutablePathRef pathRef = CGPathCreateMutable();

// Loop through all verbs and translate them into CGPath
Expand Down Expand Up @@ -386,15 +425,20 @@ - (void)clipPath:(const SkPath&)path matrix:(const SkMatrix&)matrix {
// The `matrix` is based on the physical pixels, convert it to UIKit points.
CATransform3D matrixInPoints =
CATransform3DConcat(GetCATransform3DFromSkMatrix(matrix), _reverseScreenScale);
[self addTransformedPath:pathRef matrix:matrixInPoints];
paths_.push_back([self getTransformedPath:pathRef matrix:matrixInPoints]);
}

- (CGAffineTransform)affineWithMatrix:(CATransform3D)matrix {
return CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41,
matrix.m42);
}

- (void)addTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix {
CGAffineTransform affine =
CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41, matrix.m42);
CGPathAddPath(pathSoFar_, &affine, path);
[self shapeLayer].path = pathSoFar_;
- (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix {
CGAffineTransform affine = [self affineWithMatrix:matrix];
CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &affine);

CGPathRelease(path);
return fml::CFRef<CGPathRef>(transformedPath);
}

@end
Expand Down