LOG_ALWAYS_FATAL("unsupported operation");
}
+void BakedOpDispatcher::onCirclePropsOp(BakedOpRenderer&, const CirclePropsOp&, const BakedOpState&) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onRoundRectPropsOp(BakedOpRenderer&, const RoundRectPropsOp&, const BakedOpState&) {
+ LOG_ALWAYS_FATAL("unsupported operation");
+}
+
namespace VertexBufferRenderFlags {
enum {
Offset = 0x1,
// (temp layers are clipped to viewport, since they don't persist offscreen content)
SkPaint saveLayerPaint;
saveLayerPaint.setAlpha(properties.getAlpha());
- onBeginLayerOp(*new (mAllocator) BeginLayerOp(
+ deferBeginLayerOp(*new (mAllocator) BeginLayerOp(
saveLayerBounds,
Matrix4::identity(),
saveLayerBounds,
&saveLayerPaint));
deferNodeOps(node);
- onEndLayerOp(*new (mAllocator) EndLayerOp());
+ deferEndLayerOp(*new (mAllocator) EndLayerOp());
} else {
deferNodeOps(node);
}
}
const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
- deferRenderNodeOp(*childOp);
+ deferRenderNodeOpImpl(*childOp);
drawIndex++;
}
}
int restoreTo = mCanvasState.save(SkCanvas::kMatrix_SaveFlag);
mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);
- deferRenderNodeOp(*childOp);
+ deferRenderNodeOpImpl(*childOp);
mCanvasState.restoreToCount(restoreTo);
}
}
/**
- * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
+ * Used to define a list of lambdas referencing private OpReorderer::onXX::defer() methods.
*
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
* E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
*/
#define OP_RECEIVER(Type) \
- [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
+ [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
typedef void (*OpDispatcher) (OpReorderer& reorderer, const RecordedOp& op);
static OpDispatcher receivers[] = {
}
}
-void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
if (op.renderNode->nothingToDraw()) return;
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
mCanvasState.restoreToCount(count);
}
-void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
+void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
if (!op.skipInOrderDraw) {
- deferRenderNodeOp(op);
+ deferRenderNodeOpImpl(op);
}
}
* Defers an unmergeable, strokeable op, accounting correctly
* for paint's style on the bounds being computed.
*/
-void OpReorderer::onStrokeableOp(const RecordedOp& op, batchid_t batchId,
+void OpReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior) {
// Note: here we account for stroke when baking the op
BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
: (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
}
-void OpReorderer::onArcOp(const ArcOp& op) {
- onStrokeableOp(op, tessBatchId(op));
+void OpReorderer::deferArcOp(const ArcOp& op) {
+ deferStrokeableOp(op, tessBatchId(op));
}
-void OpReorderer::onBitmapOp(const BitmapOp& op) {
+void OpReorderer::deferBitmapOp(const BitmapOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
}
}
-void OpReorderer::onBitmapMeshOp(const BitmapMeshOp& op) {
+void OpReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void OpReorderer::onBitmapRectOp(const BitmapRectOp& op) {
+void OpReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void OpReorderer::onLinesOp(const LinesOp& op) {
+void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+ // allocate a temporary oval op (with mAllocator, so it persists until render), so the
+ // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
+ float x = *(op.x);
+ float y = *(op.y);
+ float radius = *(op.radius);
+ Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius);
+ const OvalOp* resolvedOp = new (mAllocator) OvalOp(
+ unmappedBounds,
+ op.localMatrix,
+ op.localClipRect,
+ op.paint);
+ deferOvalOp(*resolvedOp);
+}
+
+void OpReorderer::deferLinesOp(const LinesOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
- onStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
+ deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
}
-void OpReorderer::onOvalOp(const OvalOp& op) {
- onStrokeableOp(op, tessBatchId(op));
+void OpReorderer::deferOvalOp(const OvalOp& op) {
+ deferStrokeableOp(op, tessBatchId(op));
}
-void OpReorderer::onPatchOp(const PatchOp& op) {
+void OpReorderer::deferPatchOp(const PatchOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
}
}
-void OpReorderer::onPathOp(const PathOp& op) {
- onStrokeableOp(op, OpBatchType::Bitmap);
+void OpReorderer::deferPathOp(const PathOp& op) {
+ deferStrokeableOp(op, OpBatchType::Bitmap);
}
-void OpReorderer::onPointsOp(const PointsOp& op) {
+void OpReorderer::deferPointsOp(const PointsOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
- onStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
+ deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
+}
+
+void OpReorderer::deferRectOp(const RectOp& op) {
+ deferStrokeableOp(op, tessBatchId(op));
}
-void OpReorderer::onRectOp(const RectOp& op) {
- onStrokeableOp(op, tessBatchId(op));
+void OpReorderer::deferRoundRectOp(const RoundRectOp& op) {
+ deferStrokeableOp(op, tessBatchId(op));
}
-void OpReorderer::onRoundRectOp(const RoundRectOp& op) {
- onStrokeableOp(op, tessBatchId(op));
+void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+ // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
+ // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
+ const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
+ Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)),
+ op.localMatrix,
+ op.localClipRect,
+ op.paint, *op.rx, *op.ry);
+ deferRoundRectOp(*resolvedOp);
}
-void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
+void OpReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
}
-void OpReorderer::onTextOp(const TextOp& op) {
+void OpReorderer::deferTextOp(const TextOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
}
// TODO: test rejection at defer time, where the bounds become empty
-void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
&op, nullptr);
}
-void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
+void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
int finishedLayerIndex = mLayerStack.back();
}
}
-void OpReorderer::onLayerOp(const LayerOp& op) {
+void OpReorderer::deferLayerOp(const LayerOp& op) {
LOG_ALWAYS_FATAL("unsupported");
}
-void OpReorderer::onShadowOp(const ShadowOp& op) {
+void OpReorderer::deferShadowOp(const ShadowOp& op) {
LOG_ALWAYS_FATAL("unsupported");
}
void deferNodeOps(const RenderNode& renderNode);
- void deferRenderNodeOp(const RenderNodeOp& op);
+ void deferRenderNodeOpImpl(const RenderNodeOp& op);
void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
return mFrameAllocatedPaths.back().get();
}
- void onStrokeableOp(const RecordedOp& op, batchid_t batchId,
+ void deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
/**
- * Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
+ * Declares all OpReorderer::deferXXXXOp() methods for every RecordedOp type.
*
* These private methods are called from within deferImpl to defer each individual op
* type differently.
*/
#define INTERNAL_OP_HANDLER(Type) \
- void on##Type(const Type& op);
+ void defer##Type(const Type& op);
MAP_OPS(INTERNAL_OP_HANDLER)
std::vector<std::unique_ptr<SkPath> > mFrameAllocatedPaths;
M_OP_FN(BitmapOp) \
U_OP_FN(BitmapMeshOp) \
U_OP_FN(BitmapRectOp) \
+ U_OP_FN(CirclePropsOp) \
U_OP_FN(LinesOp) \
U_OP_FN(OvalOp) \
M_OP_FN(PatchOp) \
U_OP_FN(RectOp) \
U_OP_FN(RenderNodeOp) \
U_OP_FN(RoundRectOp) \
+ U_OP_FN(RoundRectPropsOp) \
U_OP_FN(ShadowOp) \
U_OP_FN(SimpleRectsOp) \
M_OP_FN(TextOp) \
const Rect src;
};
+struct CirclePropsOp : RecordedOp {
+ CirclePropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint,
+ float* x, float* y, float* radius)
+ : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClipRect, paint)
+ , x(x)
+ , y(y)
+ , radius(radius) {}
+ const float* x;
+ const float* y;
+ const float* radius;
+};
+
struct LinesOp : RecordedOp {
LinesOp(BASE_PARAMS, const float* points, const int floatCount)
: SUPER(LinesOp)
: SUPER(OvalOp) {}
};
-
struct PatchOp : RecordedOp {
PatchOp(BASE_PARAMS, const SkBitmap* bitmap, const Res_png_9patch* patch)
: SUPER(PatchOp)
const float ry;
};
+struct RoundRectPropsOp : RecordedOp {
+ RoundRectPropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint,
+ float* left, float* top, float* right, float* bottom, float *rx, float *ry)
+ : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClipRect, paint)
+ , left(left)
+ , top(top)
+ , right(right)
+ , bottom(bottom)
+ , rx(rx)
+ , ry(ry) {}
+ const float* left;
+ const float* top;
+ const float* right;
+ const float* bottom;
+ const float* rx;
+ const float* ry;
+};
+
/**
* Real-time, dynamic-lit shadow.
*
refPaint(&paint), rx, ry));
}
+void RecordingCanvas::drawRoundRect(
+ CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+ CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+ CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+ CanvasPropertyPaint* paint) {
+ mDisplayList->ref(left);
+ mDisplayList->ref(top);
+ mDisplayList->ref(right);
+ mDisplayList->ref(bottom);
+ mDisplayList->ref(rx);
+ mDisplayList->ref(ry);
+ mDisplayList->ref(paint);
+ refBitmapsInShader(paint->value.getShader());
+ addOp(new (alloc()) RoundRectPropsOp(
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ &paint->value,
+ &left->value, &top->value, &right->value, &bottom->value,
+ &rx->value, &ry->value));
+}
+
void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
+ // TODO: move to Canvas.h
if (radius <= 0) return;
drawOval(x - radius, y - radius, x + radius, y + radius, paint);
}
+void RecordingCanvas::drawCircle(
+ CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+ CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
+ mDisplayList->ref(x);
+ mDisplayList->ref(y);
+ mDisplayList->ref(radius);
+ mDisplayList->ref(paint);
+ refBitmapsInShader(paint->value.getShader());
+ addOp(new (alloc()) CirclePropsOp(
+ *(mState.currentSnapshot()->transform),
+ mState.getRenderTargetClipBounds(),
+ &paint->value,
+ &x->value, &y->value, &radius->value));
+}
+
+
void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
addOp(new (alloc()) OvalOp(
Rect(left, top, right, bottom),
virtual GLuint getTargetFbo() const override { return -1; }
// ----------------------------------------------------------------------------
+// HWUI Canvas draw operations
+// ----------------------------------------------------------------------------
+
+ void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+ CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+ CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+ CanvasPropertyPaint* paint);
+ void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+ CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
+
+// ----------------------------------------------------------------------------
// android/graphics/Canvas interface
// ----------------------------------------------------------------------------
virtual SkCanvas* asSkCanvas() override;
--- /dev/null
+/*
+ * 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.
+ */
+
+#include "TestSceneBase.h"
+#include "utils/Color.h"
+
+class OpPropAnimation;
+
+static TestScene::Registrar _Shapes(TestScene::Info{
+ "opprops",
+ "A minimal demonstration of CanvasProperty drawing operations.",
+ TestScene::simpleCreateScene<OpPropAnimation>
+});
+
+class OpPropAnimation : public TestScene {
+public:
+ sp<CanvasPropertyPaint> mPaint = new CanvasPropertyPaint(SkPaint());
+
+ sp<CanvasPropertyPrimitive> mRoundRectLeft = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectTop = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectRight = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectBottom = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectRx = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mRoundRectRy = new CanvasPropertyPrimitive(0);
+
+ sp<CanvasPropertyPrimitive> mCircleX = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mCircleY = new CanvasPropertyPrimitive(0);
+ sp<CanvasPropertyPrimitive> mCircleRadius = new CanvasPropertyPrimitive(0);
+
+ sp<RenderNode> content;
+ void createContent(int width, int height, TestCanvas& canvas) override {
+ content = TestUtils::createNode(0, 0, width, height,
+ [this, width, height](RenderProperties& props, TestCanvas& canvas) {
+ mPaint->value.setAntiAlias(true);
+ mPaint->value.setColor(Color::Blue_500);
+
+ mRoundRectRight->value = width / 2;
+ mRoundRectBottom->value = height / 2;
+
+ mCircleX->value = width * 0.75;
+ mCircleY->value = height * 0.75;
+
+ canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+ canvas.drawRoundRect(mRoundRectLeft.get(), mRoundRectTop.get(),
+ mRoundRectRight.get(), mRoundRectBottom.get(),
+ mRoundRectRx.get(), mRoundRectRy.get(), mPaint.get());
+ canvas.drawCircle(mCircleX.get(), mCircleY.get(), mCircleRadius.get(), mPaint.get());
+ });
+ canvas.drawRenderNode(content.get());
+ }
+
+ void doFrame(int frameNr) override {
+ float value = (abs((frameNr % 200) - 100)) / 100.0f;
+ mRoundRectRx->value = dp(10) + value * dp(40);
+ mRoundRectRy->value = dp(10) + value * dp(80);
+ mCircleRadius->value = value * dp(200);
+ content->setPropertyFieldsDirty(RenderNode::GENERIC);
+ }
+};