Flutter Engine
FlutterPlatformViews_Internal.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
6 
7 #include "flutter/fml/platform/darwin/cf_utils.h"
8 #import "flutter/shell/platform/darwin/ios/ios_surface.h"
9 
10 static int kMaxPointsInVerb = 4;
11 
12 namespace flutter {
13 
15  fml::scoped_nsobject<UIView> overlay_view,
16  fml::scoped_nsobject<UIView> overlay_view_wrapper,
17  std::unique_ptr<IOSSurface> ios_surface,
18  std::unique_ptr<Surface> surface)
19  : overlay_view(std::move(overlay_view)),
20  overlay_view_wrapper(std::move(overlay_view_wrapper)),
21  ios_surface(std::move(ios_surface)),
22  surface(std::move(surface)){};
23 
25 
27  : layer_pool_(std::make_unique<FlutterPlatformViewLayerPool>()),
28  weak_factory_(std::make_unique<fml::WeakPtrFactory<FlutterPlatformViewsController>>(this)){};
29 
31 
33  return weak_factory_->GetWeakPtr();
34 }
35 
36 CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix) {
37  // Skia only supports 2D transform so we don't map z.
38  CATransform3D transform = CATransform3DIdentity;
39  transform.m11 = matrix.getScaleX();
40  transform.m21 = matrix.getSkewX();
41  transform.m41 = matrix.getTranslateX();
42  transform.m14 = matrix.getPerspX();
43 
44  transform.m12 = matrix.getSkewY();
45  transform.m22 = matrix.getScaleY();
46  transform.m42 = matrix.getTranslateY();
47  transform.m24 = matrix.getPerspY();
48  return transform;
49 }
50 
51 void ResetAnchor(CALayer* layer) {
52  // Flow uses (0, 0) to apply transform matrix so we need to match that in Quartz.
53  layer.anchorPoint = CGPointZero;
54  layer.position = CGPointZero;
55 }
56 
57 } // namespace flutter
58 
59 @implementation ChildClippingView
60 
61 // The ChildClippingView's frame is the bounding rect of the platform view. we only want touches to
62 // be hit tested and consumed by this view if they are inside the embedded platform view which could
63 // be smaller the embedded platform view is rotated.
64 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
65  for (UIView* view in self.subviews) {
66  if ([view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
67  return YES;
68  }
69  }
70  return NO;
71 }
72 
73 @end
74 
76 
77 - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix;
78 - (CGRect)getCGRectFromSkRect:(const SkRect&)clipSkRect;
79 
80 @end
81 
82 @implementation FlutterClippingMaskView {
83  std::vector<fml::CFRef<CGPathRef>> paths_;
84 }
85 
86 - (instancetype)initWithFrame:(CGRect)frame {
87  if ([super initWithFrame:frame]) {
88  self.backgroundColor = UIColor.clearColor;
89  }
90  return self;
91 }
92 
93 // In some scenarios, when we add this view as a maskView of the ChildClippingView, iOS added
94 // this view as a subview of the ChildClippingView.
95 // This results this view blocking touch events on the ChildClippingView.
96 // So we should always ignore any touch events sent to this view.
97 // See https://github.com/flutter/flutter/issues/66044
98 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
99  return NO;
100 }
101 
102 - (void)drawRect:(CGRect)rect {
103  CGContextRef context = UIGraphicsGetCurrentContext();
104  CGContextSaveGState(context);
105 
106  // For mask view, only the alpha channel is used.
107  CGContextSetAlpha(context, 1);
108 
109  for (size_t i = 0; i < paths_.size(); i++) {
110  CGContextAddPath(context, paths_.at(i));
111  CGContextClip(context);
112  }
113  CGContextFillRect(context, rect);
114  CGContextRestoreGState(context);
115 }
116 
117 - (void)clipRect:(const SkRect&)clipSkRect matrix:(const CATransform3D&)matrix {
118  CGRect clipRect = [self getCGRectFromSkRect:clipSkRect];
119  CGPathRef path = CGPathCreateWithRect(clipRect, nil);
120  paths_.push_back([self getTransformedPath:path matrix:matrix]);
121 }
122 
123 - (void)clipRRect:(const SkRRect&)clipSkRRect matrix:(const CATransform3D&)matrix {
124  CGPathRef pathRef = nullptr;
125  switch (clipSkRRect.getType()) {
126  case SkRRect::kEmpty_Type: {
127  break;
128  }
129  case SkRRect::kRect_Type: {
130  [self clipRect:clipSkRRect.rect() matrix:matrix];
131  return;
132  }
133  case SkRRect::kOval_Type:
134  case SkRRect::kSimple_Type: {
135  CGRect clipRect = [self getCGRectFromSkRect:clipSkRRect.rect()];
136  pathRef = CGPathCreateWithRoundedRect(clipRect, clipSkRRect.getSimpleRadii().x(),
137  clipSkRRect.getSimpleRadii().y(), nil);
138  break;
139  }
140  case SkRRect::kNinePatch_Type:
141  case SkRRect::kComplex_Type: {
142  CGMutablePathRef mutablePathRef = CGPathCreateMutable();
143  // Complex types, we manually add each corner.
144  SkRect clipSkRect = clipSkRRect.rect();
145  SkVector topLeftRadii = clipSkRRect.radii(SkRRect::kUpperLeft_Corner);
146  SkVector topRightRadii = clipSkRRect.radii(SkRRect::kUpperRight_Corner);
147  SkVector bottomRightRadii = clipSkRRect.radii(SkRRect::kLowerRight_Corner);
148  SkVector bottomLeftRadii = clipSkRRect.radii(SkRRect::kLowerLeft_Corner);
149 
150  // Start drawing RRect
151  // Move point to the top left corner adding the top left radii's x.
152  CGPathMoveToPoint(mutablePathRef, nil, clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop);
153  // Move point horizontally right to the top right corner and add the top right curve.
154  CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fRight - topRightRadii.x(),
155  clipSkRect.fTop);
156  CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fRight, clipSkRect.fTop,
157  clipSkRect.fRight, clipSkRect.fTop + topRightRadii.y(),
158  clipSkRect.fRight, clipSkRect.fTop + topRightRadii.y());
159  // Move point vertically down to the bottom right corner and add the bottom right curve.
160  CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fRight,
161  clipSkRect.fBottom - bottomRightRadii.y());
162  CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fRight, clipSkRect.fBottom,
163  clipSkRect.fRight - bottomRightRadii.x(), clipSkRect.fBottom,
164  clipSkRect.fRight - bottomRightRadii.x(), clipSkRect.fBottom);
165  // Move point horizontally left to the bottom left corner and add the bottom left curve.
166  CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fLeft + bottomLeftRadii.x(),
167  clipSkRect.fBottom);
168  CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fLeft, clipSkRect.fBottom,
169  clipSkRect.fLeft, clipSkRect.fBottom - bottomLeftRadii.y(),
170  clipSkRect.fLeft, clipSkRect.fBottom - bottomLeftRadii.y());
171  // Move point vertically up to the top left corner and add the top left curve.
172  CGPathAddLineToPoint(mutablePathRef, nil, clipSkRect.fLeft,
173  clipSkRect.fTop + topLeftRadii.y());
174  CGPathAddCurveToPoint(mutablePathRef, nil, clipSkRect.fLeft, clipSkRect.fTop,
175  clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop,
176  clipSkRect.fLeft + topLeftRadii.x(), clipSkRect.fTop);
177  CGPathCloseSubpath(mutablePathRef);
178 
179  pathRef = mutablePathRef;
180  break;
181  }
182  }
183  // TODO(cyanglaz): iOS does not seem to support hard edge on CAShapeLayer. It clearly stated that
184  // the CAShaperLayer will be drawn antialiased. Need to figure out a way to do the hard edge
185  // clipping on iOS.
186  paths_.push_back([self getTransformedPath:pathRef matrix:matrix]);
187 }
188 
189 - (void)clipPath:(const SkPath&)path matrix:(const CATransform3D&)matrix {
190  if (!path.isValid()) {
191  return;
192  }
193  if (path.isEmpty()) {
194  return;
195  }
196  CGMutablePathRef pathRef = CGPathCreateMutable();
197 
198  // Loop through all verbs and translate them into CGPath
199  SkPath::Iter iter(path, true);
200  SkPoint pts[kMaxPointsInVerb];
201  SkPath::Verb verb = iter.next(pts);
202  SkPoint last_pt_from_last_verb;
203  while (verb != SkPath::kDone_Verb) {
204  if (verb == SkPath::kLine_Verb || verb == SkPath::kQuad_Verb || verb == SkPath::kConic_Verb ||
205  verb == SkPath::kCubic_Verb) {
206  FML_DCHECK(last_pt_from_last_verb == pts[0]);
207  }
208  switch (verb) {
209  case SkPath::kMove_Verb: {
210  CGPathMoveToPoint(pathRef, nil, pts[0].x(), pts[0].y());
211  last_pt_from_last_verb = pts[0];
212  break;
213  }
214  case SkPath::kLine_Verb: {
215  CGPathAddLineToPoint(pathRef, nil, pts[1].x(), pts[1].y());
216  last_pt_from_last_verb = pts[1];
217  break;
218  }
219  case SkPath::kQuad_Verb: {
220  CGPathAddQuadCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
221  last_pt_from_last_verb = pts[2];
222  break;
223  }
224  case SkPath::kConic_Verb: {
225  // Conic is not available in quartz, we use quad to approximate.
226  // TODO(cyanglaz): Better approximate the conic path.
227  // https://github.com/flutter/flutter/issues/35062
228  CGPathAddQuadCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y());
229  last_pt_from_last_verb = pts[2];
230  break;
231  }
232  case SkPath::kCubic_Verb: {
233  CGPathAddCurveToPoint(pathRef, nil, pts[1].x(), pts[1].y(), pts[2].x(), pts[2].y(),
234  pts[3].x(), pts[3].y());
235  last_pt_from_last_verb = pts[3];
236  break;
237  }
238  case SkPath::kClose_Verb: {
239  CGPathCloseSubpath(pathRef);
240  break;
241  }
242  case SkPath::kDone_Verb: {
243  break;
244  }
245  }
246  verb = iter.next(pts);
247  }
248  paths_.push_back([self getTransformedPath:pathRef matrix:matrix]);
249 }
250 
251 - (fml::CFRef<CGPathRef>)getTransformedPath:(CGPathRef)path matrix:(CATransform3D)matrix {
252  CGAffineTransform affine =
253  CGAffineTransformMake(matrix.m11, matrix.m12, matrix.m21, matrix.m22, matrix.m41, matrix.m42);
254  CGPathRef transformedPath = CGPathCreateCopyByTransformingPath(path, &affine);
255  CGPathRelease(path);
256  return fml::CFRef<CGPathRef>(transformedPath);
257 }
258 
259 - (CGRect)getCGRectFromSkRect:(const SkRect&)clipSkRect {
260  return CGRectMake(clipSkRect.fLeft, clipSkRect.fTop, clipSkRect.fRight - clipSkRect.fLeft,
261  clipSkRect.fBottom - clipSkRect.fTop);
262 }
263 
264 @end
DEF_SWITCHES_START snapshot asset path
Definition: switches.h:32
#define FML_DCHECK(condition)
Definition: logging.h:86
static int kMaxPointsInVerb
void ResetAnchor(CALayer *layer)
Definition: ref_ptr.h:252
void clipRect:matrix:(const SkRect &clipSkRect, [matrix] const CATransform3D &matrix)
Definition: ascii_trie.cc:9
fml::WeakPtr< flutter::FlutterPlatformViewsController > GetWeakPtr()
FlutterPlatformViewLayer(fml::scoped_nsobject< UIView > overlay_view, fml::scoped_nsobject< UIView > overlay_view_wrapper, std::unique_ptr< IOSSurface > ios_surface, std::unique_ptr< Surface > surface)
CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix &matrix)
GdkEventButton * event
Definition: fl_view.cc:62