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