blob: b18aa34cada041cada81587e53096891b25c9db5 [file] [log] [blame] [edit]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/view_slicer.h"
#include <unordered_map>
#include "flow/embedded_views.h"
#include "fml/logging.h"
namespace flutter {
std::unordered_map<int64_t, SkRect> SliceViews(
DlCanvas* background_canvas,
const std::vector<int64_t>& composition_order,
const std::unordered_map<int64_t, std::unique_ptr<EmbedderViewSlice>>&
slices,
const std::unordered_map<int64_t, SkRect>& view_rects) {
std::unordered_map<int64_t, SkRect> overlay_layers;
auto current_frame_view_count = composition_order.size();
// Restore the clip context after exiting this method since it's changed
// below.
DlAutoCanvasRestore save(background_canvas, /*do_save=*/true);
for (size_t i = 0; i < current_frame_view_count; i++) {
int64_t view_id = composition_order[i];
EmbedderViewSlice* slice = slices.at(view_id).get();
if (slice->canvas() == nullptr) {
continue;
}
slice->end_recording();
SkRect full_joined_rect = SkRect::MakeEmpty();
// Determinate if Flutter UI intersects with any of the previous
// platform views stacked by z position.
//
// This is done by querying the r-tree that holds the records for the
// picture recorder corresponding to the flow layers added after a platform
// view layer.
for (int j = i; j >= 0; j--) {
int64_t current_view_id = composition_order[j];
auto maybe_rect = view_rects.find(current_view_id);
FML_DCHECK(maybe_rect != view_rects.end());
if (maybe_rect == view_rects.end()) {
continue;
}
SkRect current_view_rect = maybe_rect->second;
const SkIRect rounded_in_platform_view_rect = current_view_rect.roundIn();
// Each rect corresponds to a native view that renders Flutter UI.
std::vector<SkIRect> intersection_rects =
slice->region(current_view_rect).getRects();
// Ignore intersections of single width/height on the edge of the platform
// view.
// This is to address the following performance issue when interleaving
// adjacent platform views and layers: Since we `roundOut` both platform
// view rects and the layer rects, as long as the coordinate is
// fractional, there will be an intersection of a single pixel width (or
// height) after rounding out, even if they do not intersect before
// rounding out. We have to round out both platform view rect and the
// layer rect. Rounding in platform view rect will result in missing pixel
// on the intersection edge. Rounding in layer rect will result in missing
// pixel on the edge of the layer on top of the platform view.
for (auto it = intersection_rects.begin(); it != intersection_rects.end();
/*no-op*/) {
// If intersection_rect does not intersect with the *rounded in*
// platform view rect, then the intersection must be a single pixel
// width (or height) on edge.
if (!SkIRect::Intersects(*it, rounded_in_platform_view_rect)) {
it = intersection_rects.erase(it);
} else {
++it;
}
}
// Limit the number of native views, so it doesn't grow forever.
//
// In this case, the rects are merged into a single one that is the union
// of all the rects.
SkRect partial_joined_rect = SkRect::MakeEmpty();
for (const SkIRect& rect : intersection_rects) {
partial_joined_rect.join(SkRect::Make(rect));
}
// Get the intersection rect with the `current_view_rect`,
if (partial_joined_rect.intersect(
SkRect::Make(current_view_rect.roundOut()))) {
// Join the `partial_joined_rect` into `full_joined_rect` to get the
// rect above the current `slice`, only if it intersects the indicated
// view. This should always be the case because we just deleted any
// rects that don't intersect the "rounded-in" view, so they must
// all intersect the "rounded-out" view (or the partial join could
// be empty in which case this would be a NOP). Either way, the
// penalty for not checking the return value of the intersect method
// would be to join a non-overlapping rectangle into the overlay
// bounds - if the above implementation ever changes - so we check it.
full_joined_rect.join(partial_joined_rect);
}
}
if (!full_joined_rect.isEmpty()) {
overlay_layers.insert({view_id, full_joined_rect});
// Clip the background canvas, so it doesn't contain any of the pixels
// drawn on the overlay layer.
background_canvas->ClipRect(full_joined_rect,
DlCanvas::ClipOp::kDifference);
}
slice->render_into(background_canvas);
}
// Manually trigger the DlAutoCanvasRestore before we submit the frame
save.Restore();
return overlay_layers;
}
} // namespace flutter