|
26 | 26 | std::shared_ptr<IOSContext> ios_context) { |
27 | 27 | if (available_layer_index_ >= layers_.size()) { |
28 | 28 | std::shared_ptr<FlutterPlatformViewLayer> layer; |
| 29 | + fml::scoped_nsobject<FlutterOverlayView> overlay_view; |
| 30 | + fml::scoped_nsobject<FlutterOverlayView> overlay_view_wrapper; |
29 | 31 |
|
30 | 32 | if (!gr_context) { |
31 | | - fml::scoped_nsobject<FlutterOverlayView> overlay_view([[FlutterOverlayView alloc] init]); |
32 | | - overlay_view.get().autoresizingMask = |
33 | | - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); |
| 33 | + overlay_view.reset([[FlutterOverlayView alloc] init]); |
| 34 | + overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); |
| 35 | + |
34 | 36 | std::unique_ptr<IOSSurface> ios_surface = |
35 | 37 | [overlay_view.get() createSurface:std::move(ios_context)]; |
36 | 38 | std::unique_ptr<Surface> surface = ios_surface->CreateGPUSurface(); |
37 | 39 |
|
38 | 40 | layer = std::make_shared<FlutterPlatformViewLayer>( |
39 | | - std::move(overlay_view), std::move(ios_surface), std::move(surface)); |
| 41 | + std::move(overlay_view), std::move(overlay_view_wrapper), std::move(ios_surface), |
| 42 | + std::move(surface)); |
40 | 43 | } else { |
41 | 44 | CGFloat screenScale = [UIScreen mainScreen].scale; |
42 | | - fml::scoped_nsobject<FlutterOverlayView> overlay_view( |
43 | | - [[FlutterOverlayView alloc] initWithContentsScale:screenScale]); |
44 | | - overlay_view.get().autoresizingMask = |
45 | | - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); |
| 45 | + overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale]); |
| 46 | + overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale]); |
| 47 | + |
46 | 48 | std::unique_ptr<IOSSurface> ios_surface = |
47 | 49 | [overlay_view.get() createSurface:std::move(ios_context)]; |
48 | 50 | std::unique_ptr<Surface> surface = ios_surface->CreateGPUSurface(gr_context); |
49 | 51 |
|
50 | 52 | layer = std::make_shared<FlutterPlatformViewLayer>( |
51 | | - std::move(overlay_view), std::move(ios_surface), std::move(surface)); |
| 53 | + std::move(overlay_view), std::move(overlay_view_wrapper), std::move(ios_surface), |
| 54 | + std::move(surface)); |
52 | 55 | layer->gr_context = gr_context; |
53 | 56 | } |
| 57 | + // The overlay view wrapper masks the overlay view. |
| 58 | + // This is required to keep the backing surface size unchanged between frames. |
| 59 | + // |
| 60 | + // Otherwise, changing the size of the overlay would require a new surface, |
| 61 | + // which can be very expensive. |
| 62 | + // |
| 63 | + // This is the case of an animation in which the overlay size is changing in every frame. |
| 64 | + // |
| 65 | + // +------------------------+ |
| 66 | + // | overlay_view | |
| 67 | + // | +--------------+ | +--------------+ |
| 68 | + // | | wrapper | | == mask => | overlay_view | |
| 69 | + // | +--------------+ | +--------------+ |
| 70 | + // +------------------------+ |
| 71 | + overlay_view_wrapper.get().clipsToBounds = YES; |
| 72 | + [overlay_view_wrapper.get() addSubview:overlay_view]; |
54 | 73 | layers_.push_back(layer); |
55 | 74 | } |
56 | 75 | auto layer = layers_[available_layer_index_]; |
|
424 | 443 | [sub_view removeFromSuperview]; |
425 | 444 | } |
426 | 445 | views_.clear(); |
427 | | - overlays_.clear(); |
428 | 446 | composition_order_.clear(); |
429 | 447 | active_composition_order_.clear(); |
430 | 448 | picture_recorders_.clear(); |
|
500 | 518 | // Get the intersection rect between the current rect |
501 | 519 | // and the platform view rect. |
502 | 520 | joined_rect.intersect(platform_view_rect); |
| 521 | + // Subpixels in the platform may not align with the canvas subpixels. |
| 522 | + // To workaround it, round the rect values. |
| 523 | + joined_rect.setLTRB(std::round(joined_rect.left()), std::round(joined_rect.top()), |
| 524 | + std::round(joined_rect.right()), std::round(joined_rect.bottom())); |
503 | 525 | // Clip the background canvas, so it doesn't contain any of the pixels drawn |
504 | 526 | // on the overlay layer. |
505 | 527 | background_canvas->clipRect(joined_rect, SkClipOp::kDifference); |
|
545 | 567 | platform_view_root.layer.zPosition = zIndex++; |
546 | 568 | } |
547 | 569 | for (const std::shared_ptr<FlutterPlatformViewLayer>& layer : layers) { |
548 | | - if ([layer->overlay_view superview] != flutter_view) { |
549 | | - [flutter_view addSubview:layer->overlay_view]; |
| 570 | + if ([layer->overlay_view_wrapper superview] != flutter_view) { |
| 571 | + [flutter_view addSubview:layer->overlay_view_wrapper]; |
550 | 572 | } else { |
551 | | - layer->overlay_view.get().layer.zPosition = zIndex++; |
| 573 | + layer->overlay_view_wrapper.get().layer.zPosition = zIndex++; |
552 | 574 | } |
553 | 575 | } |
554 | 576 | active_composition_order_.push_back(platform_view_id); |
|
563 | 585 | int64_t view_id, |
564 | 586 | int64_t overlay_id) { |
565 | 587 | auto layer = layer_pool_->GetLayer(gr_context, ios_context); |
| 588 | + |
| 589 | + auto overlay_view_wrapper = layer->overlay_view_wrapper.get(); |
566 | 590 | auto screenScale = [UIScreen mainScreen].scale; |
567 | | - // Set the size of the overlay UIView. |
568 | | - layer->overlay_view.get().frame = CGRectMake(rect.x() / screenScale, // |
569 | | - rect.y() / screenScale, // |
570 | | - rect.width() / screenScale, // |
571 | | - rect.height() / screenScale // |
572 | | - ); |
573 | | - // Set a unique view identifier, so the overlay can be identified in unit tests. |
574 | | - layer->overlay_view.get().accessibilityIdentifier = |
| 591 | + // Set the size of the overlay view wrapper. |
| 592 | + // This wrapper view masks the overlay view. |
| 593 | + overlay_view_wrapper.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale, |
| 594 | + rect.width() / screenScale, rect.height() / screenScale); |
| 595 | + // Set a unique view identifier, so the overlay wrapper can be identified in unit tests. |
| 596 | + overlay_view_wrapper.accessibilityIdentifier = |
575 | 597 | [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; |
576 | 598 |
|
577 | | - std::unique_ptr<SurfaceFrame> frame = |
578 | | - layer->surface->AcquireFrame(SkISize::Make(rect.width(), rect.height())); |
| 599 | + auto overlay_view = layer->overlay_view.get(); |
| 600 | + // Set the size of the overlay view. |
| 601 | + // This size is equal to the the device screen size. |
| 602 | + overlay_view.frame = flutter_view_.get().bounds; |
| 603 | + |
| 604 | + std::unique_ptr<SurfaceFrame> frame = layer->surface->AcquireFrame(frame_size_); |
579 | 605 | // If frame is null, AcquireFrame already printed out an error message. |
580 | 606 | if (!frame) { |
581 | 607 | return layer; |
|
594 | 620 | void FlutterPlatformViewsController::RemoveUnusedLayers() { |
595 | 621 | auto layers = layer_pool_->GetUnusedLayers(); |
596 | 622 | for (const std::shared_ptr<FlutterPlatformViewLayer>& layer : layers) { |
597 | | - [layer->overlay_view removeFromSuperview]; |
| 623 | + [layer->overlay_view_wrapper removeFromSuperview]; |
598 | 624 | } |
599 | 625 |
|
600 | 626 | std::unordered_set<int64_t> composition_order_set; |
601 | | - |
602 | 627 | for (int64_t view_id : composition_order_) { |
603 | 628 | composition_order_set.insert(view_id); |
604 | 629 | } |
|
622 | 647 | views_.erase(viewId); |
623 | 648 | touch_interceptors_.erase(viewId); |
624 | 649 | root_views_.erase(viewId); |
625 | | - if (overlays_.find(viewId) != overlays_.end()) { |
626 | | - [overlays_[viewId]->overlay_view.get() removeFromSuperview]; |
627 | | - } |
628 | | - overlays_.erase(viewId); |
629 | 650 | current_composition_params_.erase(viewId); |
630 | 651 | clip_count_.erase(viewId); |
631 | 652 | views_to_recomposite_.erase(viewId); |
|
0 commit comments