Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 7e1330c

Browse files
committed
[macOS] Clean up PlatformView mutator scaling
When applying platform view mutators, the mutations array includes a transform from logical to physical coordinates. Since Cocoa deals only in logical points, we inject a physical to logical coordinate transform to counteract this. Rather than applying this multiple times throughout the `[FlutterMutatorView applyFlutterLayer]` method we gather a list of transforms that includes the additional physical to logical scale transform. This change is a cleanup change for readability and makes no semantic changes to how mutator views are applied. Existing unit tests in FlutterMutatorViewTest.mm are expected to pass without changes.
1 parent d970370 commit 7e1330c

File tree

1 file changed

+52
-24
lines changed

1 file changed

+52
-24
lines changed

shell/platform/darwin/macos/framework/Source/FlutterMutatorView.mm

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
// found in the LICENSE file.
44

55
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMutatorView.h"
6-
#include "flutter/fml/logging.h"
76

87
#include <QuartzCore/QuartzCore.h>
8+
99
#include <vector>
1010

11+
#include "flutter/fml/logging.h"
12+
#include "flutter/shell/platform/embedder/embedder.h"
13+
1114
@interface FlutterMutatorView () {
1215
/// Each of these views clips to a CGPathRef. These views, if present,
1316
/// are nested (first is child of FlutterMutatorView and last is parent of
@@ -85,6 +88,28 @@ FlutterRect ToFlutterRect(const CGRect& rect) {
8588
};
8689
}
8790

91+
using MutationVector = std::vector<FlutterPlatformViewMutation>;
92+
93+
/// Returns a vector of FlutterPlatformViewMutation object pointers associated with a platform view.
94+
/// The transforms sent from the engine include a transform from logical to physical coordinates.
95+
/// Since Cocoa deals only in logical points, this function prepends a scale transform that scales
96+
/// back from physical to logical coordinates to compensate.
97+
MutationVector MutationsForPlatformView(const FlutterPlatformView* view, float scale) {
98+
MutationVector mutations;
99+
mutations.reserve(view->mutations_count + 1);
100+
mutations.push_back({
101+
.type = kFlutterPlatformViewMutationTypeTransformation,
102+
.transformation{
103+
.scaleX = 1.0 / scale,
104+
.scaleY = 1.0 / scale,
105+
},
106+
});
107+
for (size_t i = 0; i < view->mutations_count; ++i) {
108+
mutations.push_back(*view->mutations[i]);
109+
}
110+
return mutations;
111+
}
112+
88113
/// Returns whether the point is inside ellipse with given radius (centered at 0, 0).
89114
bool PointInsideEllipse(const CGPoint& point, const FlutterSize& radius) {
90115
return (point.x * point.x) / (radius.width * radius.width) +
@@ -219,27 +244,29 @@ - (BOOL)isFlipped {
219244
return YES;
220245
}
221246

247+
/// Returns the scale factor to translate logical pixels to physical pixels for this view.
248+
- (CGFloat)contentsScale {
249+
return self.superview != nil ? self.superview.layer.contentsScale : 1.0;
250+
}
251+
222252
/// Whenever possible view will be clipped using layer bounds.
223253
/// If clipping to path is needed, CAShapeLayer(s) will be used as mask.
224254
/// Clipping to round rect only clips to path if round corners are intersected.
225255
- (void)applyFlutterLayer:(const FlutterLayer*)layer {
226-
CGFloat scale = self.superview != nil ? self.superview.layer.contentsScale : 1.0;
227-
228-
// Initial transform to compensate for scale factor. This is needed because all
229-
// cocoa coordinates are logical but Flutter will send the physical to logical
230-
// transform in mutations.
231-
CATransform3D transform = CATransform3DMakeScale(1.0 / scale, 1.0 / scale, 1);
256+
CGFloat scale = [self contentsScale];
257+
auto mutations = MutationsForPlatformView(layer->platform_view, scale);
232258

233259
// Platform view transform after applying all transformation mutations.
234-
CATransform3D finalTransform = transform;
235-
for (size_t i = 0; i < layer->platform_view->mutations_count; ++i) {
236-
auto mutation = layer->platform_view->mutations[i];
237-
if (mutation->type == kFlutterPlatformViewMutationTypeTransformation) {
238-
finalTransform =
239-
CATransform3DConcat(ToCATransform3D(mutation->transformation), finalTransform);
260+
CATransform3D finalTransform = CATransform3DIdentity;
261+
for (auto mutation : mutations) {
262+
if (mutation.type == kFlutterPlatformViewMutationTypeTransformation) {
263+
CATransform3D mutationTransform = ToCATransform3D(mutation.transformation);
264+
finalTransform = CATransform3DConcat(mutationTransform, finalTransform);
240265
}
241266
}
242267

268+
// Compute the untransformed bounding rect for the platform view in logical pixels.
269+
// FlutterLayer.size is in physical pixels but Cocoa uses logical points.
243270
CGRect untransformedBoundingRect =
244271
CGRectMake(0, 0, layer->size.width / scale, layer->size.height / scale);
245272

@@ -257,22 +284,23 @@ - (void)applyFlutterLayer:(const FlutterLayer*)layer {
257284
// Gathered pairs of rounded rect in local coordinates + appropriate transform.
258285
std::vector<std::pair<FlutterRoundedRect, CGAffineTransform>> roundedRects;
259286

260-
for (size_t i = 0; i < layer->platform_view->mutations_count; ++i) {
261-
auto mutation = layer->platform_view->mutations[i];
262-
if (mutation->type == kFlutterPlatformViewMutationTypeTransformation) {
263-
transform = CATransform3DConcat(ToCATransform3D(mutation->transformation), transform);
264-
} else if (mutation->type == kFlutterPlatformViewMutationTypeClipRect) {
265-
CGRect rect = CGRectApplyAffineTransform(FromFlutterRect(mutation->clip_rect),
287+
// Create the initial transform.
288+
CATransform3D transform = CATransform3DIdentity;
289+
for (auto mutation : mutations) {
290+
if (mutation.type == kFlutterPlatformViewMutationTypeTransformation) {
291+
transform = CATransform3DConcat(ToCATransform3D(mutation.transformation), transform);
292+
} else if (mutation.type == kFlutterPlatformViewMutationTypeClipRect) {
293+
CGRect rect = CGRectApplyAffineTransform(FromFlutterRect(mutation.clip_rect),
266294
CATransform3DGetAffineTransform(transform));
267295
masterClip = CGRectIntersection(rect, masterClip);
268-
} else if (mutation->type == kFlutterPlatformViewMutationTypeClipRoundedRect) {
296+
} else if (mutation.type == kFlutterPlatformViewMutationTypeClipRoundedRect) {
269297
CGAffineTransform affineTransform = CATransform3DGetAffineTransform(transform);
270-
roundedRects.push_back(std::make_pair(mutation->clip_rounded_rect, affineTransform));
271-
CGRect rect = CGRectApplyAffineTransform(FromFlutterRect(mutation->clip_rounded_rect.rect),
298+
roundedRects.push_back(std::make_pair(mutation.clip_rounded_rect, affineTransform));
299+
CGRect rect = CGRectApplyAffineTransform(FromFlutterRect(mutation.clip_rounded_rect.rect),
272300
affineTransform);
273301
masterClip = CGRectIntersection(rect, masterClip);
274-
} else if (mutation->type == kFlutterPlatformViewMutationTypeOpacity) {
275-
self.layer.opacity *= mutation->opacity;
302+
} else if (mutation.type == kFlutterPlatformViewMutationTypeOpacity) {
303+
self.layer.opacity *= mutation.opacity;
276304
}
277305
}
278306

0 commit comments

Comments
 (0)