1e4db79de127cfe961195f52907af8451026eaa20Chris Craik/*
2e4db79de127cfe961195f52907af8451026eaa20Chris Craik * Copyright (C) 2015 The Android Open Source Project
3e4db79de127cfe961195f52907af8451026eaa20Chris Craik *
4e4db79de127cfe961195f52907af8451026eaa20Chris Craik * Licensed under the Apache License, Version 2.0 (the "License");
5e4db79de127cfe961195f52907af8451026eaa20Chris Craik * you may not use this file except in compliance with the License.
6e4db79de127cfe961195f52907af8451026eaa20Chris Craik * You may obtain a copy of the License at
7e4db79de127cfe961195f52907af8451026eaa20Chris Craik *
8e4db79de127cfe961195f52907af8451026eaa20Chris Craik *      http://www.apache.org/licenses/LICENSE-2.0
9e4db79de127cfe961195f52907af8451026eaa20Chris Craik *
10e4db79de127cfe961195f52907af8451026eaa20Chris Craik * Unless required by applicable law or agreed to in writing, software
11e4db79de127cfe961195f52907af8451026eaa20Chris Craik * distributed under the License is distributed on an "AS IS" BASIS,
12e4db79de127cfe961195f52907af8451026eaa20Chris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e4db79de127cfe961195f52907af8451026eaa20Chris Craik * See the License for the specific language governing permissions and
14e4db79de127cfe961195f52907af8451026eaa20Chris Craik * limitations under the License.
15e4db79de127cfe961195f52907af8451026eaa20Chris Craik */
16e4db79de127cfe961195f52907af8451026eaa20Chris Craik
17e4db79de127cfe961195f52907af8451026eaa20Chris Craik#include "BakedOpState.h"
18e4db79de127cfe961195f52907af8451026eaa20Chris Craik
19e4db79de127cfe961195f52907af8451026eaa20Chris Craik#include "ClipArea.h"
20e4db79de127cfe961195f52907af8451026eaa20Chris Craik
21e4db79de127cfe961195f52907af8451026eaa20Chris Craiknamespace android {
22e4db79de127cfe961195f52907af8451026eaa20Chris Craiknamespace uirenderer {
23e4db79de127cfe961195f52907af8451026eaa20Chris Craik
244876de16e34622634266d09522c9153c78c7c2fbChris Craikstatic int computeClipSideFlags(const Rect& clip, const Rect& bounds) {
254876de16e34622634266d09522c9153c78c7c2fbChris Craik    int clipSideFlags = 0;
264876de16e34622634266d09522c9153c78c7c2fbChris Craik    if (clip.left > bounds.left) clipSideFlags |= OpClipSideFlags::Left;
274876de16e34622634266d09522c9153c78c7c2fbChris Craik    if (clip.top > bounds.top) clipSideFlags |= OpClipSideFlags::Top;
284876de16e34622634266d09522c9153c78c7c2fbChris Craik    if (clip.right < bounds.right) clipSideFlags |= OpClipSideFlags::Right;
294876de16e34622634266d09522c9153c78c7c2fbChris Craik    if (clip.bottom < bounds.bottom) clipSideFlags |= OpClipSideFlags::Bottom;
304876de16e34622634266d09522c9153c78c7c2fbChris Craik    return clipSideFlags;
314876de16e34622634266d09522c9153c78c7c2fbChris Craik}
324876de16e34622634266d09522c9153c78c7c2fbChris Craik
33e4db79de127cfe961195f52907af8451026eaa20Chris CraikResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
34e4db79de127cfe961195f52907af8451026eaa20Chris Craik        const RecordedOp& recordedOp, bool expandForStroke) {
35e4db79de127cfe961195f52907af8451026eaa20Chris Craik    // resolvedMatrix = parentMatrix * localMatrix
36e4db79de127cfe961195f52907af8451026eaa20Chris Craik    transform.loadMultiply(*snapshot.transform, recordedOp.localMatrix);
37e4db79de127cfe961195f52907af8451026eaa20Chris Craik
38e4db79de127cfe961195f52907af8451026eaa20Chris Craik    // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
39e4db79de127cfe961195f52907af8451026eaa20Chris Craik    clippedBounds = recordedOp.unmappedBounds;
40e4db79de127cfe961195f52907af8451026eaa20Chris Craik    if (CC_UNLIKELY(expandForStroke)) {
41e4db79de127cfe961195f52907af8451026eaa20Chris Craik        // account for non-hairline stroke
42e4db79de127cfe961195f52907af8451026eaa20Chris Craik        clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
43e4db79de127cfe961195f52907af8451026eaa20Chris Craik    }
44e4db79de127cfe961195f52907af8451026eaa20Chris Craik    transform.mapRect(clippedBounds);
45e4db79de127cfe961195f52907af8451026eaa20Chris Craik    if (CC_UNLIKELY(expandForStroke
46e4db79de127cfe961195f52907af8451026eaa20Chris Craik            && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) {
47e4db79de127cfe961195f52907af8451026eaa20Chris Craik        // account for hairline stroke when stroke may be < 1 scaled pixel
48e4db79de127cfe961195f52907af8451026eaa20Chris Craik        // Non translate || strokeWidth < 1 is conservative, but will cover all cases
49e4db79de127cfe961195f52907af8451026eaa20Chris Craik        clippedBounds.outset(0.5f);
50e4db79de127cfe961195f52907af8451026eaa20Chris Craik    }
51e4db79de127cfe961195f52907af8451026eaa20Chris Craik
52e4db79de127cfe961195f52907af8451026eaa20Chris Craik    // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
5304d46eb69fb4f4c4c332c36c6ae845da3b2ae848Chris Craik    clipState = snapshot.serializeIntersectedClip(allocator,
54e4db79de127cfe961195f52907af8451026eaa20Chris Craik            recordedOp.localClip, *(snapshot.transform));
55e4db79de127cfe961195f52907af8451026eaa20Chris Craik    LOG_ALWAYS_FATAL_IF(!clipState, "must clip!");
56e4db79de127cfe961195f52907af8451026eaa20Chris Craik
57e4db79de127cfe961195f52907af8451026eaa20Chris Craik    const Rect& clipRect = clipState->rect;
58e4db79de127cfe961195f52907af8451026eaa20Chris Craik    if (CC_UNLIKELY(clipRect.isEmpty() || !clippedBounds.intersects(clipRect))) {
59e4db79de127cfe961195f52907af8451026eaa20Chris Craik        // Rejected based on either empty clip, or bounds not intersecting with clip
6015f046866cb650d78f55d03327cfa4a474fc9471Chris Craik
6115f046866cb650d78f55d03327cfa4a474fc9471Chris Craik        // Note: we could rewind the clipState object in situations where the clipRect is empty,
6215f046866cb650d78f55d03327cfa4a474fc9471Chris Craik        // but *only* if the caching logic within ClipArea was aware of the rewind.
6315f046866cb650d78f55d03327cfa4a474fc9471Chris Craik        clipState = nullptr;
64e4db79de127cfe961195f52907af8451026eaa20Chris Craik        clippedBounds.setEmpty();
65e4db79de127cfe961195f52907af8451026eaa20Chris Craik    } else {
66678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik        // Not rejected! compute true clippedBounds, clipSideFlags, and path mask
674876de16e34622634266d09522c9153c78c7c2fbChris Craik        clipSideFlags = computeClipSideFlags(clipRect, clippedBounds);
68e4db79de127cfe961195f52907af8451026eaa20Chris Craik        clippedBounds.doIntersect(clipRect);
69678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik
70678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik        if (CC_UNLIKELY(snapshot.projectionPathMask)) {
71678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            // map projection path mask from render target space into op space,
72678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            // so intersection with op geometry is possible
73678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            Matrix4 inverseTransform;
74678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            inverseTransform.loadInverse(transform);
75678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            SkMatrix skInverseTransform;
76678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            inverseTransform.copyTo(skInverseTransform);
77678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik
78678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            auto localMask = allocator.create<SkPath>();
79678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            snapshot.projectionPathMask->transform(skInverseTransform, localMask);
80678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik            localProjectionPathMask = localMask;
81678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik        }
82e4db79de127cfe961195f52907af8451026eaa20Chris Craik    }
83e4db79de127cfe961195f52907af8451026eaa20Chris Craik}
84e4db79de127cfe961195f52907af8451026eaa20Chris Craik
854c3980b6e43cc7c0541f54b8e7e2d9d6108be622Chris CraikResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
864c3980b6e43cc7c0541f54b8e7e2d9d6108be622Chris Craik        const Matrix4& localTransform, const ClipBase* localClip) {
874c3980b6e43cc7c0541f54b8e7e2d9d6108be622Chris Craik    transform.loadMultiply(*snapshot.transform, localTransform);
8804d46eb69fb4f4c4c332c36c6ae845da3b2ae848Chris Craik    clipState = snapshot.serializeIntersectedClip(allocator, localClip, *(snapshot.transform));
894c3980b6e43cc7c0541f54b8e7e2d9d6108be622Chris Craik    clippedBounds = clipState->rect;
904c3980b6e43cc7c0541f54b8e7e2d9d6108be622Chris Craik    clipSideFlags = OpClipSideFlags::Full;
914c3980b6e43cc7c0541f54b8e7e2d9d6108be622Chris Craik    localProjectionPathMask = nullptr;
924c3980b6e43cc7c0541f54b8e7e2d9d6108be622Chris Craik}
934c3980b6e43cc7c0541f54b8e7e2d9d6108be622Chris Craik
946e068c0182f6f85bccb855a647510724d1c65a13Chris CraikResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot)
956e068c0182f6f85bccb855a647510724d1c65a13Chris Craik        : transform(*snapshot.transform)
966e068c0182f6f85bccb855a647510724d1c65a13Chris Craik        , clipState(snapshot.mutateClipArea().serializeClip(allocator))
976e068c0182f6f85bccb855a647510724d1c65a13Chris Craik        , clippedBounds(clipState->rect)
98678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik        , clipSideFlags(OpClipSideFlags::Full)
99678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik        , localProjectionPathMask(nullptr) {}
100e4db79de127cfe961195f52907af8451026eaa20Chris Craik
1014876de16e34622634266d09522c9153c78c7c2fbChris CraikResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& dstRect)
102b87eadda1818034ce03d85f30388384d1ac65916Chris Craik        : transform(Matrix4::identity())
1034876de16e34622634266d09522c9153c78c7c2fbChris Craik        , clipState(clipRect)
104b87eadda1818034ce03d85f30388384d1ac65916Chris Craik        , clippedBounds(dstRect)
105678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik        , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect))
106678ff81105753656aa4822f4f675ef96dc9d2b83Chris Craik        , localProjectionPathMask(nullptr) {
1074876de16e34622634266d09522c9153c78c7c2fbChris Craik    clippedBounds.doIntersect(clipRect->rect);
1084876de16e34622634266d09522c9153c78c7c2fbChris Craik}
109b87eadda1818034ce03d85f30388384d1ac65916Chris Craik
11080d2ade939153da87b3cd3b0a69a713bf68b64baChris CraikBakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator,
11180d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        Snapshot& snapshot, const RecordedOp& recordedOp) {
11280d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
11380d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
11480d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik            allocator, snapshot, recordedOp, false);
11580d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    if (bakedState->computedState.clippedBounds.isEmpty()) {
11680d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        // bounds are empty, so op is rejected
11780d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        allocator.rewindIfLastAlloc(bakedState);
11880d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        return nullptr;
11980d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    }
12080d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    return bakedState;
12180d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik}
12280d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik
12380d2ade939153da87b3cd3b0a69a713bf68b64baChris CraikBakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator,
12480d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        Snapshot& snapshot, const RecordedOp& recordedOp) {
12580d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
12680d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp);
12780d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik}
12880d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik
12980d2ade939153da87b3cd3b0a69a713bf68b64baChris CraikBakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator,
13080d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
13180d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
13280d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
13380d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik            ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
13480d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik            : true;
13580d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik
13680d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
13780d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik           allocator, snapshot, recordedOp, expandForStroke);
13880d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    if (bakedState->computedState.clippedBounds.isEmpty()) {
13980d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        // bounds are empty, so op is rejected
14080d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        // NOTE: this won't succeed if a clip was allocated
14180d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        allocator.rewindIfLastAlloc(bakedState);
14280d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        return nullptr;
14380d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    }
14480d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    return bakedState;
14580d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik}
14680d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik
14780d2ade939153da87b3cd3b0a69a713bf68b64baChris CraikBakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator,
14880d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
14980d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
15080d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik
15180d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    // clip isn't empty, so construct the op
15280d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
15380d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik}
15480d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik
15580d2ade939153da87b3cd3b0a69a713bf68b64baChris CraikBakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator,
15680d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik        const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
15780d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
15880d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik}
15980d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik
16080d2ade939153da87b3cd3b0a69a713bf68b64baChris Craikvoid BakedOpState::setupOpacity(const SkPaint* paint) {
16180d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik    computedState.opaqueOverClippedBounds = computedState.transform.isSimple()
16280d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik            && computedState.clipState->mode == ClipMode::Rectangle
16380d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik            && MathUtils::areEqual(alpha, 1.0f)
16480d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik            && !roundRectClipState
16580d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik            && PaintUtils::isOpaquePaint(paint);
16680d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik}
16780d2ade939153da87b3cd3b0a69a713bf68b64baChris Craik
168e4db79de127cfe961195f52907af8451026eaa20Chris Craik} // namespace uirenderer
169e4db79de127cfe961195f52907af8451026eaa20Chris Craik} // namespace android
170