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