RenderProperties.h revision 52c4eba143ce88a231e2691f65abd076f9e21c18
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You 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#pragma once 18 19#include "Caches.h" 20#include "DeviceInfo.h" 21#include "Rect.h" 22#include "RevealClip.h" 23#include "Outline.h" 24#include "utils/MathUtils.h" 25#include "utils/PaintUtils.h" 26 27#include <SkCamera.h> 28#include <SkMatrix.h> 29#include <SkRegion.h> 30#include <SkXfermode.h> 31 32#include <algorithm> 33#include <stddef.h> 34#include <vector> 35#include <cutils/compiler.h> 36#include <androidfw/ResourceTypes.h> 37#include <utils/Log.h> 38 39class SkBitmap; 40class SkColorFilter; 41class SkPaint; 42 43namespace android { 44namespace uirenderer { 45 46class Matrix4; 47class RenderNode; 48class RenderProperties; 49 50// The __VA_ARGS__ will be executed if a & b are not equal 51#define RP_SET(a, b, ...) ((a) != (b) ? ((a) = (b), ##__VA_ARGS__, true) : false) 52#define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true) 53 54// Keep in sync with View.java:LAYER_TYPE_* 55enum class LayerType { 56 None = 0, 57 // Although we cannot build the software layer directly (must be done at 58 // record time), this information is used when applying alpha. 59 Software = 1, 60 RenderLayer = 2, 61 // TODO: LayerTypeSurfaceTexture? Maybe? 62}; 63 64enum ClippingFlags { 65 CLIP_TO_BOUNDS = 0x1 << 0, 66 CLIP_TO_CLIP_BOUNDS = 0x1 << 1, 67}; 68 69class ANDROID_API LayerProperties { 70public: 71 bool setType(LayerType type) { 72 if (RP_SET(mType, type)) { 73 reset(); 74 return true; 75 } 76 return false; 77 } 78 79 bool setOpaque(bool opaque) { 80 return RP_SET(mOpaque, opaque); 81 } 82 83 bool opaque() const { 84 return mOpaque; 85 } 86 87 bool setAlpha(uint8_t alpha) { 88 return RP_SET(mAlpha, alpha); 89 } 90 91 uint8_t alpha() const { 92 return mAlpha; 93 } 94 95 bool setXferMode(SkXfermode::Mode mode) { 96 return RP_SET(mMode, mode); 97 } 98 99 SkXfermode::Mode xferMode() const { 100 return mMode; 101 } 102 103 bool setColorFilter(SkColorFilter* filter); 104 105 SkColorFilter* colorFilter() const { 106 return mColorFilter; 107 } 108 109 // Sets alpha, xfermode, and colorfilter from an SkPaint 110 // paint may be NULL, in which case defaults will be set 111 bool setFromPaint(const SkPaint* paint); 112 113 bool needsBlending() const { 114 return !opaque() || alpha() < 255; 115 } 116 117 LayerProperties& operator=(const LayerProperties& other); 118 119private: 120 LayerProperties(); 121 ~LayerProperties(); 122 void reset(); 123 124 // Private since external users should go through properties().effectiveLayerType() 125 LayerType type() const { 126 return mType; 127 } 128 129 friend class RenderProperties; 130 131 LayerType mType = LayerType::None; 132 // Whether or not that Layer's content is opaque, doesn't include alpha 133 bool mOpaque; 134 uint8_t mAlpha; 135 SkXfermode::Mode mMode; 136 SkColorFilter* mColorFilter = nullptr; 137}; 138 139/* 140 * Data structure that holds the properties for a RenderNode 141 */ 142class ANDROID_API RenderProperties { 143public: 144 RenderProperties(); 145 virtual ~RenderProperties(); 146 147 static bool setFlag(int flag, bool newValue, int* outFlags) { 148 if (newValue) { 149 if (!(flag & *outFlags)) { 150 *outFlags |= flag; 151 return true; 152 } 153 return false; 154 } else { 155 if (flag & *outFlags) { 156 *outFlags &= ~flag; 157 return true; 158 } 159 return false; 160 } 161 } 162 163 /** 164 * Set internal layer state based on whether this layer 165 * 166 * Additionally, returns true if child RenderNodes with functors will need to use a layer 167 * to support clipping. 168 */ 169 bool prepareForFunctorPresence(bool willHaveFunctor, bool ancestorDictatesFunctorsNeedLayer) { 170 // parent may have already dictated that a descendant layer is needed 171 bool functorsNeedLayer = ancestorDictatesFunctorsNeedLayer 172 173 // Round rect clipping forces layer for functors 174 || CC_UNLIKELY(getOutline().willRoundRectClip()) 175 || CC_UNLIKELY(getRevealClip().willClip()) 176 177 // Complex matrices forces layer, due to stencil clipping 178 || CC_UNLIKELY(getTransformMatrix() && !getTransformMatrix()->isScaleTranslate()) 179 || CC_UNLIKELY(getAnimationMatrix() && !getAnimationMatrix()->isScaleTranslate()) 180 || CC_UNLIKELY(getStaticMatrix() && !getStaticMatrix()->isScaleTranslate()); 181 182 mComputedFields.mNeedLayerForFunctors = (willHaveFunctor && functorsNeedLayer); 183 184 // If on a layer, will have consumed the need for isolating functors from stencil. 185 // Thus, it's safe to reset the flag until some descendent sets it. 186 return CC_LIKELY(effectiveLayerType() == LayerType::None) && functorsNeedLayer; 187 } 188 189 RenderProperties& operator=(const RenderProperties& other); 190 191 bool setClipToBounds(bool clipToBounds) { 192 return setFlag(CLIP_TO_BOUNDS, clipToBounds, &mPrimitiveFields.mClippingFlags); 193 } 194 195 bool setClipBounds(const Rect& clipBounds) { 196 bool ret = setFlag(CLIP_TO_CLIP_BOUNDS, true, &mPrimitiveFields.mClippingFlags); 197 return RP_SET(mPrimitiveFields.mClipBounds, clipBounds) || ret; 198 } 199 200 bool setClipBoundsEmpty() { 201 return setFlag(CLIP_TO_CLIP_BOUNDS, false, &mPrimitiveFields.mClippingFlags); 202 } 203 204 bool setProjectBackwards(bool shouldProject) { 205 return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject); 206 } 207 208 bool setProjectionReceiver(bool shouldReceive) { 209 return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldReceive); 210 } 211 212 bool isProjectionReceiver() const { 213 return mPrimitiveFields.mProjectionReceiver; 214 } 215 216 bool setStaticMatrix(const SkMatrix* matrix) { 217 delete mStaticMatrix; 218 if (matrix) { 219 mStaticMatrix = new SkMatrix(*matrix); 220 } else { 221 mStaticMatrix = nullptr; 222 } 223 return true; 224 } 225 226 // Can return NULL 227 const SkMatrix* getStaticMatrix() const { 228 return mStaticMatrix; 229 } 230 231 bool setAnimationMatrix(const SkMatrix* matrix) { 232 delete mAnimationMatrix; 233 if (matrix) { 234 mAnimationMatrix = new SkMatrix(*matrix); 235 } else { 236 mAnimationMatrix = nullptr; 237 } 238 return true; 239 } 240 241 bool setAlpha(float alpha) { 242 alpha = MathUtils::clampAlpha(alpha); 243 return RP_SET(mPrimitiveFields.mAlpha, alpha); 244 } 245 246 float getAlpha() const { 247 return mPrimitiveFields.mAlpha; 248 } 249 250 bool setHasOverlappingRendering(bool hasOverlappingRendering) { 251 return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering); 252 } 253 254 bool hasOverlappingRendering() const { 255 return mPrimitiveFields.mHasOverlappingRendering; 256 } 257 258 bool setElevation(float elevation) { 259 return RP_SET(mPrimitiveFields.mElevation, elevation); 260 // Don't dirty matrix/pivot, since they don't respect Z 261 } 262 263 float getElevation() const { 264 return mPrimitiveFields.mElevation; 265 } 266 267 bool setTranslationX(float translationX) { 268 return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX); 269 } 270 271 float getTranslationX() const { 272 return mPrimitiveFields.mTranslationX; 273 } 274 275 bool setTranslationY(float translationY) { 276 return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY); 277 } 278 279 float getTranslationY() const { 280 return mPrimitiveFields.mTranslationY; 281 } 282 283 bool setTranslationZ(float translationZ) { 284 return RP_SET(mPrimitiveFields.mTranslationZ, translationZ); 285 // mMatrixOrPivotDirty not set, since matrix doesn't respect Z 286 } 287 288 float getTranslationZ() const { 289 return mPrimitiveFields.mTranslationZ; 290 } 291 292 // Animation helper 293 bool setX(float value) { 294 return setTranslationX(value - getLeft()); 295 } 296 297 // Animation helper 298 float getX() const { 299 return getLeft() + getTranslationX(); 300 } 301 302 // Animation helper 303 bool setY(float value) { 304 return setTranslationY(value - getTop()); 305 } 306 307 // Animation helper 308 float getY() const { 309 return getTop() + getTranslationY(); 310 } 311 312 // Animation helper 313 bool setZ(float value) { 314 return setTranslationZ(value - getElevation()); 315 } 316 317 float getZ() const { 318 return getElevation() + getTranslationZ(); 319 } 320 321 bool setRotation(float rotation) { 322 return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation); 323 } 324 325 float getRotation() const { 326 return mPrimitiveFields.mRotation; 327 } 328 329 bool setRotationX(float rotationX) { 330 return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX); 331 } 332 333 float getRotationX() const { 334 return mPrimitiveFields.mRotationX; 335 } 336 337 bool setRotationY(float rotationY) { 338 return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY); 339 } 340 341 float getRotationY() const { 342 return mPrimitiveFields.mRotationY; 343 } 344 345 bool setScaleX(float scaleX) { 346 return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX); 347 } 348 349 float getScaleX() const { 350 return mPrimitiveFields.mScaleX; 351 } 352 353 bool setScaleY(float scaleY) { 354 return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY); 355 } 356 357 float getScaleY() const { 358 return mPrimitiveFields.mScaleY; 359 } 360 361 bool setPivotX(float pivotX) { 362 if (RP_SET(mPrimitiveFields.mPivotX, pivotX) 363 || !mPrimitiveFields.mPivotExplicitlySet) { 364 mPrimitiveFields.mMatrixOrPivotDirty = true; 365 mPrimitiveFields.mPivotExplicitlySet = true; 366 return true; 367 } 368 return false; 369 } 370 371 /* Note that getPivotX and getPivotY are adjusted by updateMatrix(), 372 * so the value returned may be stale if the RenderProperties has been 373 * modified since the last call to updateMatrix() 374 */ 375 float getPivotX() const { 376 return mPrimitiveFields.mPivotX; 377 } 378 379 bool setPivotY(float pivotY) { 380 if (RP_SET(mPrimitiveFields.mPivotY, pivotY) 381 || !mPrimitiveFields.mPivotExplicitlySet) { 382 mPrimitiveFields.mMatrixOrPivotDirty = true; 383 mPrimitiveFields.mPivotExplicitlySet = true; 384 return true; 385 } 386 return false; 387 } 388 389 float getPivotY() const { 390 return mPrimitiveFields.mPivotY; 391 } 392 393 bool isPivotExplicitlySet() const { 394 return mPrimitiveFields.mPivotExplicitlySet; 395 } 396 397 bool setCameraDistance(float distance) { 398 if (distance != getCameraDistance()) { 399 mPrimitiveFields.mMatrixOrPivotDirty = true; 400 mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance); 401 return true; 402 } 403 return false; 404 } 405 406 float getCameraDistance() const { 407 // TODO: update getCameraLocationZ() to be const 408 return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ(); 409 } 410 411 bool setLeft(int left) { 412 if (RP_SET(mPrimitiveFields.mLeft, left)) { 413 mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; 414 if (!mPrimitiveFields.mPivotExplicitlySet) { 415 mPrimitiveFields.mMatrixOrPivotDirty = true; 416 } 417 return true; 418 } 419 return false; 420 } 421 422 int getLeft() const { 423 return mPrimitiveFields.mLeft; 424 } 425 426 bool setTop(int top) { 427 if (RP_SET(mPrimitiveFields.mTop, top)) { 428 mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; 429 if (!mPrimitiveFields.mPivotExplicitlySet) { 430 mPrimitiveFields.mMatrixOrPivotDirty = true; 431 } 432 return true; 433 } 434 return false; 435 } 436 437 int getTop() const { 438 return mPrimitiveFields.mTop; 439 } 440 441 bool setRight(int right) { 442 if (RP_SET(mPrimitiveFields.mRight, right)) { 443 mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; 444 if (!mPrimitiveFields.mPivotExplicitlySet) { 445 mPrimitiveFields.mMatrixOrPivotDirty = true; 446 } 447 return true; 448 } 449 return false; 450 } 451 452 int getRight() const { 453 return mPrimitiveFields.mRight; 454 } 455 456 bool setBottom(int bottom) { 457 if (RP_SET(mPrimitiveFields.mBottom, bottom)) { 458 mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; 459 if (!mPrimitiveFields.mPivotExplicitlySet) { 460 mPrimitiveFields.mMatrixOrPivotDirty = true; 461 } 462 return true; 463 } 464 return false; 465 } 466 467 int getBottom() const { 468 return mPrimitiveFields.mBottom; 469 } 470 471 bool setLeftTop(int left, int top) { 472 bool leftResult = setLeft(left); 473 bool topResult = setTop(top); 474 return leftResult || topResult; 475 } 476 477 bool setLeftTopRightBottom(int left, int top, int right, int bottom) { 478 if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop 479 || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) { 480 mPrimitiveFields.mLeft = left; 481 mPrimitiveFields.mTop = top; 482 mPrimitiveFields.mRight = right; 483 mPrimitiveFields.mBottom = bottom; 484 mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; 485 mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; 486 if (!mPrimitiveFields.mPivotExplicitlySet) { 487 mPrimitiveFields.mMatrixOrPivotDirty = true; 488 } 489 return true; 490 } 491 return false; 492 } 493 494 bool offsetLeftRight(int offset) { 495 if (offset != 0) { 496 mPrimitiveFields.mLeft += offset; 497 mPrimitiveFields.mRight += offset; 498 return true; 499 } 500 return false; 501 } 502 503 bool offsetTopBottom(int offset) { 504 if (offset != 0) { 505 mPrimitiveFields.mTop += offset; 506 mPrimitiveFields.mBottom += offset; 507 return true; 508 } 509 return false; 510 } 511 512 int getWidth() const { 513 return mPrimitiveFields.mWidth; 514 } 515 516 int getHeight() const { 517 return mPrimitiveFields.mHeight; 518 } 519 520 const SkMatrix* getAnimationMatrix() const { 521 return mAnimationMatrix; 522 } 523 524 bool hasTransformMatrix() const { 525 return getTransformMatrix() && !getTransformMatrix()->isIdentity(); 526 } 527 528 // May only call this if hasTransformMatrix() is true 529 bool isTransformTranslateOnly() const { 530 return getTransformMatrix()->getType() == SkMatrix::kTranslate_Mask; 531 } 532 533 const SkMatrix* getTransformMatrix() const { 534 LOG_ALWAYS_FATAL_IF(mPrimitiveFields.mMatrixOrPivotDirty, "Cannot get a dirty matrix!"); 535 return mComputedFields.mTransformMatrix; 536 } 537 538 int getClippingFlags() const { 539 return mPrimitiveFields.mClippingFlags; 540 } 541 542 bool getClipToBounds() const { 543 return mPrimitiveFields.mClippingFlags & CLIP_TO_BOUNDS; 544 } 545 546 const Rect& getClipBounds() const { 547 return mPrimitiveFields.mClipBounds; 548 } 549 550 void getClippingRectForFlags(uint32_t flags, Rect* outRect) const { 551 if (flags & CLIP_TO_BOUNDS) { 552 outRect->set(0, 0, getWidth(), getHeight()); 553 if (flags & CLIP_TO_CLIP_BOUNDS) { 554 outRect->doIntersect(mPrimitiveFields.mClipBounds); 555 } 556 } else { 557 outRect->set(mPrimitiveFields.mClipBounds); 558 } 559 } 560 561 bool getHasOverlappingRendering() const { 562 return mPrimitiveFields.mHasOverlappingRendering; 563 } 564 565 const Outline& getOutline() const { 566 return mPrimitiveFields.mOutline; 567 } 568 569 const RevealClip& getRevealClip() const { 570 return mPrimitiveFields.mRevealClip; 571 } 572 573 bool getProjectBackwards() const { 574 return mPrimitiveFields.mProjectBackwards; 575 } 576 577 void debugOutputProperties(const int level) const; 578 579 void updateMatrix(); 580 581 Outline& mutableOutline() { 582 return mPrimitiveFields.mOutline; 583 } 584 585 RevealClip& mutableRevealClip() { 586 return mPrimitiveFields.mRevealClip; 587 } 588 589 const LayerProperties& layerProperties() const { 590 return mLayerProperties; 591 } 592 593 LayerProperties& mutateLayerProperties() { 594 return mLayerProperties; 595 } 596 597 // Returns true if damage calculations should be clipped to bounds 598 // TODO: Figure out something better for getZ(), as children should still be 599 // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX 600 // for this RP's getZ() anyway, this can be optimized when we have a 601 // Z damage estimate instead of INT_MAX 602 bool getClipDamageToBounds() const { 603 return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty()); 604 } 605 606 bool hasShadow() const { 607 return getZ() > 0.0f 608 && getOutline().getPath() != nullptr 609 && getOutline().getAlpha() != 0.0f; 610 } 611 612 bool fitsOnLayer() const { 613 const DeviceInfo* deviceInfo = DeviceInfo::get(); 614 return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() 615 && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize(); 616 } 617 618 bool promotedToLayer() const { 619 return mLayerProperties.mType == LayerType::None 620 && fitsOnLayer() 621 && (mComputedFields.mNeedLayerForFunctors 622 || (!MathUtils::isZero(mPrimitiveFields.mAlpha) 623 && mPrimitiveFields.mAlpha < 1 624 && mPrimitiveFields.mHasOverlappingRendering)); 625 } 626 627 LayerType effectiveLayerType() const { 628 return CC_UNLIKELY(promotedToLayer()) ? LayerType::RenderLayer : mLayerProperties.mType; 629 } 630 631private: 632 // Rendering properties 633 struct PrimitiveFields { 634 int mLeft = 0, mTop = 0, mRight = 0, mBottom = 0; 635 int mWidth = 0, mHeight = 0; 636 int mClippingFlags = CLIP_TO_BOUNDS; 637 float mAlpha = 1; 638 float mTranslationX = 0, mTranslationY = 0, mTranslationZ = 0; 639 float mElevation = 0; 640 float mRotation = 0, mRotationX = 0, mRotationY = 0; 641 float mScaleX = 1, mScaleY = 1; 642 float mPivotX = 0, mPivotY = 0; 643 bool mHasOverlappingRendering = false; 644 bool mPivotExplicitlySet = false; 645 bool mMatrixOrPivotDirty = false; 646 bool mProjectBackwards = false; 647 bool mProjectionReceiver = false; 648 Rect mClipBounds; 649 Outline mOutline; 650 RevealClip mRevealClip; 651 } mPrimitiveFields; 652 653 SkMatrix* mStaticMatrix; 654 SkMatrix* mAnimationMatrix; 655 LayerProperties mLayerProperties; 656 657 /** 658 * These fields are all generated from other properties and are not set directly. 659 */ 660 struct ComputedFields { 661 ComputedFields(); 662 ~ComputedFields(); 663 664 /** 665 * Stores the total transformation of the DisplayList based upon its scalar 666 * translate/rotate/scale properties. 667 * 668 * In the common translation-only case, the matrix isn't necessarily allocated, 669 * and the mTranslation properties are used directly. 670 */ 671 SkMatrix* mTransformMatrix; 672 673 Sk3DView mTransformCamera; 674 675 // Force layer on for functors to enable render features they don't yet support (clipping) 676 bool mNeedLayerForFunctors = false; 677 } mComputedFields; 678}; 679 680} /* namespace uirenderer */ 681} /* namespace android */ 682