| 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 | #include "flutter/flow/layers/transform_layer.h" |
| 6 | |
| 7 | #include <optional> |
| 8 | |
| 9 | namespace flutter { |
| 10 | |
| 11 | TransformLayer::TransformLayer(const SkM44& transform) : transform_(transform) { |
| 12 | // Checks (in some degree) that SkM44 transform_ is valid and initialized. |
| 13 | // |
| 14 | // If transform_ is uninitialized, this assert may look flaky as it doesn't |
| 15 | // fail all the time, and some rerun may make it pass. But don't ignore it and |
| 16 | // just rerun the test if this is triggered, since even a flaky failure here |
| 17 | // may signify a potentially big problem in the code. |
| 18 | // |
| 19 | // We have to write this flaky test because there is no reliable way to test |
| 20 | // whether a variable is initialized or not in C++. |
| 21 | FML_DCHECK(transform_.isFinite()); |
| 22 | if (!transform_.isFinite()) { |
| 23 | FML_LOG(ERROR) << "TransformLayer is constructed with an invalid matrix." ; |
| 24 | transform_.setIdentity(); |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | void TransformLayer::Diff(DiffContext* context, const Layer* old_layer) { |
| 29 | DiffContext::AutoSubtreeRestore subtree(context); |
| 30 | auto* prev = static_cast<const TransformLayer*>(old_layer); |
| 31 | if (!context->IsSubtreeDirty()) { |
| 32 | FML_DCHECK(prev); |
| 33 | if (transform_ != prev->transform_) { |
| 34 | context->MarkSubtreeDirty(previous_paint_region: context->GetOldLayerPaintRegion(layer: old_layer)); |
| 35 | } |
| 36 | } |
| 37 | context->PushTransform(transform: transform_); |
| 38 | DiffChildren(context, old_layer: prev); |
| 39 | context->SetLayerPaintRegion(layer: this, region: context->CurrentSubtreeRegion()); |
| 40 | } |
| 41 | |
| 42 | void TransformLayer::Preroll(PrerollContext* context) { |
| 43 | auto mutator = context->state_stack.save(); |
| 44 | mutator.transform(m44: transform_); |
| 45 | |
| 46 | SkRect child_paint_bounds = SkRect::MakeEmpty(); |
| 47 | PrerollChildren(context, child_paint_bounds: &child_paint_bounds); |
| 48 | |
| 49 | // We convert to a 3x3 matrix here primarily because the SkM44 object |
| 50 | // does not support a mapRect operation. |
| 51 | // https://bugs.chromium.org/p/skia/issues/detail?id=11720&q=mapRect&can=2 |
| 52 | // |
| 53 | // All geometry is X,Y only which means the 3rd row of the 4x4 matrix |
| 54 | // is ignored and the output of the 3rd column is also ignored. |
| 55 | // So we can transform the rectangle using just the 3x3 SkMatrix |
| 56 | // equivalent without any loss of information. |
| 57 | // |
| 58 | // Performance consideration: |
| 59 | // Skia has an internal mapRect for their SkM44 object that is faster |
| 60 | // than what SkMatrix does when it has perspective elements. But SkMatrix |
| 61 | // is otherwise optimal for non-perspective matrices. If SkM44 ever exposes |
| 62 | // a mapRect operation, or if SkMatrix ever optimizes its handling of |
| 63 | // the perspective elements, this issue will become moot. |
| 64 | transform_.asM33().mapRect(rect: &child_paint_bounds); |
| 65 | set_paint_bounds(child_paint_bounds); |
| 66 | } |
| 67 | |
| 68 | void TransformLayer::Paint(PaintContext& context) const { |
| 69 | FML_DCHECK(needs_painting(context)); |
| 70 | |
| 71 | auto mutator = context.state_stack.save(); |
| 72 | mutator.transform(m44: transform_); |
| 73 | |
| 74 | PaintChildren(context); |
| 75 | } |
| 76 | |
| 77 | } // namespace flutter |
| 78 | |