RenderProperties.cpp revision e45b1fd03b524d2b57cc6c222d89076a31a08bea
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you mPrimitiveFields.may not use this file except in compliance with the License.
6 * You mPrimitiveFields.may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
19#include "RenderProperties.h"
20
21#include <utils/Trace.h>
22
23#include <SkCanvas.h>
24#include <SkMatrix.h>
25#include <SkPath.h>
26#include <SkPathOps.h>
27
28#include "Matrix.h"
29#include "utils/MathUtils.h"
30
31namespace android {
32namespace uirenderer {
33
34RenderProperties::PrimitiveFields::PrimitiveFields()
35        : mClipToBounds(true)
36        , mProjectBackwards(false)
37        , mProjectionReceiver(false)
38        , mAlpha(1)
39        , mHasOverlappingRendering(true)
40        , mElevation(0)
41        , mTranslationX(0), mTranslationY(0), mTranslationZ(0)
42        , mRotation(0), mRotationX(0), mRotationY(0)
43        , mScaleX(1), mScaleY(1)
44        , mPivotX(0), mPivotY(0)
45        , mLeft(0), mTop(0), mRight(0), mBottom(0)
46        , mWidth(0), mHeight(0)
47        , mPivotExplicitlySet(false)
48        , mMatrixOrPivotDirty(false)
49        , mCaching(false) {
50}
51
52RenderProperties::ComputedFields::ComputedFields()
53        : mTransformMatrix(NULL)
54        , mClipPath(NULL)
55        , mClipPathOp(SkRegion::kIntersect_Op) {
56}
57
58RenderProperties::ComputedFields::~ComputedFields() {
59    delete mTransformMatrix;
60    delete mClipPath;
61}
62
63RenderProperties::RenderProperties()
64        : mStaticMatrix(NULL)
65        , mAnimationMatrix(NULL) {
66}
67
68RenderProperties::~RenderProperties() {
69    delete mStaticMatrix;
70    delete mAnimationMatrix;
71}
72
73RenderProperties& RenderProperties::operator=(const RenderProperties& other) {
74    if (this != &other) {
75        mPrimitiveFields = other.mPrimitiveFields;
76        setStaticMatrix(other.getStaticMatrix());
77        setAnimationMatrix(other.getAnimationMatrix());
78        setCameraDistance(other.getCameraDistance());
79
80        // Update the computed clip path
81        updateClipPath();
82
83        // Force recalculation of the matrix, since other's dirty bit may be clear
84        mPrimitiveFields.mMatrixOrPivotDirty = true;
85        updateMatrix();
86    }
87    return *this;
88}
89
90void RenderProperties::debugOutputProperties(const int level) const {
91    if (mPrimitiveFields.mLeft != 0 || mPrimitiveFields.mTop != 0) {
92        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mPrimitiveFields.mLeft, mPrimitiveFields.mTop);
93    }
94    if (mStaticMatrix) {
95        ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
96                level * 2, "", mStaticMatrix, SK_MATRIX_ARGS(mStaticMatrix));
97    }
98    if (mAnimationMatrix) {
99        ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
100                level * 2, "", mAnimationMatrix, SK_MATRIX_ARGS(mAnimationMatrix));
101    }
102    if (hasTransformMatrix()) {
103        if (isTransformTranslateOnly()) {
104            ALOGD("%*sTranslate %.2f, %.2f, %.2f",
105                    level * 2, "", getTranslationX(), getTranslationY(), getZ());
106        } else {
107            ALOGD("%*sConcatMatrix %p: " SK_MATRIX_STRING,
108                    level * 2, "", mComputedFields.mTransformMatrix, SK_MATRIX_ARGS(mComputedFields.mTransformMatrix));
109        }
110    }
111
112    bool clipToBoundsNeeded = mPrimitiveFields.mCaching ? false : mPrimitiveFields.mClipToBounds;
113    if (mPrimitiveFields.mAlpha < 1) {
114        if (mPrimitiveFields.mCaching) {
115            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
116        } else if (!mPrimitiveFields.mHasOverlappingRendering) {
117            ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
118        } else {
119            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
120            if (clipToBoundsNeeded) {
121                flags |= SkCanvas::kClipToLayer_SaveFlag;
122                clipToBoundsNeeded = false; // clipping done by save layer
123            }
124            ALOGD("%*sSaveLayerAlpha %d, %d, %d, %d, %d, 0x%x", level * 2, "",
125                    0, 0, getWidth(), getHeight(),
126                    (int)(mPrimitiveFields.mAlpha * 255), flags);
127        }
128    }
129    if (clipToBoundsNeeded) {
130        ALOGD("%*sClipRect %d, %d, %d, %d", level * 2, "",
131                0, 0, getWidth(), getHeight());
132    }
133}
134
135void RenderProperties::updateMatrix() {
136    if (mPrimitiveFields.mMatrixOrPivotDirty) {
137        if (!mComputedFields.mTransformMatrix) {
138            // only allocate a mPrimitiveFields.matrix if we have a complex transform
139            mComputedFields.mTransformMatrix = new SkMatrix();
140        }
141        if (!mPrimitiveFields.mPivotExplicitlySet) {
142            mPrimitiveFields.mPivotX = mPrimitiveFields.mWidth / 2.0f;
143            mPrimitiveFields.mPivotY = mPrimitiveFields.mHeight / 2.0f;
144        }
145        SkMatrix* transform = mComputedFields.mTransformMatrix;
146        transform->reset();
147        if (MathUtils::isZero(getRotationX()) && MathUtils::isZero(getRotationY())) {
148            transform->setTranslate(getTranslationX(), getTranslationY());
149            transform->preRotate(getRotation(), getPivotX(), getPivotY());
150            transform->preScale(getScaleX(), getScaleY(), getPivotX(), getPivotY());
151        } else {
152            SkMatrix transform3D;
153            mComputedFields.mTransformCamera.save();
154            transform->preScale(getScaleX(), getScaleY(), getPivotX(), getPivotY());
155            mComputedFields.mTransformCamera.rotateX(mPrimitiveFields.mRotationX);
156            mComputedFields.mTransformCamera.rotateY(mPrimitiveFields.mRotationY);
157            mComputedFields.mTransformCamera.rotateZ(-mPrimitiveFields.mRotation);
158            mComputedFields.mTransformCamera.getMatrix(&transform3D);
159            transform3D.preTranslate(-getPivotX(), -getPivotY());
160            transform3D.postTranslate(getPivotX() + getTranslationX(),
161                    getPivotY() + getTranslationY());
162            transform->postConcat(transform3D);
163            mComputedFields.mTransformCamera.restore();
164        }
165        mPrimitiveFields.mMatrixOrPivotDirty = false;
166    }
167}
168
169void RenderProperties::updateClipPath() {
170    const SkPath* outlineClipPath = mPrimitiveFields.mOutline.willClip()
171            ? mPrimitiveFields.mOutline.getPath() : NULL;
172    const SkPath* revealClipPath = mPrimitiveFields.mRevealClip.getPath();
173
174    if (!outlineClipPath && !revealClipPath) {
175        // mComputedFields.mClipPath doesn't need to be updated, since it won't be used
176        return;
177    }
178
179    if (mComputedFields.mClipPath == NULL) {
180        mComputedFields.mClipPath = new SkPath();
181    }
182    SkPath* clipPath = mComputedFields.mClipPath;
183    mComputedFields.mClipPathOp = SkRegion::kIntersect_Op;
184
185    if (outlineClipPath && revealClipPath) {
186        SkPathOp op = kIntersect_PathOp;
187        if (mPrimitiveFields.mRevealClip.isInverseClip()) {
188            op = kDifference_PathOp; // apply difference step in the Op below, instead of draw time
189        }
190
191        Op(*outlineClipPath, *revealClipPath, op, clipPath);
192    } else if (outlineClipPath) {
193        *clipPath = *outlineClipPath;
194    } else {
195        *clipPath = *revealClipPath;
196        if (mPrimitiveFields.mRevealClip.isInverseClip()) {
197            // apply difference step at draw time
198            mComputedFields.mClipPathOp = SkRegion::kDifference_Op;
199        }
200    }
201}
202
203} /* namespace uirenderer */
204} /* namespace android */
205