mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
d601cc084b
Summary: Adds a flag that can be set in FlatViewGroup to see known performance issues in Nodes objects. This is mostly useful in internal development of Nodes, and will be a dead code path when not set. Reviewed By: ahmedre Differential Revision: D3732675
302 lines
8.7 KiB
Java
302 lines
8.7 KiB
Java
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
package com.facebook.react.flat;
|
|
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Color;
|
|
import android.graphics.Paint;
|
|
import android.graphics.Typeface;
|
|
|
|
/**
|
|
* Base class for all DrawCommands. Becomes immutable once it has its bounds set. Until then, a
|
|
* subclass is able to mutate any of its properties (e.g. updating Layout in DrawTextLayout).
|
|
*
|
|
* The idea is to be able to reuse unmodified objects when we build up DrawCommands before we ship
|
|
* them to UI thread, but we can only do that if DrawCommands are immutable.
|
|
*/
|
|
/* package */ abstract class AbstractDrawCommand extends DrawCommand implements Cloneable {
|
|
|
|
private float mLeft;
|
|
private float mTop;
|
|
private float mRight;
|
|
private float mBottom;
|
|
private boolean mFrozen;
|
|
|
|
protected boolean mNeedsClipping;
|
|
private float mClipLeft;
|
|
private float mClipTop;
|
|
private float mClipRight;
|
|
private float mClipBottom;
|
|
|
|
// Used to draw highlights in debug draw.
|
|
private static Paint sDebugHighlightRed;
|
|
private static Paint sDebugHighlightYellow;
|
|
private static Paint sDebugHighlightOverlayText;
|
|
|
|
public final boolean clipBoundsMatch(
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom) {
|
|
return mClipLeft == clipLeft && mClipTop == clipTop
|
|
&& mClipRight == clipRight && mClipBottom == clipBottom;
|
|
}
|
|
|
|
protected final void setClipBounds(
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom) {
|
|
mClipLeft = clipLeft;
|
|
mClipTop = clipTop;
|
|
mClipRight = clipRight;
|
|
mClipBottom = clipBottom;
|
|
// We put this check here to not clip when we have the default [-infinity, infinity] bounds,
|
|
// since clipRect in those cases is essentially no-op anyway. This is needed to fix a bug that
|
|
// shows up during screenshot testing. Note that checking one side is enough, since if one side
|
|
// is infinite, all sides will be infinite, since we only set infinite for all sides at the
|
|
// same time - conversely, if one side is finite, all sides will be finite.
|
|
mNeedsClipping = mClipLeft != Float.NEGATIVE_INFINITY;
|
|
}
|
|
|
|
public final float getClipLeft() {
|
|
return mClipLeft;
|
|
}
|
|
|
|
public final float getClipTop() {
|
|
return mClipTop;
|
|
}
|
|
|
|
public final float getClipRight() {
|
|
return mClipRight;
|
|
}
|
|
|
|
public final float getClipBottom() {
|
|
return mClipBottom;
|
|
}
|
|
|
|
protected void applyClipping(Canvas canvas) {
|
|
canvas.clipRect(mClipLeft, mClipTop, mClipRight, mClipBottom);
|
|
}
|
|
|
|
/**
|
|
* Don't override this unless you need to do custom clipping in a draw command. Otherwise just
|
|
* override onPreDraw and onDraw.
|
|
*/
|
|
@Override
|
|
public void draw(FlatViewGroup parent, Canvas canvas) {
|
|
onPreDraw(parent, canvas);
|
|
if (mNeedsClipping && shouldClip()) {
|
|
canvas.save(Canvas.CLIP_SAVE_FLAG);
|
|
applyClipping(canvas);
|
|
onDraw(canvas);
|
|
canvas.restore();
|
|
} else {
|
|
onDraw(canvas);
|
|
}
|
|
}
|
|
|
|
protected static int getDebugBorderColor() {
|
|
return Color.CYAN;
|
|
}
|
|
|
|
protected String getDebugName() {
|
|
return getClass().getSimpleName().substring(4);
|
|
}
|
|
|
|
private void initDebugHighlightResources(FlatViewGroup parent) {
|
|
if (sDebugHighlightRed == null) {
|
|
sDebugHighlightRed = new Paint();
|
|
sDebugHighlightRed.setARGB(75, 255, 0, 0);
|
|
}
|
|
if (sDebugHighlightYellow == null) {
|
|
sDebugHighlightYellow = new Paint();
|
|
sDebugHighlightYellow.setARGB(100, 255, 204, 0);
|
|
}
|
|
if (sDebugHighlightOverlayText == null) {
|
|
sDebugHighlightOverlayText = new Paint();
|
|
sDebugHighlightOverlayText.setAntiAlias(true);
|
|
sDebugHighlightOverlayText.setARGB(200, 50, 50, 50);
|
|
sDebugHighlightOverlayText.setTextAlign(Paint.Align.RIGHT);
|
|
sDebugHighlightOverlayText.setTypeface(Typeface.MONOSPACE);
|
|
sDebugHighlightOverlayText.setTextSize(parent.dipsToPixels(9));
|
|
}
|
|
}
|
|
|
|
private void debugDrawHighlightRect(Canvas canvas, Paint paint, String text) {
|
|
canvas.drawRect(getLeft(), getTop(), getRight(), getBottom(), paint);
|
|
canvas.drawText(text, getRight() - 5, getBottom() - 5, sDebugHighlightOverlayText);
|
|
}
|
|
|
|
protected void debugDrawWarningHighlight(Canvas canvas, String text) {
|
|
debugDrawHighlightRect(canvas, sDebugHighlightRed, text);
|
|
}
|
|
|
|
protected void debugDrawCautionHighlight(Canvas canvas, String text) {
|
|
debugDrawHighlightRect(canvas, sDebugHighlightYellow, text);
|
|
}
|
|
|
|
@Override
|
|
public final void debugDraw(FlatViewGroup parent, Canvas canvas) {
|
|
onDebugDraw(parent, canvas);
|
|
if (FlatViewGroup.DEBUG_HIGHLIGHT_PERFORMANCE_ISSUES) {
|
|
initDebugHighlightResources(parent);
|
|
onDebugDrawHighlight(canvas);
|
|
}
|
|
}
|
|
|
|
protected void onDebugDraw(FlatViewGroup parent, Canvas canvas) {
|
|
parent.debugDrawNamedRect(
|
|
canvas,
|
|
getDebugBorderColor(),
|
|
getDebugName(),
|
|
mLeft,
|
|
mTop,
|
|
mRight,
|
|
mBottom);
|
|
}
|
|
|
|
protected void onDebugDrawHighlight(Canvas canvas) {
|
|
}
|
|
|
|
protected void onPreDraw(FlatViewGroup parent, Canvas canvas) {
|
|
}
|
|
|
|
/**
|
|
* Updates boundaries of the AbstractDrawCommand and freezes it.
|
|
* Will return a frozen copy if the current AbstractDrawCommand cannot be mutated.
|
|
*
|
|
* This should not be called on a DrawView, as the DrawView is modified on UI thread. Use
|
|
* DrawView.collectDrawView instead to avoid race conditions.
|
|
*/
|
|
public AbstractDrawCommand updateBoundsAndFreeze(
|
|
float left,
|
|
float top,
|
|
float right,
|
|
float bottom,
|
|
float clipLeft,
|
|
float clipTop,
|
|
float clipRight,
|
|
float clipBottom) {
|
|
if (mFrozen) {
|
|
// see if we can reuse it
|
|
boolean boundsMatch = boundsMatch(left, top, right, bottom);
|
|
boolean clipBoundsMatch = clipBoundsMatch(clipLeft, clipTop, clipRight, clipBottom);
|
|
if (boundsMatch && clipBoundsMatch) {
|
|
return this;
|
|
}
|
|
|
|
try {
|
|
AbstractDrawCommand copy = (AbstractDrawCommand) clone();
|
|
if (!boundsMatch) {
|
|
copy.setBounds(left, top, right, bottom);
|
|
}
|
|
if (!clipBoundsMatch) {
|
|
copy.setClipBounds(clipLeft, clipTop, clipRight, clipBottom);
|
|
}
|
|
return copy;
|
|
} catch (CloneNotSupportedException e) {
|
|
// This should not happen since AbstractDrawCommand implements Cloneable
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
setBounds(left, top, right, bottom);
|
|
setClipBounds(clipLeft, clipTop, clipRight, clipBottom);
|
|
mFrozen = true;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Returns a non-frozen shallow copy of AbstractDrawCommand as defined by {@link Object#clone()}.
|
|
*/
|
|
public final AbstractDrawCommand mutableCopy() {
|
|
try {
|
|
AbstractDrawCommand copy = (AbstractDrawCommand) super.clone();
|
|
copy.mFrozen = false;
|
|
return copy;
|
|
} catch (CloneNotSupportedException e) {
|
|
// should not happen since we implement Cloneable
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether this object was frozen and thus cannot be mutated.
|
|
*/
|
|
public final boolean isFrozen() {
|
|
return mFrozen;
|
|
}
|
|
|
|
/**
|
|
* Mark this object as frozen, indicating that it should not be mutated.
|
|
*/
|
|
public final void freeze() {
|
|
mFrozen = true;
|
|
}
|
|
|
|
/**
|
|
* Left position of this DrawCommand relative to the hosting View.
|
|
*/
|
|
public final float getLeft() {
|
|
return mLeft;
|
|
}
|
|
|
|
/**
|
|
* Top position of this DrawCommand relative to the hosting View.
|
|
*/
|
|
public final float getTop() {
|
|
return mTop;
|
|
}
|
|
|
|
/**
|
|
* Right position of this DrawCommand relative to the hosting View.
|
|
*/
|
|
public final float getRight() {
|
|
return mRight;
|
|
}
|
|
|
|
/**
|
|
* Bottom position of this DrawCommand relative to the hosting View.
|
|
*/
|
|
public final float getBottom() {
|
|
return mBottom;
|
|
}
|
|
|
|
protected abstract void onDraw(Canvas canvas);
|
|
|
|
protected boolean shouldClip() {
|
|
return mLeft < getClipLeft() || mTop < getClipTop() ||
|
|
mRight > getClipRight() || mBottom > getClipBottom();
|
|
}
|
|
|
|
protected void onBoundsChanged() {
|
|
}
|
|
|
|
/**
|
|
* Updates boundaries of this DrawCommand.
|
|
*/
|
|
protected final void setBounds(float left, float top, float right, float bottom) {
|
|
mLeft = left;
|
|
mTop = top;
|
|
mRight = right;
|
|
mBottom = bottom;
|
|
|
|
onBoundsChanged();
|
|
}
|
|
|
|
/**
|
|
* Returns true if boundaries match and don't need to be updated. False otherwise.
|
|
*/
|
|
protected final boolean boundsMatch(float left, float top, float right, float bottom) {
|
|
return mLeft == left && mTop == top && mRight == right && mBottom == bottom;
|
|
}
|
|
}
|