RecordingCanvas.cpp revision 2dbb4c46ee648c64bb977b6839374d73b5a605d9
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#define MIL_PIX 1000000
28static Rect sUnreasonablyLargeBounds(-MIL_PIX, -MIL_PIX, MIL_PIX, MIL_PIX);
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    if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) {
360        addOp(alloc().create_trivial<RoundRectOp>(
361                Rect(left, top, right, bottom),
362                *(mState.currentSnapshot()->transform),
363                getRecordedClip(),
364                refPaint(&paint), rx, ry));
365    } else {
366        drawRect(left, top, right, bottom, paint);
367    }
368}
369
370void RecordingCanvas::drawRoundRect(
371        CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
372        CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
373        CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
374        CanvasPropertyPaint* paint) {
375    mDisplayList->ref(left);
376    mDisplayList->ref(top);
377    mDisplayList->ref(right);
378    mDisplayList->ref(bottom);
379    mDisplayList->ref(rx);
380    mDisplayList->ref(ry);
381    mDisplayList->ref(paint);
382    refBitmapsInShader(paint->value.getShader());
383    addOp(alloc().create_trivial<RoundRectPropsOp>(
384            *(mState.currentSnapshot()->transform),
385            getRecordedClip(),
386            &paint->value,
387            &left->value, &top->value, &right->value, &bottom->value,
388            &rx->value, &ry->value));
389}
390
391void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
392    // TODO: move to Canvas.h
393    if (radius <= 0) return;
394    drawOval(x - radius, y - radius, x + radius, y + radius, paint);
395}
396
397void RecordingCanvas::drawCircle(
398        CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
399        CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
400    mDisplayList->ref(x);
401    mDisplayList->ref(y);
402    mDisplayList->ref(radius);
403    mDisplayList->ref(paint);
404    refBitmapsInShader(paint->value.getShader());
405    addOp(alloc().create_trivial<CirclePropsOp>(
406            *(mState.currentSnapshot()->transform),
407            getRecordedClip(),
408            &paint->value,
409            &x->value, &y->value, &radius->value));
410}
411
412void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
413    addOp(alloc().create_trivial<OvalOp>(
414            Rect(left, top, right, bottom),
415            *(mState.currentSnapshot()->transform),
416            getRecordedClip(),
417            refPaint(&paint)));
418}
419
420void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
421        float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
422    if (fabs(sweepAngle) >= 360.0f) {
423        drawOval(left, top, right, bottom, paint);
424    } else {
425        addOp(alloc().create_trivial<ArcOp>(
426                Rect(left, top, right, bottom),
427                *(mState.currentSnapshot()->transform),
428                getRecordedClip(),
429                refPaint(&paint),
430                startAngle, sweepAngle, useCenter));
431    }
432}
433
434void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
435    addOp(alloc().create_trivial<PathOp>(
436            Rect(path.getBounds()),
437            *(mState.currentSnapshot()->transform),
438            getRecordedClip(),
439            refPaint(&paint), refPath(&path)));
440}
441
442void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
443    mDisplayList->ref(tree);
444    addOp(alloc().create_trivial<VectorDrawableOp>(
445            tree,
446            Rect(tree->getBounds()),
447            *(mState.currentSnapshot()->transform),
448            getRecordedClip()));
449}
450
451// Bitmap-based
452void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
453    save(SaveFlags::Matrix);
454    translate(left, top);
455    drawBitmap(&bitmap, paint);
456    restore();
457}
458
459void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
460                            const SkPaint* paint) {
461    if (matrix.isIdentity()) {
462        drawBitmap(&bitmap, paint);
463    } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
464            && MathUtils::isPositive(matrix.getScaleX())
465            && MathUtils::isPositive(matrix.getScaleY())) {
466        // SkMatrix::isScaleTranslate() not available in L
467        SkRect src;
468        SkRect dst;
469        bitmap.getBounds(&src);
470        matrix.mapRect(&dst, src);
471        drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
472                   dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
473    } else {
474        save(SaveFlags::Matrix);
475        concat(matrix);
476        drawBitmap(&bitmap, paint);
477        restore();
478    }
479}
480
481void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
482            float srcRight, float srcBottom, float dstLeft, float dstTop,
483            float dstRight, float dstBottom, const SkPaint* paint) {
484    if (srcLeft == 0 && srcTop == 0
485            && srcRight == bitmap.width()
486            && srcBottom == bitmap.height()
487            && (srcBottom - srcTop == dstBottom - dstTop)
488            && (srcRight - srcLeft == dstRight - dstLeft)) {
489        // transform simple rect to rect drawing case into position bitmap ops, since they merge
490        save(SaveFlags::Matrix);
491        translate(dstLeft, dstTop);
492        drawBitmap(&bitmap, paint);
493        restore();
494    } else {
495        addOp(alloc().create_trivial<BitmapRectOp>(
496                Rect(dstLeft, dstTop, dstRight, dstBottom),
497                *(mState.currentSnapshot()->transform),
498                getRecordedClip(),
499                refPaint(paint), refBitmap(bitmap),
500                Rect(srcLeft, srcTop, srcRight, srcBottom)));
501    }
502}
503
504void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
505            const float* vertices, const int* colors, const SkPaint* paint) {
506    int vertexCount = (meshWidth + 1) * (meshHeight + 1);
507    addOp(alloc().create_trivial<BitmapMeshOp>(
508            calcBoundsOfPoints(vertices, vertexCount * 2),
509            *(mState.currentSnapshot()->transform),
510            getRecordedClip(),
511            refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight,
512            refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex
513            refBuffer<int>(colors, vertexCount))); // 1 color per vertex
514}
515
516void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
517            float dstLeft, float dstTop, float dstRight, float dstBottom,
518            const SkPaint* paint) {
519    addOp(alloc().create_trivial<PatchOp>(
520            Rect(dstLeft, dstTop, dstRight, dstBottom),
521            *(mState.currentSnapshot()->transform),
522            getRecordedClip(),
523            refPaint(paint), refBitmap(bitmap), refPatch(&patch)));
524}
525
526// Text
527void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int glyphCount,
528            const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
529            float boundsRight, float boundsBottom, float totalAdvance) {
530    if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
531    glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
532    positions = refBuffer<float>(positions, glyphCount * 2);
533
534    // TODO: either must account for text shadow in bounds, or record separate ops for text shadows
535    addOp(alloc().create_trivial<TextOp>(
536            Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
537            *(mState.currentSnapshot()->transform),
538            getRecordedClip(),
539            refPaint(&paint), glyphs, positions, glyphCount, x, y));
540    drawTextDecorations(x, y, totalAdvance, paint);
541}
542
543void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
544            float hOffset, float vOffset, const SkPaint& paint) {
545    if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
546    glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
547    auto clip = getRecordedClip();
548    addOp(alloc().create_trivial<TextOnPathOp>(
549            getConservativeOpBounds(clip), // TODO: explicitly define bounds
550            *(mState.currentSnapshot()->transform),
551            clip,
552            refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset));
553}
554
555void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
556    addOp(alloc().create_trivial<BitmapOp>(
557            Rect(bitmap->width(), bitmap->height()),
558            *(mState.currentSnapshot()->transform),
559            getRecordedClip(),
560            refPaint(paint), refBitmap(*bitmap)));
561}
562
563void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
564    auto&& stagingProps = renderNode->stagingProperties();
565    RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
566            Rect(stagingProps.getWidth(), stagingProps.getHeight()),
567            *(mState.currentSnapshot()->transform),
568            getRecordedClip(),
569            renderNode);
570    int opIndex = addOp(op);
571    if (CC_LIKELY(opIndex >= 0)) {
572        int childIndex = mDisplayList->addChild(op);
573
574        // update the chunk's child indices
575        DisplayList::Chunk& chunk = mDisplayList->chunks.back();
576        chunk.endChildIndex = childIndex + 1;
577
578        if (renderNode->stagingProperties().isProjectionReceiver()) {
579            // use staging property, since recording on UI thread
580            mDisplayList->projectionReceiveIndex = opIndex;
581        }
582    }
583}
584
585void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
586    // We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics.
587    mDisplayList->ref(layerHandle);
588
589    Layer* layer = layerHandle->backingLayer();
590    Matrix4 totalTransform(*(mState.currentSnapshot()->transform));
591    totalTransform.multiply(layer->getTransform());
592
593    addOp(alloc().create_trivial<TextureLayerOp>(
594            Rect(layer->getWidth(), layer->getHeight()),
595            totalTransform,
596            getRecordedClip(),
597            layer));
598}
599
600void RecordingCanvas::callDrawGLFunction(Functor* functor) {
601    mDisplayList->functors.push_back(functor);
602    auto clip = getRecordedClip();
603    addOp(alloc().create_trivial<FunctorOp>(
604            getConservativeOpBounds(clip),
605            *(mState.currentSnapshot()->transform),
606            clip,
607            functor));
608}
609
610size_t RecordingCanvas::addOp(RecordedOp* op) {
611    // skip op with empty clip
612    if (op->localClip && op->localClip->rect.isEmpty()) {
613        // NOTE: this rejection happens after op construction/content ref-ing, so content ref'd
614        // and held by renderthread isn't affected by clip rejection.
615        // Could rewind alloc here if desired, but callers would have to not touch op afterwards.
616        return -1;
617    }
618
619    int insertIndex = mDisplayList->ops.size();
620    mDisplayList->ops.push_back(op);
621    if (mDeferredBarrierType != DeferredBarrierType::None) {
622        // op is first in new chunk
623        mDisplayList->chunks.emplace_back();
624        DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
625        newChunk.beginOpIndex = insertIndex;
626        newChunk.endOpIndex = insertIndex + 1;
627        newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
628
629        int nextChildIndex = mDisplayList->children.size();
630        newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
631        mDeferredBarrierType = DeferredBarrierType::None;
632    } else {
633        // standard case - append to existing chunk
634        mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
635    }
636    return insertIndex;
637}
638
639void RecordingCanvas::refBitmapsInShader(const SkShader* shader) {
640    if (!shader) return;
641
642    // If this paint has an SkShader that has an SkBitmap add
643    // it to the bitmap pile
644    SkBitmap bitmap;
645    SkShader::TileMode xy[2];
646    if (shader->isABitmap(&bitmap, nullptr, xy)) {
647        refBitmap(bitmap);
648        return;
649    }
650    SkShader::ComposeRec rec;
651    if (shader->asACompose(&rec)) {
652        refBitmapsInShader(rec.fShaderA);
653        refBitmapsInShader(rec.fShaderB);
654        return;
655    }
656}
657
658}; // namespace uirenderer
659}; // namespace android
660