RecordingCanvas.cpp revision aed7f58fb05a25ce2112829e77c0eb5dd268e8a7
1/*
2 * Copyright (C) 2015 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#include "RecordingCanvas.h"
18
19#include "DeferredLayerUpdater.h"
20#include "RecordedOp.h"
21#include "RenderNode.h"
22#include "VectorDrawable.h"
23#include "hwui/MinikinUtils.h"
24#include "hwui/Bitmap.h"
25
26namespace android {
27namespace uirenderer {
28
29RecordingCanvas::RecordingCanvas(size_t width, size_t height)
30        : mState(*this)
31        , mResourceCache(ResourceCache::getInstance()) {
32    resetRecording(width, height);
33}
34
35RecordingCanvas::~RecordingCanvas() {
36    LOG_ALWAYS_FATAL_IF(mDisplayList,
37            "Destroyed a RecordingCanvas during a record!");
38}
39
40void RecordingCanvas::resetRecording(int width, int height, RenderNode* node) {
41    LOG_ALWAYS_FATAL_IF(mDisplayList,
42            "prepareDirty called a second time during a recording!");
43    mDisplayList = new DisplayList();
44
45    mState.initializeRecordingSaveStack(width, height);
46
47    mDeferredBarrierType = DeferredBarrierType::InOrder;
48}
49
50DisplayList* RecordingCanvas::finishRecording() {
51    restoreToCount(1);
52    mPaintMap.clear();
53    mRegionMap.clear();
54    mPathMap.clear();
55    DisplayList* displayList = mDisplayList;
56    mDisplayList = nullptr;
57    mSkiaCanvasProxy.reset(nullptr);
58    return displayList;
59}
60
61void RecordingCanvas::insertReorderBarrier(bool enableReorder) {
62    if (enableReorder) {
63        mDeferredBarrierType = DeferredBarrierType::OutOfOrder;
64        mDeferredBarrierClip = getRecordedClip();
65    } else {
66        mDeferredBarrierType = DeferredBarrierType::InOrder;
67        mDeferredBarrierClip = nullptr;
68    }
69}
70
71SkCanvas* RecordingCanvas::asSkCanvas() {
72    LOG_ALWAYS_FATAL_IF(!mDisplayList,
73            "attempting to get an SkCanvas when we are not recording!");
74    if (!mSkiaCanvasProxy) {
75        mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
76    }
77
78    // SkCanvas instances default to identity transform, but should inherit
79    // the state of this Canvas; if this code was in the SkiaCanvasProxy
80    // constructor, we couldn't cache mSkiaCanvasProxy.
81    SkMatrix parentTransform;
82    getMatrix(&parentTransform);
83    mSkiaCanvasProxy.get()->setMatrix(parentTransform);
84
85    return mSkiaCanvasProxy.get();
86}
87
88// ----------------------------------------------------------------------------
89// CanvasStateClient implementation
90// ----------------------------------------------------------------------------
91
92void RecordingCanvas::onViewportInitialized() {
93}
94
95void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {
96    if (removed.flags & Snapshot::kFlagIsFboLayer) {
97        addOp(alloc().create_trivial<EndLayerOp>());
98    } else if (removed.flags & Snapshot::kFlagIsLayer) {
99        addOp(alloc().create_trivial<EndUnclippedLayerOp>());
100    }
101}
102
103// ----------------------------------------------------------------------------
104// android/graphics/Canvas state operations
105// ----------------------------------------------------------------------------
106// Save (layer)
107int RecordingCanvas::save(SaveFlags::Flags flags) {
108    return mState.save((int) flags);
109}
110
111void RecordingCanvas::RecordingCanvas::restore() {
112    mState.restore();
113}
114
115void RecordingCanvas::restoreToCount(int saveCount) {
116    mState.restoreToCount(saveCount);
117}
118
119int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
120        const SkPaint* paint, SaveFlags::Flags flags) {
121    // force matrix/clip isolation for layer
122    flags |= SaveFlags::MatrixClip;
123    bool clippedLayer = flags & SaveFlags::ClipToLayer;
124
125    const Snapshot& previous = *mState.currentSnapshot();
126
127    // initialize the snapshot as though it almost represents an FBO layer so deferred draw
128    // operations will be able to store and restore the current clip and transform info, and
129    // quick rejection will be correct (for display lists)
130
131    Rect unmappedBounds(left, top, right, bottom);
132    unmappedBounds.roundOut();
133
134    // determine clipped bounds relative to previous viewport.
135    Rect visibleBounds = unmappedBounds;
136    previous.transform->mapRect(visibleBounds);
137
138    if (CC_UNLIKELY(!clippedLayer
139            && previous.transform->rectToRect()
140            && visibleBounds.contains(previous.getRenderTargetClip()))) {
141        // unlikely case where an unclipped savelayer is recorded with a clip it can use,
142        // as none of its unaffected/unclipped area is visible
143        clippedLayer = true;
144        flags |= SaveFlags::ClipToLayer;
145    }
146
147    visibleBounds.doIntersect(previous.getRenderTargetClip());
148    visibleBounds.snapToPixelBoundaries();
149    visibleBounds.doIntersect(Rect(previous.getViewportWidth(), previous.getViewportHeight()));
150
151    // Map visible bounds back to layer space, and intersect with parameter bounds
152    Rect layerBounds = visibleBounds;
153    if (CC_LIKELY(!layerBounds.isEmpty())) {
154        // if non-empty, can safely map by the inverse transform
155        Matrix4 inverse;
156        inverse.loadInverse(*previous.transform);
157        inverse.mapRect(layerBounds);
158        layerBounds.doIntersect(unmappedBounds);
159    }
160
161    int saveValue = mState.save((int) flags);
162    Snapshot& snapshot = *mState.writableSnapshot();
163
164    // layerBounds is in original bounds space, but clipped by current recording clip
165    if (!layerBounds.isEmpty() && !unmappedBounds.isEmpty()) {
166        if (CC_LIKELY(clippedLayer)) {
167            auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed
168            if (addOp(alloc().create_trivial<BeginLayerOp>(
169                    unmappedBounds,
170                    *previous.transform, // transform to *draw* with
171                    previousClip, // clip to *draw* with
172                    refPaint(paint))) >= 0) {
173                snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
174                snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
175                snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
176
177                Rect clip = layerBounds;
178                clip.translate(-unmappedBounds.left, -unmappedBounds.top);
179                snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
180                snapshot.roundRectClipState = nullptr;
181                return saveValue;
182            }
183        } else {
184            if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
185                    unmappedBounds,
186                    *mState.currentSnapshot()->transform,
187                    getRecordedClip(),
188                    refPaint(paint))) >= 0) {
189                snapshot.flags |= Snapshot::kFlagIsLayer;
190                return saveValue;
191            }
192        }
193    }
194
195    // Layer not needed, so skip recording it...
196    if (CC_LIKELY(clippedLayer)) {
197        // ... and set empty clip to reject inner content, if possible
198        snapshot.resetClip(0, 0, 0, 0);
199    }
200    return saveValue;
201}
202
203// Matrix
204void RecordingCanvas::rotate(float degrees) {
205    if (degrees == 0) return;
206
207    mState.rotate(degrees);
208}
209
210void RecordingCanvas::scale(float sx, float sy) {
211    if (sx == 1 && sy == 1) return;
212
213    mState.scale(sx, sy);
214}
215
216void RecordingCanvas::skew(float sx, float sy) {
217    mState.skew(sx, sy);
218}
219
220void RecordingCanvas::translate(float dx, float dy) {
221    if (dx == 0 && dy == 0) return;
222
223    mState.translate(dx, dy, 0);
224}
225
226// Clip
227bool RecordingCanvas::getClipBounds(SkRect* outRect) const {
228    *outRect = mState.getLocalClipBounds().toSkRect();
229    return !(outRect->isEmpty());
230}
231bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
232    return mState.quickRejectConservative(left, top, right, bottom);
233}
234bool RecordingCanvas::quickRejectPath(const SkPath& path) const {
235    SkRect bounds = path.getBounds();
236    return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
237}
238bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
239    return mState.clipRect(left, top, right, bottom, op);
240}
241bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
242    return mState.clipPath(path, op);
243}
244bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
245    return mState.clipRegion(region, op);
246}
247
248// ----------------------------------------------------------------------------
249// android/graphics/Canvas draw operations
250// ----------------------------------------------------------------------------
251void RecordingCanvas::drawColor(int color, SkBlendMode mode) {
252    addOp(alloc().create_trivial<ColorOp>(
253            getRecordedClip(),
254            color,
255            mode));
256}
257
258void RecordingCanvas::drawPaint(const SkPaint& paint) {
259    SkRect bounds;
260    if (getClipBounds(&bounds)) {
261        drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint);
262    }
263}
264
265static Rect calcBoundsOfPoints(const float* points, int floatCount) {
266    Rect unmappedBounds(points[0], points[1], points[0], points[1]);
267    for (int i = 2; i < floatCount; i += 2) {
268        unmappedBounds.expandToCover(points[i], points[i + 1]);
269    }
270    return unmappedBounds;
271}
272
273// Geometry
274void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) {
275    if (CC_UNLIKELY(floatCount < 2 || PaintUtils::paintWillNotDraw(paint))) return;
276    floatCount &= ~0x1; // round down to nearest two
277
278    addOp(alloc().create_trivial<PointsOp>(
279            calcBoundsOfPoints(points, floatCount),
280            *mState.currentSnapshot()->transform,
281            getRecordedClip(),
282            refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
283}
284
285void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
286    if (CC_UNLIKELY(floatCount < 4 || PaintUtils::paintWillNotDraw(paint))) return;
287    floatCount &= ~0x3; // round down to nearest four
288
289    addOp(alloc().create_trivial<LinesOp>(
290            calcBoundsOfPoints(points, floatCount),
291            *mState.currentSnapshot()->transform,
292            getRecordedClip(),
293            refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
294}
295
296void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
297    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
298
299    addOp(alloc().create_trivial<RectOp>(
300            Rect(left, top, right, bottom),
301            *(mState.currentSnapshot()->transform),
302            getRecordedClip(),
303            refPaint(&paint)));
304}
305
306void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) {
307    if (rects == nullptr) return;
308
309    Vertex* rectData = (Vertex*) mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount);
310    Vertex* vertex = rectData;
311
312    float left = FLT_MAX;
313    float top = FLT_MAX;
314    float right = FLT_MIN;
315    float bottom = FLT_MIN;
316    for (int index = 0; index < vertexCount; index += 4) {
317        float l = rects[index + 0];
318        float t = rects[index + 1];
319        float r = rects[index + 2];
320        float b = rects[index + 3];
321
322        Vertex::set(vertex++, l, t);
323        Vertex::set(vertex++, r, t);
324        Vertex::set(vertex++, l, b);
325        Vertex::set(vertex++, r, b);
326
327        left = std::min(left, l);
328        top = std::min(top, t);
329        right = std::max(right, r);
330        bottom = std::max(bottom, b);
331    }
332    addOp(alloc().create_trivial<SimpleRectsOp>(
333            Rect(left, top, right, bottom),
334            *(mState.currentSnapshot()->transform),
335            getRecordedClip(),
336            refPaint(paint), rectData, vertexCount));
337}
338
339void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
340    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
341
342    if (paint.getStyle() == SkPaint::kFill_Style
343            && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) {
344        int count = 0;
345        Vector<float> rects;
346        SkRegion::Iterator it(region);
347        while (!it.done()) {
348            const SkIRect& r = it.rect();
349            rects.push(r.fLeft);
350            rects.push(r.fTop);
351            rects.push(r.fRight);
352            rects.push(r.fBottom);
353            count += 4;
354            it.next();
355        }
356        drawSimpleRects(rects.array(), count, &paint);
357    } else {
358        SkRegion::Iterator it(region);
359        while (!it.done()) {
360            const SkIRect& r = it.rect();
361            drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
362            it.next();
363        }
364    }
365}
366
367void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
368            float rx, float ry, const SkPaint& paint) {
369    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
370
371    if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) {
372        addOp(alloc().create_trivial<RoundRectOp>(
373                Rect(left, top, right, bottom),
374                *(mState.currentSnapshot()->transform),
375                getRecordedClip(),
376                refPaint(&paint), rx, ry));
377    } else {
378        drawRect(left, top, right, bottom, paint);
379    }
380}
381
382void RecordingCanvas::drawRoundRect(
383        CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
384        CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
385        CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
386        CanvasPropertyPaint* paint) {
387    mDisplayList->ref(left);
388    mDisplayList->ref(top);
389    mDisplayList->ref(right);
390    mDisplayList->ref(bottom);
391    mDisplayList->ref(rx);
392    mDisplayList->ref(ry);
393    mDisplayList->ref(paint);
394    refBitmapsInShader(paint->value.getShader());
395    addOp(alloc().create_trivial<RoundRectPropsOp>(
396            *(mState.currentSnapshot()->transform),
397            getRecordedClip(),
398            &paint->value,
399            &left->value, &top->value, &right->value, &bottom->value,
400            &rx->value, &ry->value));
401}
402
403void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
404    // TODO: move to Canvas.h
405    if (CC_UNLIKELY(radius <= 0 || PaintUtils::paintWillNotDraw(paint))) return;
406
407    drawOval(x - radius, y - radius, x + radius, y + radius, paint);
408}
409
410void RecordingCanvas::drawCircle(
411        CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
412        CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
413    mDisplayList->ref(x);
414    mDisplayList->ref(y);
415    mDisplayList->ref(radius);
416    mDisplayList->ref(paint);
417    refBitmapsInShader(paint->value.getShader());
418    addOp(alloc().create_trivial<CirclePropsOp>(
419            *(mState.currentSnapshot()->transform),
420            getRecordedClip(),
421            &paint->value,
422            &x->value, &y->value, &radius->value));
423}
424
425void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
426    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
427
428    addOp(alloc().create_trivial<OvalOp>(
429            Rect(left, top, right, bottom),
430            *(mState.currentSnapshot()->transform),
431            getRecordedClip(),
432            refPaint(&paint)));
433}
434
435void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
436        float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
437    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
438
439    if (fabs(sweepAngle) >= 360.0f) {
440        drawOval(left, top, right, bottom, paint);
441    } else {
442        addOp(alloc().create_trivial<ArcOp>(
443                Rect(left, top, right, bottom),
444                *(mState.currentSnapshot()->transform),
445                getRecordedClip(),
446                refPaint(&paint),
447                startAngle, sweepAngle, useCenter));
448    }
449}
450
451void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
452    if (CC_UNLIKELY(PaintUtils::paintWillNotDraw(paint))) return;
453
454    addOp(alloc().create_trivial<PathOp>(
455            Rect(path.getBounds()),
456            *(mState.currentSnapshot()->transform),
457            getRecordedClip(),
458            refPaint(&paint), refPath(&path)));
459}
460
461void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
462    mDisplayList->ref(tree);
463    mDisplayList->vectorDrawables.push_back(tree);
464    addOp(alloc().create_trivial<VectorDrawableOp>(
465            tree,
466            Rect(tree->stagingProperties()->getBounds()),
467            *(mState.currentSnapshot()->transform),
468            getRecordedClip()));
469}
470
471// Bitmap-based
472void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
473    SkBitmap skBitmap;
474    bitmap.getSkBitmap(&skBitmap);
475    save(SaveFlags::Matrix);
476    translate(left, top);
477    drawBitmap(&skBitmap, paint);
478    restore();
479}
480
481void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
482                            const SkPaint* paint) {
483    if (matrix.isIdentity()) {
484        drawBitmap(&bitmap, paint);
485    } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
486            && MathUtils::isPositive(matrix.getScaleX())
487            && MathUtils::isPositive(matrix.getScaleY())) {
488        // SkMatrix::isScaleTranslate() not available in L
489        SkRect src;
490        SkRect dst;
491        bitmap.getBounds(&src);
492        matrix.mapRect(&dst, src);
493        drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
494                   dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
495    } else {
496        save(SaveFlags::Matrix);
497        concat(matrix);
498        drawBitmap(&bitmap, paint);
499        restore();
500    }
501}
502
503void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
504            float srcRight, float srcBottom, float dstLeft, float dstTop,
505            float dstRight, float dstBottom, const SkPaint* paint) {
506    if (srcLeft == 0 && srcTop == 0
507            && srcRight == bitmap.width()
508            && srcBottom == bitmap.height()
509            && (srcBottom - srcTop == dstBottom - dstTop)
510            && (srcRight - srcLeft == dstRight - dstLeft)) {
511        // transform simple rect to rect drawing case into position bitmap ops, since they merge
512        save(SaveFlags::Matrix);
513        translate(dstLeft, dstTop);
514        drawBitmap(&bitmap, paint);
515        restore();
516    } else {
517        addOp(alloc().create_trivial<BitmapRectOp>(
518                Rect(dstLeft, dstTop, dstRight, dstBottom),
519                *(mState.currentSnapshot()->transform),
520                getRecordedClip(),
521                refPaint(paint), refBitmap(bitmap),
522                Rect(srcLeft, srcTop, srcRight, srcBottom)));
523    }
524}
525
526void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
527            const float* vertices, const int* colors, const SkPaint* paint) {
528    int vertexCount = (meshWidth + 1) * (meshHeight + 1);
529    addOp(alloc().create_trivial<BitmapMeshOp>(
530            calcBoundsOfPoints(vertices, vertexCount * 2),
531            *(mState.currentSnapshot()->transform),
532            getRecordedClip(),
533            refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight,
534            refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex
535            refBuffer<int>(colors, vertexCount))); // 1 color per vertex
536}
537
538void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
539            float dstLeft, float dstTop, float dstRight, float dstBottom,
540            const SkPaint* paint) {
541    addOp(alloc().create_trivial<PatchOp>(
542            Rect(dstLeft, dstTop, dstRight, dstBottom),
543            *(mState.currentSnapshot()->transform),
544            getRecordedClip(),
545            refPaint(paint), refBitmap(bitmap), refPatch(&patch)));
546}
547
548// Text
549void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
550            const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
551            float boundsRight, float boundsBottom, float totalAdvance) {
552    if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
553    glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
554    positions = refBuffer<float>(positions, glyphCount * 2);
555
556    // TODO: either must account for text shadow in bounds, or record separate ops for text shadows
557    addOp(alloc().create_trivial<TextOp>(
558            Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
559            *(mState.currentSnapshot()->transform),
560            getRecordedClip(),
561            refPaint(&paint), glyphs, positions, glyphCount, x, y));
562    drawTextDecorations(x, y, totalAdvance, paint);
563}
564
565void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
566        const SkPaint& paint, const SkPath& path, size_t start, size_t end) {
567    uint16_t glyphs[1];
568    for (size_t i = start; i < end; i++) {
569        glyphs[0] = layout.getGlyphId(i);
570        float x = hOffset + layout.getX(i);
571        float y = vOffset + layout.getY(i);
572        if (PaintUtils::paintWillNotDrawText(paint)) return;
573        const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1);
574        addOp(alloc().create_trivial<TextOnPathOp>(
575                *(mState.currentSnapshot()->transform),
576                getRecordedClip(),
577                refPaint(&paint), tempGlyphs, 1, refPath(&path), x, y));
578    }
579}
580
581void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
582    addOp(alloc().create_trivial<BitmapOp>(
583            Rect(bitmap->width(), bitmap->height()),
584            *(mState.currentSnapshot()->transform),
585            getRecordedClip(),
586            refPaint(paint), refBitmap(*bitmap)));
587}
588
589void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
590    auto&& stagingProps = renderNode->stagingProperties();
591    RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
592            Rect(stagingProps.getWidth(), stagingProps.getHeight()),
593            *(mState.currentSnapshot()->transform),
594            getRecordedClip(),
595            renderNode);
596    int opIndex = addOp(op);
597    if (CC_LIKELY(opIndex >= 0)) {
598        int childIndex = mDisplayList->addChild(op);
599
600        // update the chunk's child indices
601        DisplayList::Chunk& chunk = mDisplayList->chunks.back();
602        chunk.endChildIndex = childIndex + 1;
603
604        if (renderNode->stagingProperties().isProjectionReceiver()) {
605            // use staging property, since recording on UI thread
606            mDisplayList->projectionReceiveIndex = opIndex;
607        }
608    }
609}
610
611void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
612    // We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics.
613    mDisplayList->ref(layerHandle);
614
615    // Note that the backing layer has *not* yet been updated, so don't trust
616    // its width, height, transform, etc...!
617    addOp(alloc().create_trivial<TextureLayerOp>(
618            Rect(layerHandle->getWidth(), layerHandle->getHeight()),
619            *(mState.currentSnapshot()->transform),
620            getRecordedClip(),
621            layerHandle->backingLayer()));
622}
623
624void RecordingCanvas::callDrawGLFunction(Functor* functor,
625        GlFunctorLifecycleListener* listener) {
626    mDisplayList->functors.push_back({functor, listener});
627    mDisplayList->ref(listener);
628    addOp(alloc().create_trivial<FunctorOp>(
629            *(mState.currentSnapshot()->transform),
630            getRecordedClip(),
631            functor));
632}
633
634int RecordingCanvas::addOp(RecordedOp* op) {
635    // skip op with empty clip
636    if (op->localClip && op->localClip->rect.isEmpty()) {
637        // NOTE: this rejection happens after op construction/content ref-ing, so content ref'd
638        // and held by renderthread isn't affected by clip rejection.
639        // Could rewind alloc here if desired, but callers would have to not touch op afterwards.
640        return -1;
641    }
642
643    int insertIndex = mDisplayList->ops.size();
644    mDisplayList->ops.push_back(op);
645    if (mDeferredBarrierType != DeferredBarrierType::None) {
646        // op is first in new chunk
647        mDisplayList->chunks.emplace_back();
648        DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
649        newChunk.beginOpIndex = insertIndex;
650        newChunk.endOpIndex = insertIndex + 1;
651        newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
652        newChunk.reorderClip = mDeferredBarrierClip;
653
654        int nextChildIndex = mDisplayList->children.size();
655        newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
656        mDeferredBarrierType = DeferredBarrierType::None;
657    } else {
658        // standard case - append to existing chunk
659        mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
660    }
661    return insertIndex;
662}
663
664void RecordingCanvas::refBitmapsInShader(const SkShader* shader) {
665    if (!shader) return;
666
667    // If this paint has an SkShader that has an SkBitmap add
668    // it to the bitmap pile
669    SkBitmap bitmap;
670    SkShader::TileMode xy[2];
671    if (shader->isABitmap(&bitmap, nullptr, xy)) {
672        refBitmap(bitmap);
673        return;
674    }
675    SkShader::ComposeRec rec;
676    if (shader->asACompose(&rec)) {
677        refBitmapsInShader(rec.fShaderA);
678        refBitmapsInShader(rec.fShaderB);
679        return;
680    }
681}
682
683}; // namespace uirenderer
684}; // namespace android
685