/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_HWUI_BAKED_OP_STATE_H #define ANDROID_HWUI_BAKED_OP_STATE_H #include "Matrix.h" #include "RecordedOp.h" #include "Rect.h" #include "Snapshot.h" namespace android { namespace uirenderer { namespace OpClipSideFlags { enum { None = 0x0, Left = 0x1, Top = 0x2, Right = 0x4, Bottom = 0x8, Full = 0xF, // ConservativeFull = 0x1F needed? }; } /** * Holds a list of BakedOpStates of ops that can be drawn together */ struct MergedBakedOpList { const BakedOpState*const* states; size_t count; int clipSideFlags; Rect clip; }; /** * Holds the resolved clip, transform, and bounds of a recordedOp, when replayed with a snapshot */ class ResolvedRenderState { public: ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke); // Constructor for unbounded ops without transform/clip (namely shadows) ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot); // Constructor for primitive ops provided clip, and no transform ResolvedRenderState(const ClipRect* viewportRect, const Rect& dstRect); Rect computeLocalSpaceClip() const { Matrix4 inverse; inverse.loadInverse(transform); Rect outClip(clipRect()); inverse.mapRect(outClip); return outClip; } const Rect& clipRect() const { return clipState->rect; } bool requiresClip() const { return clipSideFlags != OpClipSideFlags::None || CC_UNLIKELY(clipState->mode != ClipMode::Rectangle); } // returns the clip if it's needed to draw the operation, otherwise nullptr const ClipBase* getClipIfNeeded() const { return requiresClip() ? clipState : nullptr; } Matrix4 transform; const ClipBase* clipState = nullptr; Rect clippedBounds; int clipSideFlags = 0; }; /** * Self-contained op wrapper, containing all resolved state required to draw the op. * * Stashed pointers within all point to longer lived objects, with no ownership implied. */ class BakedOpState { public: static BakedOpState* tryConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; BakedOpState* bakedState = allocator.create_trivial( allocator, snapshot, recordedOp, false); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } enum class StrokeBehavior { // stroking is forced, regardless of style on paint Forced, // stroking is defined by style on paint StyleDefined, }; static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined) ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style) : true; BakedOpState* bakedState = allocator.create_trivial( allocator, snapshot, recordedOp, expandForStroke); if (bakedState->computedState.clippedBounds.isEmpty()) { // bounds are empty, so op is rejected // NOTE: this won't succeed if a clip was allocated allocator.rewindIfLastAlloc(bakedState); return nullptr; } return bakedState; } static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) { if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; // clip isn't empty, so construct the op return allocator.create_trivial(allocator, snapshot, shadowOpPtr); } static BakedOpState* directConstruct(LinearAllocator& allocator, const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { return allocator.create_trivial(clip, dstRect, recordedOp); } // computed state: ResolvedRenderState computedState; // simple state (straight pointer/value storage): const float alpha; const RoundRectClipState* roundRectClipState; const ProjectionPathMask* projectionPathMask; const RecordedOp* op; private: friend class LinearAllocator; BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) : computedState(allocator, snapshot, recordedOp, expandForStroke) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) , projectionPathMask(snapshot.projectionPathMask) , op(&recordedOp) {} BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) : computedState(allocator, snapshot) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) , projectionPathMask(snapshot.projectionPathMask) , op(shadowOpPtr) {} BakedOpState(const ClipRect* viewportRect, const Rect& dstRect, const RecordedOp& recordedOp) : computedState(viewportRect, dstRect) , alpha(1.0f) , roundRectClipState(nullptr) , projectionPathMask(nullptr) , op(&recordedOp) {} }; }; // namespace uirenderer }; // namespace android #endif // ANDROID_HWUI_BAKED_OP_STATE_H