/* * Copyright (C) 2018 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 "RecordingCanvas.h" #include #include #include #include #include #include #include "Mesh.h" #include "SkAndroidFrameworkUtils.h" #include "SkBlendMode.h" #include "SkCanvas.h" #include "SkCanvasPriv.h" #include "SkColor.h" #include "SkData.h" #include "SkDrawShadowInfo.h" #include "SkImage.h" #include "SkImageFilter.h" #include "SkImageInfo.h" #include "SkLatticeIter.h" #include "SkMesh.h" #include "SkPaint.h" #include "SkPicture.h" #include "SkRRect.h" #include "SkRSXform.h" #include "SkRect.h" #include "SkRegion.h" #include "SkTextBlob.h" #include "SkVertices.h" #include "Tonemapper.h" #include "VectorDrawable.h" #include "effects/GainmapRenderer.h" #include "include/gpu/GpuTypes.h" // from Skia #include "include/gpu/GrDirectContext.h" #include "include/gpu/ganesh/SkMeshGanesh.h" #include "pipeline/skia/AnimatedDrawables.h" #include "pipeline/skia/FunctorDrawable.h" #ifdef __ANDROID__ #include "renderthread/CanvasContext.h" #endif namespace android { namespace uirenderer { #ifndef SKLITEDL_PAGE #define SKLITEDL_PAGE 4096 #endif // A stand-in for an optional SkRect which was not set, e.g. bounds for a saveLayer(). static const SkRect kUnset = {SK_ScalarInfinity, 0, 0, 0}; static const SkRect* maybe_unset(const SkRect& r) { return r.left() == SK_ScalarInfinity ? nullptr : &r; } // copy_v(dst, src,n, src,n, ...) copies an arbitrary number of typed srcs into dst. static void copy_v(void* dst) {} template static void copy_v(void* dst, const S* src, int n, Rest&&... rest) { LOG_FATAL_IF(((uintptr_t)dst & (alignof(S) - 1)) != 0, "Expected %p to be aligned for at least %zu bytes.", dst, alignof(S)); // If n is 0, there is nothing to copy into dst from src. if (n > 0) { memcpy(dst, src, n * sizeof(S)); dst = reinterpret_cast( reinterpret_cast(dst) + n * sizeof(S)); } // Repeat for the next items, if any copy_v(dst, std::forward(rest)...); } // Helper for getting back at arrays which have been copy_v'd together after an Op. template static const D* pod(const T* op, size_t offset = 0) { return reinterpret_cast( reinterpret_cast(op + 1) + offset); } namespace { #define X(T) T, enum class Type : uint8_t { #include "DisplayListOps.in" }; #undef X struct Op { uint32_t type : 8; uint32_t skip : 24; }; static_assert(sizeof(Op) == 4, ""); struct Save final : Op { static const auto kType = Type::Save; void draw(SkCanvas* c, const SkMatrix&) const { c->save(); } }; struct Restore final : Op { static const auto kType = Type::Restore; void draw(SkCanvas* c, const SkMatrix&) const { c->restore(); } }; struct SaveLayer final : Op { static const auto kType = Type::SaveLayer; SaveLayer(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) { if (bounds) { this->bounds = *bounds; } if (paint) { this->paint = *paint; } this->backdrop = sk_ref_sp(backdrop); this->flags = flags; } SkRect bounds = kUnset; SkPaint paint; sk_sp backdrop; SkCanvas::SaveLayerFlags flags; void draw(SkCanvas* c, const SkMatrix&) const { c->saveLayer({maybe_unset(bounds), &paint, backdrop.get(), flags}); } }; struct SaveBehind final : Op { static const auto kType = Type::SaveBehind; SaveBehind(const SkRect* subset) { if (subset) { this->subset = *subset; } } SkRect subset = kUnset; void draw(SkCanvas* c, const SkMatrix&) const { SkAndroidFrameworkUtils::SaveBehind(c, &subset); } }; struct Concat final : Op { static const auto kType = Type::Concat; Concat(const SkM44& matrix) : matrix(matrix) {} SkM44 matrix; void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); } }; struct SetMatrix final : Op { static const auto kType = Type::SetMatrix; SetMatrix(const SkM44& matrix) : matrix(matrix) {} SkM44 matrix; void draw(SkCanvas* c, const SkMatrix& original) const { c->setMatrix(SkM44(original) * matrix); } }; struct Scale final : Op { static const auto kType = Type::Scale; Scale(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} SkScalar sx, sy; void draw(SkCanvas* c, const SkMatrix&) const { c->scale(sx, sy); } }; struct Translate final : Op { static const auto kType = Type::Translate; Translate(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {} SkScalar dx, dy; void draw(SkCanvas* c, const SkMatrix&) const { c->translate(dx, dy); } }; struct ClipPath final : Op { static const auto kType = Type::ClipPath; ClipPath(const SkPath& path, SkClipOp op, bool aa) : path(path), op(op), aa(aa) {} SkPath path; SkClipOp op; bool aa; void draw(SkCanvas* c, const SkMatrix&) const { c->clipPath(path, op, aa); } }; struct ClipRect final : Op { static const auto kType = Type::ClipRect; ClipRect(const SkRect& rect, SkClipOp op, bool aa) : rect(rect), op(op), aa(aa) {} SkRect rect; SkClipOp op; bool aa; void draw(SkCanvas* c, const SkMatrix&) const { c->clipRect(rect, op, aa); } }; struct ClipRRect final : Op { static const auto kType = Type::ClipRRect; ClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) : rrect(rrect), op(op), aa(aa) {} SkRRect rrect; SkClipOp op; bool aa; void draw(SkCanvas* c, const SkMatrix&) const { c->clipRRect(rrect, op, aa); } }; struct ClipRegion final : Op { static const auto kType = Type::ClipRegion; ClipRegion(const SkRegion& region, SkClipOp op) : region(region), op(op) {} SkRegion region; SkClipOp op; void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); } }; struct ClipShader final : Op { static const auto kType = Type::ClipShader; ClipShader(const sk_sp& shader, SkClipOp op) : shader(shader), op(op) {} sk_sp shader; SkClipOp op; void draw(SkCanvas* c, const SkMatrix&) const { c->clipShader(shader, op); } }; struct ResetClip final : Op { static const auto kType = Type::ResetClip; ResetClip() {} void draw(SkCanvas* c, const SkMatrix&) const { SkAndroidFrameworkUtils::ResetClip(c); } }; struct DrawPaint final : Op { static const auto kType = Type::DrawPaint; DrawPaint(const SkPaint& paint) : paint(paint) {} SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawPaint(paint); } }; struct DrawBehind final : Op { static const auto kType = Type::DrawBehind; DrawBehind(const SkPaint& paint) : paint(paint) {} SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { SkCanvasPriv::DrawBehind(c, paint); } }; struct DrawPath final : Op { static const auto kType = Type::DrawPath; DrawPath(const SkPath& path, const SkPaint& paint) : path(path), paint(paint) {} SkPath path; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawPath(path, paint); } }; struct DrawRect final : Op { static const auto kType = Type::DrawRect; DrawRect(const SkRect& rect, const SkPaint& paint) : rect(rect), paint(paint) {} SkRect rect; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawRect(rect, paint); } }; struct DrawRegion final : Op { static const auto kType = Type::DrawRegion; DrawRegion(const SkRegion& region, const SkPaint& paint) : region(region), paint(paint) {} SkRegion region; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawRegion(region, paint); } }; struct DrawOval final : Op { static const auto kType = Type::DrawOval; DrawOval(const SkRect& oval, const SkPaint& paint) : oval(oval), paint(paint) {} SkRect oval; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawOval(oval, paint); } }; struct DrawArc final : Op { static const auto kType = Type::DrawArc; DrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) : oval(oval) , startAngle(startAngle) , sweepAngle(sweepAngle) , useCenter(useCenter) , paint(paint) {} SkRect oval; SkScalar startAngle; SkScalar sweepAngle; bool useCenter; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawArc(oval, startAngle, sweepAngle, useCenter, paint); } }; struct DrawRRect final : Op { static const auto kType = Type::DrawRRect; DrawRRect(const SkRRect& rrect, const SkPaint& paint) : rrect(rrect), paint(paint) {} SkRRect rrect; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawRRect(rrect, paint); } }; struct DrawDRRect final : Op { static const auto kType = Type::DrawDRRect; DrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) : outer(outer), inner(inner), paint(paint) {} SkRRect outer, inner; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawDRRect(outer, inner, paint); } }; struct DrawAnnotation final : Op { static const auto kType = Type::DrawAnnotation; DrawAnnotation(const SkRect& rect, SkData* value) : rect(rect), value(sk_ref_sp(value)) {} SkRect rect; sk_sp value; void draw(SkCanvas* c, const SkMatrix&) const { c->drawAnnotation(rect, pod(this), value.get()); } }; struct DrawDrawable final : Op { static const auto kType = Type::DrawDrawable; DrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) : drawable(sk_ref_sp(drawable)) { if (matrix) { this->matrix = *matrix; } } sk_sp drawable; SkMatrix matrix = SkMatrix::I(); // It is important that we call drawable->draw(c) here instead of c->drawDrawable(drawable). // Drawables are mutable and in cases, like RenderNodeDrawable, are not expected to produce the // same content if retained outside the duration of the frame. Therefore we resolve // them now and do not allow the canvas to take a reference to the drawable and potentially // keep it alive for longer than the frames duration (e.g. SKP serialization). void draw(SkCanvas* c, const SkMatrix&) const { drawable->draw(c, &matrix); } }; struct DrawPicture final : Op { static const auto kType = Type::DrawPicture; DrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) : picture(sk_ref_sp(picture)) { if (matrix) { this->matrix = *matrix; } if (paint) { this->paint = *paint; has_paint = true; } } sk_sp picture; SkMatrix matrix = SkMatrix::I(); SkPaint paint; bool has_paint = false; // TODO: why is a default paint not the same? void draw(SkCanvas* c, const SkMatrix&) const { c->drawPicture(picture.get(), &matrix, has_paint ? &paint : nullptr); } }; struct DrawImage final : Op { static const auto kType = Type::DrawImage; DrawImage(DrawImagePayload&& payload, SkScalar x, SkScalar y, const SkSamplingOptions& sampling, const SkPaint* paint) : image(std::move(payload.image)) , x(x) , y(y) , sampling(sampling) , palette(payload.palette) , gainmap(std::move(payload.gainmapImage)) , gainmapInfo(payload.gainmapInfo) { if (paint) { this->paint = *paint; } } sk_sp image; SkScalar x, y; SkSamplingOptions sampling; SkPaint paint; BitmapPalette palette; sk_sp gainmap; SkGainmapInfo gainmapInfo; void draw(SkCanvas* c, const SkMatrix&) const { if (gainmap) { SkRect src = SkRect::MakeWH(image->width(), image->height()); SkRect dst = SkRect::MakeXYWH(x, y, src.width(), src.height()); DrawGainmapBitmap(c, image, src, dst, sampling, &paint, SkCanvas::kFast_SrcRectConstraint, gainmap, gainmapInfo); } else { SkPaint newPaint = paint; tonemapPaint(image->imageInfo(), c->imageInfo(), -1, newPaint); c->drawImage(image.get(), x, y, sampling, &newPaint); } } }; struct DrawImageRect final : Op { static const auto kType = Type::DrawImageRect; DrawImageRect(DrawImagePayload&& payload, const SkRect* src, const SkRect& dst, const SkSamplingOptions& sampling, const SkPaint* paint, SkCanvas::SrcRectConstraint constraint) : image(std::move(payload.image)) , dst(dst) , sampling(sampling) , constraint(constraint) , palette(payload.palette) , gainmap(std::move(payload.gainmapImage)) , gainmapInfo(payload.gainmapInfo) { this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height()); if (paint) { this->paint = *paint; } } sk_sp image; SkRect src, dst; SkSamplingOptions sampling; SkPaint paint; SkCanvas::SrcRectConstraint constraint; BitmapPalette palette; sk_sp gainmap; SkGainmapInfo gainmapInfo; void draw(SkCanvas* c, const SkMatrix&) const { if (gainmap) { DrawGainmapBitmap(c, image, src, dst, sampling, &paint, constraint, gainmap, gainmapInfo); } else { SkPaint newPaint = paint; tonemapPaint(image->imageInfo(), c->imageInfo(), -1, newPaint); c->drawImageRect(image.get(), src, dst, sampling, &newPaint, constraint); } } }; struct DrawImageLattice final : Op { static const auto kType = Type::DrawImageLattice; DrawImageLattice(DrawImagePayload&& payload, int xs, int ys, int fs, const SkIRect& src, const SkRect& dst, SkFilterMode filter, const SkPaint* paint) : image(std::move(payload.image)) , xs(xs) , ys(ys) , fs(fs) , src(src) , dst(dst) , filter(filter) , palette(payload.palette) { if (paint) { this->paint = *paint; } } sk_sp image; int xs, ys, fs; SkIRect src; SkRect dst; SkFilterMode filter; SkPaint paint; BitmapPalette palette; void draw(SkCanvas* c, const SkMatrix&) const { // TODO: Support drawing a gainmap 9-patch? auto xdivs = pod(this, 0), ydivs = pod(this, xs * sizeof(int)); auto colors = (0 == fs) ? nullptr : pod(this, (xs + ys) * sizeof(int)); auto flags = (0 == fs) ? nullptr : pod( this, (xs + ys) * sizeof(int) + fs * sizeof(SkColor)); SkPaint newPaint = paint; tonemapPaint(image->imageInfo(), c->imageInfo(), -1, newPaint); c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys, &src, colors}, dst, filter, &newPaint); } }; struct DrawTextBlob final : Op { static const auto kType = Type::DrawTextBlob; DrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint), drawTextBlobMode(gDrawTextBlobMode) {} sk_sp blob; SkScalar x, y; SkPaint paint; DrawTextBlobMode drawTextBlobMode; void draw(SkCanvas* c, const SkMatrix&) const { c->drawTextBlob(blob.get(), x, y, paint); } }; struct DrawPatch final : Op { static const auto kType = Type::DrawPatch; DrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texs[4], SkBlendMode bmode, const SkPaint& paint) : xfermode(bmode), paint(paint) { copy_v(this->cubics, cubics, 12); if (colors) { copy_v(this->colors, colors, 4); has_colors = true; } if (texs) { copy_v(this->texs, texs, 4); has_texs = true; } } SkPoint cubics[12]; SkColor colors[4]; SkPoint texs[4]; SkBlendMode xfermode; SkPaint paint; bool has_colors = false; bool has_texs = false; void draw(SkCanvas* c, const SkMatrix&) const { c->drawPatch(cubics, has_colors ? colors : nullptr, has_texs ? texs : nullptr, xfermode, paint); } }; struct DrawPoints final : Op { static const auto kType = Type::DrawPoints; DrawPoints(SkCanvas::PointMode mode, size_t count, const SkPaint& paint) : mode(mode), count(count), paint(paint) {} SkCanvas::PointMode mode; size_t count; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { if (paint.isAntiAlias()) { c->drawPoints(mode, count, pod(this), paint); } else { c->save(); #ifdef __ANDROID__ auto pixelSnap = renderthread::CanvasContext::getActiveContext()->getPixelSnapMatrix(); auto transform = c->getLocalToDevice(); transform.postConcat(pixelSnap); c->setMatrix(transform); #endif c->drawPoints(mode, count, pod(this), paint); c->restore(); } } }; struct DrawVertices final : Op { static const auto kType = Type::DrawVertices; DrawVertices(const SkVertices* v, SkBlendMode m, const SkPaint& p) : vertices(sk_ref_sp(const_cast(v))), mode(m), paint(p) {} sk_sp vertices; SkBlendMode mode; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawVertices(vertices, mode, paint); } }; struct DrawSkMesh final : Op { static const auto kType = Type::DrawSkMesh; DrawSkMesh(const SkMesh& mesh, sk_sp blender, const SkPaint& paint) : cpuMesh(mesh), blender(std::move(blender)), paint(paint) { isGpuBased = false; } const SkMesh& cpuMesh; mutable SkMesh gpuMesh; sk_sp blender; SkPaint paint; mutable bool isGpuBased; mutable GrDirectContext::DirectContextID contextId; void draw(SkCanvas* c, const SkMatrix&) const { #ifdef __ANDROID__ GrDirectContext* directContext = c->recordingContext()->asDirectContext(); GrDirectContext::DirectContextID id = directContext->directContextID(); if (!isGpuBased || contextId != id) { sk_sp vb = SkMeshes::CopyVertexBuffer(directContext, cpuMesh.refVertexBuffer()); if (!cpuMesh.indexBuffer()) { gpuMesh = SkMesh::Make(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(), cpuMesh.vertexOffset(), cpuMesh.refUniforms(), SkSpan(), cpuMesh.bounds()) .mesh; } else { sk_sp ib = SkMeshes::CopyIndexBuffer(directContext, cpuMesh.refIndexBuffer()); gpuMesh = SkMesh::MakeIndexed(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(), cpuMesh.vertexOffset(), ib, cpuMesh.indexCount(), cpuMesh.indexOffset(), cpuMesh.refUniforms(), SkSpan(), cpuMesh.bounds()) .mesh; } isGpuBased = true; contextId = id; } c->drawMesh(gpuMesh, blender, paint); #else c->drawMesh(cpuMesh, blender, paint); #endif } }; struct DrawMesh final : Op { static const auto kType = Type::DrawMesh; DrawMesh(const Mesh& mesh, sk_sp blender, const SkPaint& paint) : mesh(mesh.takeSnapshot()), blender(std::move(blender)), paint(paint) {} Mesh::Snapshot mesh; sk_sp blender; SkPaint paint; void draw(SkCanvas* c, const SkMatrix&) const { c->drawMesh(mesh.getSkMesh(), blender, paint); } }; struct DrawAtlas final : Op { static const auto kType = Type::DrawAtlas; DrawAtlas(const SkImage* atlas, int count, SkBlendMode mode, const SkSamplingOptions& sampling, const SkRect* cull, const SkPaint* paint, bool has_colors) : atlas(sk_ref_sp(atlas)), count(count), mode(mode), sampling(sampling) , has_colors(has_colors) { if (cull) { this->cull = *cull; } if (paint) { this->paint = *paint; } } sk_sp atlas; int count; SkBlendMode mode; SkSamplingOptions sampling; SkRect cull = kUnset; SkPaint paint; bool has_colors; void draw(SkCanvas* c, const SkMatrix&) const { auto xforms = pod(this, 0); auto texs = pod(this, count * sizeof(SkRSXform)); auto colors = has_colors ? pod(this, count * (sizeof(SkRSXform) + sizeof(SkRect))) : nullptr; c->drawAtlas(atlas.get(), xforms, texs, colors, count, mode, sampling, maybe_unset(cull), &paint); } }; struct DrawShadowRec final : Op { static const auto kType = Type::DrawShadowRec; DrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) : fPath(path), fRec(rec) {} SkPath fPath; SkDrawShadowRec fRec; void draw(SkCanvas* c, const SkMatrix&) const { c->private_draw_shadow_rec(fPath, fRec); } }; struct DrawVectorDrawable final : Op { static const auto kType = Type::DrawVectorDrawable; DrawVectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) , mBounds(tree->stagingProperties().getBounds()) , palette(tree->computePalette()) { // Recording, so use staging properties tree->getPaintFor(&paint, tree->stagingProperties()); } void draw(SkCanvas* canvas, const SkMatrix&) const { mRoot->draw(canvas, mBounds, paint); } sp mRoot; SkRect mBounds; Paint paint; BitmapPalette palette; }; struct DrawRippleDrawable final : Op { static const auto kType = Type::DrawRippleDrawable; DrawRippleDrawable(const skiapipeline::RippleDrawableParams& params) : mParams(params) {} void draw(SkCanvas* canvas, const SkMatrix&) const { skiapipeline::AnimatedRippleDrawable::draw(canvas, mParams); } skiapipeline::RippleDrawableParams mParams; }; struct DrawWebView final : Op { static const auto kType = Type::DrawWebView; DrawWebView(skiapipeline::FunctorDrawable* drawable) : drawable(sk_ref_sp(drawable)) {} sk_sp drawable; // We can't invoke SkDrawable::draw directly, because VkFunctorDrawable expects // SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw. // SkCanvas::drawDrawable/SkGpuDevice::drawDrawable has the logic to invoke // onSnapGpuDrawHandler. private: // Unfortunately WebView does not have complex clip information serialized, and we only perform // best-effort stencil fill for GLES. So for Vulkan we create an intermediate layer if the // canvas clip is complex. static bool needsCompositedLayer(SkCanvas* c) { if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) { return false; } SkRegion clipRegion; // WebView's rasterizer has access to simple clips, so for Vulkan we only need to check if // the clip is more complex than a rectangle. c->temporary_internal_getRgnClip(&clipRegion); return clipRegion.isComplex(); } mutable SkImageInfo mLayerImageInfo; mutable sk_sp mLayerSurface = nullptr; public: void draw(SkCanvas* c, const SkMatrix&) const { if (needsCompositedLayer(c)) { // What we do now is create an offscreen surface, sized by the clip bounds. // We won't apply a clip while drawing - clipping will be performed when compositing the // surface back onto the original canvas. Note also that we're not using saveLayer // because the webview functor still doesn't respect the canvas clip stack. const SkIRect deviceBounds = c->getDeviceClipBounds(); if (mLayerSurface == nullptr || c->imageInfo() != mLayerImageInfo) { mLayerImageInfo = c->imageInfo().makeWH(deviceBounds.width(), deviceBounds.height()); // SkCanvas::makeSurface returns a new surface that will be GPU-backed if // canvas was also. mLayerSurface = c->makeSurface(mLayerImageInfo); } SkCanvas* layerCanvas = mLayerSurface->getCanvas(); SkAutoCanvasRestore(layerCanvas, true); layerCanvas->clear(SK_ColorTRANSPARENT); // Preserve the transform from the original canvas, but now the clip rectangle is // anchored at the origin so we need to transform the clipped content to the origin. SkM44 mat4(c->getLocalToDevice()); mat4.postTranslate(-deviceBounds.fLeft, -deviceBounds.fTop); layerCanvas->concat(mat4); layerCanvas->drawDrawable(drawable.get()); SkAutoCanvasRestore acr(c, true); // Temporarily use an identity transform, because this is just blitting to the parent // canvas with an offset. SkMatrix invertedMatrix; if (!c->getTotalMatrix().invert(&invertedMatrix)) { ALOGW("Unable to extract invert canvas matrix; aborting VkFunctor draw"); return; } c->concat(invertedMatrix); mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop); } else { c->drawDrawable(drawable.get()); } } }; } static constexpr inline bool is_power_of_two(int value) { return (value & (value - 1)) == 0; } template constexpr bool doesPaintHaveFill(T& paint) { using T1 = std::remove_cv_t; if constexpr (std::is_same_v) { return paint.getStyle() != SkPaint::Style::kStroke_Style; } else if constexpr (std::is_same_v) { return paint.getStyle() != SkPaint::Style::kStroke_Style; } else if constexpr (std::is_same_v) { return paint && paint->getStyle() != SkPaint::Style::kStroke_Style; } else if constexpr (std::is_same_v) { return paint && paint->getStyle() != SkPaint::Style::kStroke_Style; } return false; } template constexpr bool hasPaintWithFill(Args&&... args) { return (... || doesPaintHaveFill(args)); } template void* DisplayListData::push(size_t pod, Args&&... args) { size_t skip = SkAlignPtr(sizeof(T) + pod); LOG_FATAL_IF(skip >= (1 << 24)); if (fUsed + skip > fReserved) { static_assert(is_power_of_two(SKLITEDL_PAGE), "This math needs updating for non-pow2."); // Next greater multiple of SKLITEDL_PAGE. fReserved = (fUsed + skip + SKLITEDL_PAGE) & ~(SKLITEDL_PAGE - 1); fBytes.realloc(fReserved); LOG_ALWAYS_FATAL_IF(fBytes.get() == nullptr, "realloc(%zd) failed", fReserved); } LOG_FATAL_IF((fUsed + skip) > fReserved); auto op = (T*)(fBytes.get() + fUsed); fUsed += skip; new (op) T{std::forward(args)...}; op->type = (uint32_t)T::kType; op->skip = skip; // check if this is a fill op or not, in case we need to avoid messing with it with force invert if constexpr (!std::is_same_v) { if (hasPaintWithFill(args...)) { mHasFill = true; } } return op + 1; } template inline void DisplayListData::map(const Fn fns[], Args... args) const { auto end = fBytes.get() + fUsed; for (const uint8_t* ptr = fBytes.get(); ptr < end;) { auto op = (const Op*)ptr; auto type = op->type; auto skip = op->skip; if (auto fn = fns[type]) { // We replace no-op functions with nullptrs fn(op, args...); // to avoid the overhead of a pointless call. } ptr += skip; } } void DisplayListData::save() { this->push(0); } void DisplayListData::restore() { this->push(0); } void DisplayListData::saveLayer(const SkRect* bounds, const SkPaint* paint, const SkImageFilter* backdrop, SkCanvas::SaveLayerFlags flags) { this->push(0, bounds, paint, backdrop, flags); } void DisplayListData::saveBehind(const SkRect* subset) { this->push(0, subset); } void DisplayListData::concat(const SkM44& m) { this->push(0, m); } void DisplayListData::setMatrix(const SkM44& matrix) { this->push(0, matrix); } void DisplayListData::scale(SkScalar sx, SkScalar sy) { this->push(0, sx, sy); } void DisplayListData::translate(SkScalar dx, SkScalar dy) { this->push(0, dx, dy); } void DisplayListData::clipPath(const SkPath& path, SkClipOp op, bool aa) { this->push(0, path, op, aa); } void DisplayListData::clipRect(const SkRect& rect, SkClipOp op, bool aa) { this->push(0, rect, op, aa); } void DisplayListData::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { this->push(0, rrect, op, aa); } void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) { this->push(0, region, op); } void DisplayListData::clipShader(const sk_sp& shader, SkClipOp op) { this->push(0, shader, op); } void DisplayListData::resetClip() { this->push(0); } void DisplayListData::drawPaint(const SkPaint& paint) { this->push(0, paint); } void DisplayListData::drawBehind(const SkPaint& paint) { this->push(0, paint); } void DisplayListData::drawPath(const SkPath& path, const SkPaint& paint) { this->push(0, path, paint); } void DisplayListData::drawRect(const SkRect& rect, const SkPaint& paint) { this->push(0, rect, paint); } void DisplayListData::drawRegion(const SkRegion& region, const SkPaint& paint) { this->push(0, region, paint); } void DisplayListData::drawOval(const SkRect& oval, const SkPaint& paint) { this->push(0, oval, paint); } void DisplayListData::drawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { this->push(0, oval, startAngle, sweepAngle, useCenter, paint); } void DisplayListData::drawRRect(const SkRRect& rrect, const SkPaint& paint) { this->push(0, rrect, paint); } void DisplayListData::drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { this->push(0, outer, inner, paint); } void DisplayListData::drawAnnotation(const SkRect& rect, const char* key, SkData* value) { size_t bytes = strlen(key) + 1; void* pod = this->push(bytes, rect, value); copy_v(pod, key, bytes); } void DisplayListData::drawDrawable(SkDrawable* drawable, const SkMatrix* matrix) { this->push(0, drawable, matrix); } void DisplayListData::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { this->push(0, picture, matrix, paint); } void DisplayListData::drawImage(DrawImagePayload&& payload, SkScalar x, SkScalar y, const SkSamplingOptions& sampling, const SkPaint* paint) { this->push(0, std::move(payload), x, y, sampling, paint); } void DisplayListData::drawImageRect(DrawImagePayload&& payload, const SkRect* src, const SkRect& dst, const SkSamplingOptions& sampling, const SkPaint* paint, SkCanvas::SrcRectConstraint constraint) { this->push(0, std::move(payload), src, dst, sampling, paint, constraint); } void DisplayListData::drawImageLattice(DrawImagePayload&& payload, const SkCanvas::Lattice& lattice, const SkRect& dst, SkFilterMode filter, const SkPaint* paint) { int xs = lattice.fXCount, ys = lattice.fYCount; int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0; size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::RectType) + fs * sizeof(SkColor); LOG_FATAL_IF(!lattice.fBounds); void* pod = this->push(bytes, std::move(payload), xs, ys, fs, *lattice.fBounds, dst, filter, paint); copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes, fs); } void DisplayListData::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { this->push(0, blob, x, y, paint); mHasText = true; } void DisplayListData::drawRippleDrawable(const skiapipeline::RippleDrawableParams& params) { this->push(0, params); } void DisplayListData::drawPatch(const SkPoint points[12], const SkColor colors[4], const SkPoint texs[4], SkBlendMode bmode, const SkPaint& paint) { this->push(0, points, colors, texs, bmode, paint); } void DisplayListData::drawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint points[], const SkPaint& paint) { void* pod = this->push(count * sizeof(SkPoint), mode, count, paint); copy_v(pod, points, count); } void DisplayListData::drawVertices(const SkVertices* vert, SkBlendMode mode, const SkPaint& paint) { this->push(0, vert, mode, paint); } void DisplayListData::drawMesh(const SkMesh& mesh, const sk_sp& blender, const SkPaint& paint) { this->push(0, mesh, blender, paint); } void DisplayListData::drawMesh(const Mesh& mesh, const sk_sp& blender, const SkPaint& paint) { this->push(0, mesh, blender, paint); } void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], const SkColor colors[], int count, SkBlendMode xfermode, const SkSamplingOptions& sampling, const SkRect* cull, const SkPaint* paint) { size_t bytes = count * (sizeof(SkRSXform) + sizeof(SkRect)); if (colors) { bytes += count * sizeof(SkColor); } void* pod = this->push(bytes, atlas, count, xfermode, sampling, cull, paint, colors != nullptr); copy_v(pod, xforms, count, texs, count, colors, colors ? count : 0); } void DisplayListData::drawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { this->push(0, path, rec); } void DisplayListData::drawVectorDrawable(VectorDrawableRoot* tree) { this->push(0, tree); } void DisplayListData::drawWebView(skiapipeline::FunctorDrawable* drawable) { this->push(0, drawable); } typedef void (*draw_fn)(const void*, SkCanvas*, const SkMatrix&); typedef void (*void_fn)(const void*); typedef void (*color_transform_fn)(const void*, ColorTransform); // All ops implement draw(). #define X(T) \ [](const void* op, SkCanvas* c, const SkMatrix& original) { \ ((const T*)op)->draw(c, original); \ }, static const draw_fn draw_fns[] = { #include "DisplayListOps.in" }; #undef X // Most state ops (matrix, clip, save, restore) have a trivial destructor. #define X(T) \ !std::is_trivially_destructible::value ? [](const void* op) { ((const T*)op)->~T(); } \ : (void_fn) nullptr, static const void_fn dtor_fns[] = { #include "DisplayListOps.in" }; #undef X void DisplayListData::draw(SkCanvas* canvas) const { SkAutoCanvasRestore acr(canvas, false); this->map(draw_fns, canvas, canvas->getTotalMatrix()); } DisplayListData::~DisplayListData() { this->reset(); } void DisplayListData::reset() { this->map(dtor_fns); // Leave fBytes and fReserved alone. fUsed = 0; } template using has_paint_helper = decltype(std::declval().paint); template constexpr bool has_paint = std::experimental::is_detected_v; template using has_palette_helper = decltype(std::declval().palette); template constexpr bool has_palette = std::experimental::is_detected_v; template constexpr color_transform_fn colorTransformForOp() { if constexpr(has_paint && has_palette) { // It's a bitmap return [](const void* opRaw, ColorTransform transform) { // TODO: We should be const. Or not. Or just use a different map // Unclear, but this is the quick fix const T* op = reinterpret_cast(opRaw); const SkPaint* paint = &op->paint; transformPaint(transform, const_cast(paint), op->palette); }; } else if constexpr(has_paint) { return [](const void* opRaw, ColorTransform transform) { // TODO: We should be const. Or not. Or just use a different map // Unclear, but this is the quick fix const T* op = reinterpret_cast(opRaw); const SkPaint* paint = &op->paint; transformPaint(transform, const_cast(paint)); }; } else { return nullptr; } } template<> constexpr color_transform_fn colorTransformForOp() { return [](const void *opRaw, ColorTransform transform) { const DrawTextBlob *op = reinterpret_cast(opRaw); switch (op->drawTextBlobMode) { case DrawTextBlobMode::HctOutline: const_cast(op->paint).setColor(SK_ColorBLACK); break; case DrawTextBlobMode::HctInner: const_cast(op->paint).setColor(SK_ColorWHITE); break; default: transformPaint(transform, const_cast(&(op->paint))); break; } }; } template <> constexpr color_transform_fn colorTransformForOp() { return [](const void* opRaw, ColorTransform transform) { const DrawRippleDrawable* op = reinterpret_cast(opRaw); // Ripple drawable needs to contrast against the background, so we need the inverse color. SkColor color = transformColorInverse(transform, op->mParams.color); const_cast(op)->mParams.color = color; }; } #define X(T) colorTransformForOp(), static const color_transform_fn color_transform_fns[] = { #include "DisplayListOps.in" }; #undef X void DisplayListData::applyColorTransform(ColorTransform transform) { this->map(color_transform_fns, transform); } RecordingCanvas::RecordingCanvas() : INHERITED(1, 1), fDL(nullptr) {} void RecordingCanvas::reset(DisplayListData* dl, const SkIRect& bounds) { this->resetCanvas(bounds.right(), bounds.bottom()); fDL = dl; mClipMayBeComplex = false; mSaveCount = mComplexSaveCount = 0; } sk_sp RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { return nullptr; } void RecordingCanvas::willSave() { mSaveCount++; fDL->save(); } SkCanvas::SaveLayerStrategy RecordingCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { fDL->saveLayer(rec.fBounds, rec.fPaint, rec.fBackdrop, rec.fSaveLayerFlags); return SkCanvas::kNoLayer_SaveLayerStrategy; } void RecordingCanvas::willRestore() { mSaveCount--; if (mSaveCount < mComplexSaveCount) { mClipMayBeComplex = false; mComplexSaveCount = 0; } fDL->restore(); } bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) { fDL->saveBehind(subset); return false; } void RecordingCanvas::didConcat44(const SkM44& m) { fDL->concat(m); } void RecordingCanvas::didSetM44(const SkM44& matrix) { fDL->setMatrix(matrix); } void RecordingCanvas::didScale(SkScalar sx, SkScalar sy) { fDL->scale(sx, sy); } void RecordingCanvas::didTranslate(SkScalar dx, SkScalar dy) { fDL->translate(dx, dy); } void RecordingCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle style) { fDL->clipRect(rect, op, style == kSoft_ClipEdgeStyle); if (!getTotalMatrix().isScaleTranslate()) { setClipMayBeComplex(); } this->INHERITED::onClipRect(rect, op, style); } void RecordingCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle style) { if (rrect.getType() > SkRRect::kRect_Type || !getTotalMatrix().isScaleTranslate()) { setClipMayBeComplex(); } fDL->clipRRect(rrect, op, style == kSoft_ClipEdgeStyle); this->INHERITED::onClipRRect(rrect, op, style); } void RecordingCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle style) { setClipMayBeComplex(); fDL->clipPath(path, op, style == kSoft_ClipEdgeStyle); this->INHERITED::onClipPath(path, op, style); } void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) { if (region.isComplex() || !getTotalMatrix().isScaleTranslate()) { setClipMayBeComplex(); } fDL->clipRegion(region, op); this->INHERITED::onClipRegion(region, op); } void RecordingCanvas::onClipShader(sk_sp shader, SkClipOp op) { setClipMayBeComplex(); fDL->clipShader(shader, op); this->INHERITED::onClipShader(shader, op); } void RecordingCanvas::onResetClip() { // This is part of "replace op" emulation, but rely on the following intersection // clip to potentially mark the clip as complex. If we are already complex, we do // not reset the complexity so that we don't break the contract that no higher // save point has a complex clip when "not complex". fDL->resetClip(); this->INHERITED::onResetClip(); } void RecordingCanvas::onDrawPaint(const SkPaint& paint) { fDL->drawPaint(paint); } void RecordingCanvas::onDrawBehind(const SkPaint& paint) { fDL->drawBehind(paint); } void RecordingCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { fDL->drawPath(path, paint); } void RecordingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { fDL->drawRect(rect, paint); } void RecordingCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { fDL->drawRegion(region, paint); } void RecordingCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { fDL->drawOval(oval, paint); } void RecordingCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, bool useCenter, const SkPaint& paint) { fDL->drawArc(oval, startAngle, sweepAngle, useCenter, paint); } void RecordingCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { fDL->drawRRect(rrect, paint); } void RecordingCanvas::onDrawDRRect(const SkRRect& out, const SkRRect& in, const SkPaint& paint) { fDL->drawDRRect(out, in, paint); } void RecordingCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) { fDL->drawDrawable(drawable, matrix); } void RecordingCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { fDL->drawPicture(picture, matrix, paint); } void RecordingCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* val) { fDL->drawAnnotation(rect, key, val); } void RecordingCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) { fDL->drawTextBlob(blob, x, y, paint); } void RecordingCanvas::drawRippleDrawable(const skiapipeline::RippleDrawableParams& params) { fDL->drawRippleDrawable(params); } void RecordingCanvas::drawImage(DrawImagePayload&& payload, SkScalar x, SkScalar y, const SkSamplingOptions& sampling, const SkPaint* paint) { fDL->drawImage(std::move(payload), x, y, sampling, paint); } void RecordingCanvas::drawImageRect(DrawImagePayload&& payload, const SkRect& src, const SkRect& dst, const SkSamplingOptions& sampling, const SkPaint* paint, SrcRectConstraint constraint) { fDL->drawImageRect(std::move(payload), &src, dst, sampling, paint, constraint); } void RecordingCanvas::drawImageLattice(DrawImagePayload&& payload, const Lattice& lattice, const SkRect& dst, SkFilterMode filter, const SkPaint* paint) { if (!payload.image || dst.isEmpty()) { return; } SkIRect bounds; Lattice latticePlusBounds = lattice; if (!latticePlusBounds.fBounds) { bounds = SkIRect::MakeWH(payload.image->width(), payload.image->height()); latticePlusBounds.fBounds = &bounds; } if (SkLatticeIter::Valid(payload.image->width(), payload.image->height(), latticePlusBounds)) { fDL->drawImageLattice(std::move(payload), latticePlusBounds, dst, filter, paint); } else { SkSamplingOptions sampling(filter, SkMipmapMode::kNone); fDL->drawImageRect(std::move(payload), nullptr, dst, sampling, paint, kFast_SrcRectConstraint); } } void RecordingCanvas::onDrawImage2(const SkImage* img, SkScalar x, SkScalar y, const SkSamplingOptions& sampling, const SkPaint* paint) { fDL->drawImage(DrawImagePayload(img), x, y, sampling, paint); } void RecordingCanvas::onDrawImageRect2(const SkImage* img, const SkRect& src, const SkRect& dst, const SkSamplingOptions& sampling, const SkPaint* paint, SrcRectConstraint constraint) { fDL->drawImageRect(DrawImagePayload(img), &src, dst, sampling, paint, constraint); } void RecordingCanvas::onDrawImageLattice2(const SkImage* img, const SkCanvas::Lattice& lattice, const SkRect& dst, SkFilterMode filter, const SkPaint* paint) { fDL->drawImageLattice(DrawImagePayload(img), lattice, dst, filter, paint); } void RecordingCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) { fDL->drawPatch(cubics, colors, texCoords, bmode, paint); } void RecordingCanvas::onDrawPoints(SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { fDL->drawPoints(mode, count, pts, paint); } void RecordingCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { fDL->drawVertices(vertices, mode, paint); } void RecordingCanvas::onDrawMesh(const SkMesh& mesh, sk_sp blender, const SkPaint& paint) { fDL->drawMesh(mesh, blender, paint); } void RecordingCanvas::drawMesh(const Mesh& mesh, sk_sp blender, const SkPaint& paint) { fDL->drawMesh(mesh, blender, paint); } void RecordingCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], const SkColor colors[], int count, SkBlendMode bmode, const SkSamplingOptions& sampling, const SkRect* cull, const SkPaint* paint) { fDL->drawAtlas(atlas, xforms, texs, colors, count, bmode, sampling, cull, paint); } void RecordingCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { fDL->drawShadowRec(path, rec); } void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { fDL->drawVectorDrawable(tree); } void RecordingCanvas::drawWebView(skiapipeline::FunctorDrawable* drawable) { fDL->drawWebView(drawable); } } // namespace uirenderer } // namespace android