Merge "Use path intersection instead of saveLayer+mesh to mask projected ripples" into mnc-dev
This commit is contained in:
@@ -189,6 +189,9 @@ void CanvasState::setClippingRoundRect(LinearAllocator& allocator,
|
||||
mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
|
||||
}
|
||||
|
||||
void CanvasState::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
|
||||
mSnapshot->setProjectionPathMask(allocator, path);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Quick Rejection
|
||||
|
||||
@@ -130,6 +130,7 @@ public:
|
||||
void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
|
||||
void setClippingRoundRect(LinearAllocator& allocator,
|
||||
const Rect& rect, float radius, bool highPriority = true);
|
||||
void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
|
||||
|
||||
/**
|
||||
* Returns true if drawing in the rectangle (left, top, right, bottom)
|
||||
|
||||
@@ -195,6 +195,7 @@ public:
|
||||
// Identical round rect clip state means both ops will clip in the same way, or not at all.
|
||||
// As the state objects are const, we can compare their pointers to determine mergeability
|
||||
if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false;
|
||||
if (lhs->mProjectionPathMask != rhs->mProjectionPathMask) return false;
|
||||
|
||||
/* Clipping compatibility check
|
||||
*
|
||||
|
||||
@@ -63,6 +63,7 @@ public:
|
||||
mat4 mMatrix;
|
||||
float mAlpha;
|
||||
const RoundRectClipState* mRoundRectClipState;
|
||||
const ProjectionPathMask* mProjectionPathMask;
|
||||
};
|
||||
|
||||
class OpStatePair {
|
||||
|
||||
@@ -134,6 +134,12 @@ public:
|
||||
|
||||
uint8_t getType() const;
|
||||
|
||||
void multiplyInverse(const Matrix4& v) {
|
||||
Matrix4 inv;
|
||||
inv.loadInverse(v);
|
||||
multiply(inv);
|
||||
}
|
||||
|
||||
void multiply(const Matrix4& v) {
|
||||
Matrix4 u;
|
||||
u.loadMultiply(*this, v);
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
#include <SkCanvas.h>
|
||||
#include <SkColor.h>
|
||||
#include <SkPathOps.h>
|
||||
#include <SkShader.h>
|
||||
#include <SkTypeface.h>
|
||||
|
||||
@@ -1193,8 +1194,9 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
|
||||
state.mMatrix.load(*currentMatrix);
|
||||
state.mAlpha = currentSnapshot()->alpha;
|
||||
|
||||
// always store/restore, since it's just a pointer
|
||||
// always store/restore, since these are just pointers
|
||||
state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
|
||||
state.mProjectionPathMask = currentSnapshot()->projectionPathMask;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1202,6 +1204,7 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool
|
||||
setMatrix(state.mMatrix);
|
||||
writableSnapshot()->alpha = state.mAlpha;
|
||||
writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
|
||||
writableSnapshot()->projectionPathMask = state.mProjectionPathMask;
|
||||
|
||||
if (state.mClipValid && !skipClipRestore) {
|
||||
writableSnapshot()->setClip(state.mClip.left, state.mClip.top,
|
||||
@@ -1758,6 +1761,7 @@ void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
|
||||
void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
|
||||
VertexBuffer vertexBuffer;
|
||||
// TODO: try clipping large paths to viewport
|
||||
|
||||
PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
|
||||
drawVertexBuffer(vertexBuffer, paint);
|
||||
}
|
||||
@@ -1864,19 +1868,41 @@ void OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p
|
||||
|| PaintUtils::paintWillNotDraw(*p)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->getPathEffect() != nullptr) {
|
||||
mCaches.textureState().activateTexture(0);
|
||||
PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
|
||||
drawShape(x - radius, y - radius, texture, p);
|
||||
} else {
|
||||
SkPath path;
|
||||
if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
|
||||
path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
|
||||
} else {
|
||||
path.addCircle(x, y, radius);
|
||||
}
|
||||
drawConvexPath(path, p);
|
||||
return;
|
||||
}
|
||||
|
||||
SkPath path;
|
||||
if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
|
||||
path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
|
||||
} else {
|
||||
path.addCircle(x, y, radius);
|
||||
}
|
||||
|
||||
if (CC_UNLIKELY(currentSnapshot()->projectionPathMask != nullptr)) {
|
||||
// mask ripples with projection mask
|
||||
SkPath maskPath = *(currentSnapshot()->projectionPathMask->projectionMask);
|
||||
|
||||
Matrix4 screenSpaceTransform;
|
||||
currentSnapshot()->buildScreenSpaceTransform(&screenSpaceTransform);
|
||||
|
||||
Matrix4 totalTransform;
|
||||
totalTransform.loadInverse(screenSpaceTransform);
|
||||
totalTransform.multiply(currentSnapshot()->projectionPathMask->projectionMaskTransform);
|
||||
|
||||
SkMatrix skTotalTransform;
|
||||
totalTransform.copyTo(skTotalTransform);
|
||||
maskPath.transform(skTotalTransform);
|
||||
|
||||
// Mask the ripple path by the projection mask, now that it's
|
||||
// in local space. Note that this can create CCW paths.
|
||||
Op(path, maskPath, kIntersect_PathOp, &path);
|
||||
}
|
||||
drawConvexPath(path, p);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
|
||||
@@ -2149,6 +2175,10 @@ void OpenGLRenderer::setClippingRoundRect(LinearAllocator& allocator,
|
||||
mState.setClippingRoundRect(allocator, rect, radius, highPriority);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
|
||||
mState.setProjectionPathMask(allocator, path);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
|
||||
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
|
||||
DrawOpMode drawOpMode) {
|
||||
|
||||
@@ -399,6 +399,7 @@ public:
|
||||
void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
|
||||
void setClippingRoundRect(LinearAllocator& allocator,
|
||||
const Rect& rect, float radius, bool highPriority = true);
|
||||
void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
|
||||
|
||||
inline bool hasRectToRectTransform() const { return mState.hasRectToRectTransform(); }
|
||||
inline const mat4* currentTransform() const { return mState.currentTransform(); }
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
#include <SkPath.h>
|
||||
#include <SkPaint.h>
|
||||
#include <SkPoint.h>
|
||||
#include <SkGeometry.h> // WARNING: Internal Skia Header
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -912,6 +913,39 @@ void pushToVector(Vector<Vertex>& vertices, float x, float y) {
|
||||
Vertex::set(newVertex, x, y);
|
||||
}
|
||||
|
||||
class ClockwiseEnforcer {
|
||||
public:
|
||||
void addPoint(const SkPoint& point) {
|
||||
double x = point.x();
|
||||
double y = point.y();
|
||||
|
||||
if (initialized) {
|
||||
sum += (x + lastX) * (y - lastY);
|
||||
} else {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
}
|
||||
void reverseVectorIfNotClockwise(Vector<Vertex>& vertices) {
|
||||
if (sum < 0) {
|
||||
// negative sum implies CounterClockwise
|
||||
const int size = vertices.size();
|
||||
for (int i = 0; i < size / 2; i++) {
|
||||
Vertex tmp = vertices[i];
|
||||
int k = size - 1 - i;
|
||||
vertices.replaceAt(vertices[k], i);
|
||||
vertices.replaceAt(tmp, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool initialized = false;
|
||||
double lastX, lastY;
|
||||
double sum = 0;
|
||||
};
|
||||
|
||||
bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
|
||||
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
|
||||
Vector<Vertex>& outputVertices) {
|
||||
@@ -922,18 +956,22 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
|
||||
SkPath::Iter iter(path, forceClose);
|
||||
SkPoint pts[4];
|
||||
SkPath::Verb v;
|
||||
ClockwiseEnforcer clockwiseEnforcer;
|
||||
while (SkPath::kDone_Verb != (v = iter.next(pts))) {
|
||||
switch (v) {
|
||||
case SkPath::kMove_Verb:
|
||||
pushToVector(outputVertices, pts[0].x(), pts[0].y());
|
||||
ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
|
||||
clockwiseEnforcer.addPoint(pts[0]);
|
||||
break;
|
||||
case SkPath::kClose_Verb:
|
||||
ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
|
||||
clockwiseEnforcer.addPoint(pts[0]);
|
||||
break;
|
||||
case SkPath::kLine_Verb:
|
||||
ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
|
||||
pushToVector(outputVertices, pts[1].x(), pts[1].y());
|
||||
clockwiseEnforcer.addPoint(pts[1]);
|
||||
break;
|
||||
case SkPath::kQuad_Verb:
|
||||
ALOGV("kQuad_Verb");
|
||||
@@ -942,6 +980,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
|
||||
pts[2].x(), pts[2].y(),
|
||||
pts[1].x(), pts[1].y(),
|
||||
sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
|
||||
clockwiseEnforcer.addPoint(pts[1]);
|
||||
clockwiseEnforcer.addPoint(pts[2]);
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
ALOGV("kCubic_Verb");
|
||||
@@ -951,6 +991,9 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
|
||||
pts[3].x(), pts[3].y(),
|
||||
pts[2].x(), pts[2].y(),
|
||||
sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
|
||||
clockwiseEnforcer.addPoint(pts[1]);
|
||||
clockwiseEnforcer.addPoint(pts[2]);
|
||||
clockwiseEnforcer.addPoint(pts[3]);
|
||||
break;
|
||||
case SkPath::kConic_Verb: {
|
||||
ALOGV("kConic_Verb");
|
||||
@@ -965,6 +1008,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
|
||||
quads[offset+1].x(), quads[offset+1].y(),
|
||||
sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
|
||||
}
|
||||
clockwiseEnforcer.addPoint(pts[1]);
|
||||
clockwiseEnforcer.addPoint(pts[2]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -972,13 +1017,17 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
|
||||
}
|
||||
}
|
||||
|
||||
bool wasClosed = false;
|
||||
int size = outputVertices.size();
|
||||
if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
|
||||
outputVertices[0].y == outputVertices[size - 1].y) {
|
||||
outputVertices.pop();
|
||||
return true;
|
||||
wasClosed = true;
|
||||
}
|
||||
return false;
|
||||
|
||||
// ensure output vector is clockwise
|
||||
clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
|
||||
return wasClosed;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
const mat4& transform, VertexBuffer& vertexBuffer);
|
||||
|
||||
/**
|
||||
* Approximates a convex, CW outline into a Vector of 2d vertices.
|
||||
* Approximates a convex outline into a clockwise Vector of 2d vertices.
|
||||
*
|
||||
* @param path The outline to be approximated
|
||||
* @param thresholdSquared The threshold of acceptable error (in pixels) when approximating
|
||||
|
||||
@@ -769,31 +769,9 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
|
||||
const RenderProperties& backgroundProps = backgroundOp->mRenderNode->properties();
|
||||
renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
|
||||
|
||||
// If the projection reciever has an outline, we mask each of the projected rendernodes to it
|
||||
// Either with clipRect, or special saveLayer masking
|
||||
if (projectionReceiverOutline != nullptr) {
|
||||
const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
|
||||
if (projectionReceiverOutline->isRect(nullptr)) {
|
||||
// mask to the rect outline simply with clipRect
|
||||
ClipRectOp* clipOp = new (alloc) ClipRectOp(
|
||||
outlineBounds.left(), outlineBounds.top(),
|
||||
outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
|
||||
handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
|
||||
} else {
|
||||
// wrap the projected RenderNodes with a SaveLayer that will mask to the outline
|
||||
SaveLayerOp* op = new (alloc) SaveLayerOp(
|
||||
outlineBounds.left(), outlineBounds.top(),
|
||||
outlineBounds.right(), outlineBounds.bottom(),
|
||||
255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag);
|
||||
op->setMask(projectionReceiverOutline);
|
||||
handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
|
||||
|
||||
/* TODO: add optimizations here to take advantage of placement/size of projected
|
||||
* children (which may shrink saveLayer area significantly). This is dependent on
|
||||
* passing actual drawing/dirtying bounds of projected content down to native.
|
||||
*/
|
||||
}
|
||||
}
|
||||
// If the projection reciever has an outline, we mask projected content to it
|
||||
// (which we know, apriori, are all tessellated paths)
|
||||
renderer.setProjectionPathMask(alloc, projectionReceiverOutline);
|
||||
|
||||
// draw projected nodes
|
||||
for (size_t i = 0; i < mProjectedNodes.size(); i++) {
|
||||
@@ -808,10 +786,8 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
|
||||
renderer.restoreToCount(restoreTo);
|
||||
}
|
||||
|
||||
if (projectionReceiverOutline != nullptr) {
|
||||
handler(new (alloc) RestoreToCountOp(restoreTo),
|
||||
PROPERTY_SAVECOUNT, properties().getClipToBounds());
|
||||
}
|
||||
handler(new (alloc) RestoreToCountOp(restoreTo),
|
||||
PROPERTY_SAVECOUNT, properties().getClipToBounds());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -158,71 +158,6 @@ Vector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Test whether the polygon is order in clockwise.
|
||||
*
|
||||
* @param polygon the polygon as a Vector2 array
|
||||
* @param len the number of points of the polygon
|
||||
*/
|
||||
bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) {
|
||||
if (len < 2 || polygon == nullptr) {
|
||||
return true;
|
||||
}
|
||||
double sum = 0;
|
||||
double p1x = polygon[len - 1].x;
|
||||
double p1y = polygon[len - 1].y;
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
||||
double p2x = polygon[i].x;
|
||||
double p2y = polygon[i].y;
|
||||
sum += p1x * p2y - p2x * p1y;
|
||||
p1x = p2x;
|
||||
p1y = p2y;
|
||||
}
|
||||
return sum < 0;
|
||||
}
|
||||
|
||||
bool ShadowTessellator::isClockwisePath(const SkPath& path) {
|
||||
SkPath::Iter iter(path, false);
|
||||
SkPoint pts[4];
|
||||
SkPath::Verb v;
|
||||
|
||||
Vector<Vector2> arrayForDirection;
|
||||
while (SkPath::kDone_Verb != (v = iter.next(pts))) {
|
||||
switch (v) {
|
||||
case SkPath::kMove_Verb:
|
||||
arrayForDirection.add((Vector2){pts[0].x(), pts[0].y()});
|
||||
break;
|
||||
case SkPath::kLine_Verb:
|
||||
arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
|
||||
break;
|
||||
case SkPath::kConic_Verb:
|
||||
case SkPath::kQuad_Verb:
|
||||
arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
|
||||
arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
|
||||
break;
|
||||
case SkPath::kCubic_Verb:
|
||||
arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
|
||||
arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
|
||||
arrayForDirection.add((Vector2){pts[3].x(), pts[3].y()});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isClockwise(arrayForDirection.array(), arrayForDirection.size());
|
||||
}
|
||||
|
||||
void ShadowTessellator::reverseVertexArray(Vertex* polygon, int len) {
|
||||
int n = len / 2;
|
||||
for (int i = 0; i < n; i++) {
|
||||
Vertex tmp = polygon[i];
|
||||
int k = len - 1 - i;
|
||||
polygon[i] = polygon[k];
|
||||
polygon[k] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1,
|
||||
const Vector2& vector2, float divisor) {
|
||||
|
||||
@@ -83,23 +83,6 @@ public:
|
||||
static bool isClockwise(const Vector2* polygon, int len);
|
||||
|
||||
static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2);
|
||||
/**
|
||||
* Determine whether the path is clockwise, using the control points.
|
||||
*
|
||||
* TODO: Given the skia is using inverted Y coordinate, shadow system needs
|
||||
* to convert to the same coordinate to avoid the extra reverse.
|
||||
*
|
||||
* @param path The path to be examined.
|
||||
*/
|
||||
static bool isClockwisePath(const SkPath &path);
|
||||
|
||||
/**
|
||||
* Reverse the vertex array.
|
||||
*
|
||||
* @param polygon The vertex array to be reversed.
|
||||
* @param len The length of the vertex array.
|
||||
*/
|
||||
static void reverseVertexArray(Vertex* polygon, int len);
|
||||
|
||||
static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
|
||||
float divisor);
|
||||
|
||||
@@ -36,6 +36,7 @@ Snapshot::Snapshot()
|
||||
, empty(false)
|
||||
, alpha(1.0f)
|
||||
, roundRectClipState(nullptr)
|
||||
, projectionPathMask(nullptr)
|
||||
, mClipArea(&mClipAreaRoot) {
|
||||
transform = &mTransformRoot;
|
||||
region = nullptr;
|
||||
@@ -54,6 +55,7 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
|
||||
, empty(false)
|
||||
, alpha(s->alpha)
|
||||
, roundRectClipState(s->roundRectClipState)
|
||||
, projectionPathMask(s->projectionPathMask)
|
||||
, mClipArea(nullptr)
|
||||
, mViewportData(s->mViewportData)
|
||||
, mRelativeLightCenter(s->mRelativeLightCenter) {
|
||||
@@ -141,6 +143,34 @@ void Snapshot::resetTransform(float x, float y, float z) {
|
||||
transform->loadTranslate(x, y, z);
|
||||
}
|
||||
|
||||
void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
|
||||
// build (reverse ordered) list of the stack of snapshots, terminated with a NULL
|
||||
Vector<const Snapshot*> snapshotList;
|
||||
snapshotList.push(nullptr);
|
||||
const Snapshot* current = this;
|
||||
do {
|
||||
snapshotList.push(current);
|
||||
current = current->previous.get();
|
||||
} while (current);
|
||||
|
||||
// traverse the list, adding in each transform that contributes to the total transform
|
||||
outTransform->loadIdentity();
|
||||
for (size_t i = snapshotList.size() - 1; i > 0; i--) {
|
||||
// iterate down the stack
|
||||
const Snapshot* current = snapshotList[i];
|
||||
const Snapshot* next = snapshotList[i - 1];
|
||||
if (current->flags & kFlagIsFboLayer) {
|
||||
// if we've hit a layer, translate by the layer's draw offset
|
||||
outTransform->translate(current->layer->layer.left, current->layer->layer.top);
|
||||
}
|
||||
if (!next || (next->flags & kFlagIsFboLayer)) {
|
||||
// if this snapshot is last, or if this snapshot is last before an
|
||||
// FBO layer (which reset the transform), apply it
|
||||
outTransform->multiply(*(current->transform));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Clipping round rect
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -191,6 +221,18 @@ void Snapshot::setClippingRoundRect(LinearAllocator& allocator, const Rect& boun
|
||||
roundRectClipState = state;
|
||||
}
|
||||
|
||||
void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
|
||||
if (path) {
|
||||
ProjectionPathMask* mask = new (allocator) ProjectionPathMask;
|
||||
mask->projectionMask = path;
|
||||
buildScreenSpaceTransform(&(mask->projectionMaskTransform));
|
||||
|
||||
projectionPathMask = mask;
|
||||
} else {
|
||||
projectionPathMask = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Queries
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -63,6 +63,17 @@ public:
|
||||
float radius;
|
||||
};
|
||||
|
||||
class ProjectionPathMask {
|
||||
public:
|
||||
/** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
|
||||
static void* operator new(size_t size, LinearAllocator& allocator) {
|
||||
return allocator.alloc(size);
|
||||
}
|
||||
|
||||
const SkPath* projectionMask;
|
||||
Matrix4 projectionMaskTransform;
|
||||
};
|
||||
|
||||
/**
|
||||
* A snapshot holds information about the current state of the rendering
|
||||
* surface. A snapshot is usually created whenever the user calls save()
|
||||
@@ -189,6 +200,11 @@ public:
|
||||
void setClippingRoundRect(LinearAllocator& allocator, const Rect& bounds,
|
||||
float radius, bool highPriority);
|
||||
|
||||
/**
|
||||
* Sets (and replaces) the current projection mask
|
||||
*/
|
||||
void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
|
||||
|
||||
/**
|
||||
* Indicates whether this snapshot should be ignored. A snapshot
|
||||
* is typically ignored if its layer is invisible or empty.
|
||||
@@ -200,6 +216,12 @@ public:
|
||||
*/
|
||||
bool hasPerspectiveTransform() const;
|
||||
|
||||
/**
|
||||
* Fills outTransform with the current, total transform to screen space,
|
||||
* across layer boundaries.
|
||||
*/
|
||||
void buildScreenSpaceTransform(Matrix4* outTransform) const;
|
||||
|
||||
/**
|
||||
* Dirty flags.
|
||||
*/
|
||||
@@ -272,6 +294,11 @@ public:
|
||||
*/
|
||||
const RoundRectClipState* roundRectClipState;
|
||||
|
||||
/**
|
||||
* Current projection masking path - used exclusively to mask tessellated circles.
|
||||
*/
|
||||
const ProjectionPathMask* projectionPathMask;
|
||||
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -207,6 +207,16 @@ static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* t
|
||||
transformXY->mapPoint(point.x, point.y);
|
||||
}
|
||||
|
||||
static void reverseVertexArray(Vertex* polygon, int len) {
|
||||
int n = len / 2;
|
||||
for (int i = 0; i < n; i++) {
|
||||
Vertex tmp = polygon[i];
|
||||
int k = len - 1 - i;
|
||||
polygon[i] = polygon[k];
|
||||
polygon[k] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void tessellateShadows(
|
||||
const Matrix4* drawTransform, const Rect* localClip,
|
||||
bool isCasterOpaque, const SkPath* casterPerimeter,
|
||||
@@ -219,10 +229,9 @@ static void tessellateShadows(
|
||||
const float casterRefinementThresholdSquared = 4.0f;
|
||||
PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
|
||||
casterRefinementThresholdSquared, casterVertices2d);
|
||||
if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
|
||||
ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
|
||||
casterVertices2d.size());
|
||||
}
|
||||
|
||||
// Shadow requires CCW for now. TODO: remove potential double-reverse
|
||||
reverseVertexArray(casterVertices2d.editArray(), casterVertices2d.size());
|
||||
|
||||
if (casterVertices2d.size() == 0) return;
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="11" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" />
|
||||
|
||||
<application
|
||||
android:label="HwUi"
|
||||
android:hardwareAccelerated="true">
|
||||
android:theme="@android:style/Theme.Material.Light">
|
||||
|
||||
<activity
|
||||
android:name="HwTests"
|
||||
@@ -42,8 +42,7 @@
|
||||
|
||||
<activity
|
||||
android:name="PathOpsActivity"
|
||||
android:label="Path/Ops"
|
||||
android:theme="@android:style/Theme.Holo.Light">
|
||||
android:label="Path/Ops">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="com.android.test.hwui.TEST" />
|
||||
@@ -52,8 +51,7 @@
|
||||
|
||||
<activity
|
||||
android:name="AssetsAtlasActivity"
|
||||
android:label="Atlas/Framework"
|
||||
android:theme="@android:style/Theme.Holo.Light">
|
||||
android:label="Atlas/Framework">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="com.android.test.hwui.TEST" />
|
||||
@@ -62,8 +60,7 @@
|
||||
|
||||
<activity
|
||||
android:name="ScaledTextActivity"
|
||||
android:label="Text/Scaled"
|
||||
android:theme="@android:style/Theme.Holo.Light">
|
||||
android:label="Text/Scaled">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="com.android.test.hwui.TEST" />
|
||||
@@ -72,8 +69,7 @@
|
||||
|
||||
<activity
|
||||
android:name="Rotate3dTextActivity"
|
||||
android:label="Text/3D Rotation"
|
||||
android:theme="@android:style/Theme.Holo.Light">
|
||||
android:label="Text/3D Rotation">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="com.android.test.hwui.TEST" />
|
||||
|
||||
Reference in New Issue
Block a user