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 #include "hwui/MinikinUtils.h"
     24 
     25 namespace android {
     26 namespace uirenderer {
     27 
     28 RecordingCanvas::RecordingCanvas(size_t width, size_t height)
     29         : mState(*this)
     30         , mResourceCache(ResourceCache::getInstance()) {
     31     resetRecording(width, height);
     32 }
     33 
     34 RecordingCanvas::~RecordingCanvas() {
     35     LOG_ALWAYS_FATAL_IF(mDisplayList,
     36             "Destroyed a RecordingCanvas during a record!");
     37 }
     38 
     39 void RecordingCanvas::resetRecording(int width, int height, RenderNode* node) {
     40     LOG_ALWAYS_FATAL_IF(mDisplayList,
     41             "prepareDirty called a second time during a recording!");
     42     mDisplayList = new DisplayList();
     43 
     44     mState.initializeRecordingSaveStack(width, height);
     45 
     46     mDeferredBarrierType = DeferredBarrierType::InOrder;
     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, SkClipOp op) {
    238     return mState.clipRect(left, top, right, bottom, op);
    239 }
    240 bool RecordingCanvas::clipPath(const SkPath* path, SkClipOp op) {
    241     return mState.clipPath(path, op);
    242 }
    243 
    244 // ----------------------------------------------------------------------------
    245 // android/graphics/Canvas draw operations
    246 // ----------------------------------------------------------------------------
    247 void RecordingCanvas::drawColor(int color, SkBlendMode mode) {
    248     addOp(alloc().create_trivial<ColorOp>(
    249             getRecordedClip(),
    250             color,
    251             mode));
    252 }
    253 
    254 void RecordingCanvas::drawPaint(const SkPaint& paint) {
    255     SkRect bounds;
    256     if (getClipBounds(&bounds)) {
    257         drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint);
    258     }
    259 }
    260 
    261 static Rect calcBoundsOfPoints(const float* points, int floatCount) {
    262     Rect unmappedBounds(points[0], points[1], points[0], points[1]);
    263     for (int i = 2; i < floatCount; i += 2) {
    264         unmappedBounds.expandToCover(points[i], points[i + 1]);
    265     }
    266     return unmappedBounds;
    267 }
    268 
    269 // Geometry
    270 void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) {
    271     if (CC_UNLIKELY(floatCount < 2 || paint.nothingToDraw())) return;
    272     floatCount &= ~0x1; // round down to nearest two
    273 
    274     addOp(alloc().create_trivial<PointsOp>(
    275             calcBoundsOfPoints(points, floatCount),
    276             *mState.currentSnapshot()->transform,
    277             getRecordedClip(),
    278             refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
    279 }
    280 
    281 void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
    282     if (CC_UNLIKELY(floatCount < 4 || paint.nothingToDraw())) return;
    283     floatCount &= ~0x3; // round down to nearest four
    284 
    285     addOp(alloc().create_trivial<LinesOp>(
    286             calcBoundsOfPoints(points, floatCount),
    287             *mState.currentSnapshot()->transform,
    288             getRecordedClip(),
    289             refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
    290 }
    291 
    292 void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) {
    293     if (CC_UNLIKELY(paint.nothingToDraw())) return;
    294 
    295     addOp(alloc().create_trivial<RectOp>(
    296             Rect(left, top, right, bottom),
    297             *(mState.currentSnapshot()->transform),
    298             getRecordedClip(),
    299             refPaint(&paint)));
    300 }
    301 
    302 void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) {
    303     if (rects == nullptr) return;
    304 
    305     Vertex* rectData = (Vertex*) mDisplayList->allocator.create_trivial_array<Vertex>(vertexCount);
    306     Vertex* vertex = rectData;
    307 
    308     float left = FLT_MAX;
    309     float top = FLT_MAX;
    310     float right = FLT_MIN;
    311     float bottom = FLT_MIN;
    312     for (int index = 0; index < vertexCount; index += 4) {
    313         float l = rects[index + 0];
    314         float t = rects[index + 1];
    315         float r = rects[index + 2];
    316         float b = rects[index + 3];
    317 
    318         Vertex::set(vertex++, l, t);
    319         Vertex::set(vertex++, r, t);
    320         Vertex::set(vertex++, l, b);
    321         Vertex::set(vertex++, r, b);
    322 
    323         left = std::min(left, l);
    324         top = std::min(top, t);
    325         right = std::max(right, r);
    326         bottom = std::max(bottom, b);
    327     }
    328     addOp(alloc().create_trivial<SimpleRectsOp>(
    329             Rect(left, top, right, bottom),
    330             *(mState.currentSnapshot()->transform),
    331             getRecordedClip(),
    332             refPaint(paint), rectData, vertexCount));
    333 }
    334 
    335 void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
    336     if (CC_UNLIKELY(paint.nothingToDraw())) return;
    337 
    338     if (paint.getStyle() == SkPaint::kFill_Style
    339             && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) {
    340         int count = 0;
    341         Vector<float> rects;
    342         SkRegion::Iterator it(region);
    343         while (!it.done()) {
    344             const SkIRect& r = it.rect();
    345             rects.push(r.fLeft);
    346             rects.push(r.fTop);
    347             rects.push(r.fRight);
    348             rects.push(r.fBottom);
    349             count += 4;
    350             it.next();
    351         }
    352         drawSimpleRects(rects.array(), count, &paint);
    353     } else {
    354         SkRegion::Iterator it(region);
    355         while (!it.done()) {
    356             const SkIRect& r = it.rect();
    357             drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
    358             it.next();
    359         }
    360     }
    361 }
    362 
    363 void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
    364             float rx, float ry, const SkPaint& paint) {
    365     if (CC_UNLIKELY(paint.nothingToDraw())) return;
    366 
    367     if (CC_LIKELY(MathUtils::isPositive(rx) || MathUtils::isPositive(ry))) {
    368         addOp(alloc().create_trivial<RoundRectOp>(
    369                 Rect(left, top, right, bottom),
    370                 *(mState.currentSnapshot()->transform),
    371                 getRecordedClip(),
    372                 refPaint(&paint), rx, ry));
    373     } else {
    374         drawRect(left, top, right, bottom, paint);
    375     }
    376 }
    377 
    378 void RecordingCanvas::drawRoundRect(
    379         CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
    380         CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
    381         CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
    382         CanvasPropertyPaint* paint) {
    383     mDisplayList->ref(left);
    384     mDisplayList->ref(top);
    385     mDisplayList->ref(right);
    386     mDisplayList->ref(bottom);
    387     mDisplayList->ref(rx);
    388     mDisplayList->ref(ry);
    389     mDisplayList->ref(paint);
    390     refBitmapsInShader(paint->value.getShader());
    391     addOp(alloc().create_trivial<RoundRectPropsOp>(
    392             *(mState.currentSnapshot()->transform),
    393             getRecordedClip(),
    394             &paint->value,
    395             &left->value, &top->value, &right->value, &bottom->value,
    396             &rx->value, &ry->value));
    397 }
    398 
    399 void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
    400     // TODO: move to Canvas.h
    401     if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
    402 
    403     drawOval(x - radius, y - radius, x + radius, y + radius, paint);
    404 }
    405 
    406 void RecordingCanvas::drawCircle(
    407         CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
    408         CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
    409     mDisplayList->ref(x);
    410     mDisplayList->ref(y);
    411     mDisplayList->ref(radius);
    412     mDisplayList->ref(paint);
    413     refBitmapsInShader(paint->value.getShader());
    414     addOp(alloc().create_trivial<CirclePropsOp>(
    415             *(mState.currentSnapshot()->transform),
    416             getRecordedClip(),
    417             &paint->value,
    418             &x->value, &y->value, &radius->value));
    419 }
    420 
    421 void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
    422     if (CC_UNLIKELY(paint.nothingToDraw())) return;
    423 
    424     addOp(alloc().create_trivial<OvalOp>(
    425             Rect(left, top, right, bottom),
    426             *(mState.currentSnapshot()->transform),
    427             getRecordedClip(),
    428             refPaint(&paint)));
    429 }
    430 
    431 void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
    432         float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
    433     if (CC_UNLIKELY(paint.nothingToDraw())) return;
    434 
    435     if (fabs(sweepAngle) >= 360.0f) {
    436         drawOval(left, top, right, bottom, paint);
    437     } else {
    438         addOp(alloc().create_trivial<ArcOp>(
    439                 Rect(left, top, right, bottom),
    440                 *(mState.currentSnapshot()->transform),
    441                 getRecordedClip(),
    442                 refPaint(&paint),
    443                 startAngle, sweepAngle, useCenter));
    444     }
    445 }
    446 
    447 void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
    448     if (CC_UNLIKELY(paint.nothingToDraw())) return;
    449 
    450     addOp(alloc().create_trivial<PathOp>(
    451             Rect(path.getBounds()),
    452             *(mState.currentSnapshot()->transform),
    453             getRecordedClip(),
    454             refPaint(&paint), refPath(&path)));
    455 }
    456 
    457 void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
    458     mDisplayList->ref(tree);
    459     mDisplayList->vectorDrawables.push_back(tree);
    460     addOp(alloc().create_trivial<VectorDrawableOp>(
    461             tree,
    462             Rect(tree->stagingProperties()->getBounds()),
    463             *(mState.currentSnapshot()->transform),
    464             getRecordedClip()));
    465 }
    466 
    467 // Bitmap-based
    468 void RecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
    469     save(SaveFlags::Matrix);
    470     translate(left, top);
    471     drawBitmap(bitmap, paint);
    472     restore();
    473 }
    474 
    475 void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
    476                             const SkPaint* paint) {
    477     if (matrix.isIdentity()) {
    478         drawBitmap(bitmap, paint);
    479     } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
    480             && MathUtils::isPositive(matrix.getScaleX())
    481             && MathUtils::isPositive(matrix.getScaleY())) {
    482         // SkMatrix::isScaleTranslate() not available in L
    483         SkRect src;
    484         SkRect dst;
    485         bitmap.getBounds(&src);
    486         matrix.mapRect(&dst, src);
    487         drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
    488                    dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
    489     } else {
    490         save(SaveFlags::Matrix);
    491         concat(matrix);
    492         drawBitmap(bitmap, paint);
    493         restore();
    494     }
    495 }
    496 
    497 void RecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
    498             float srcRight, float srcBottom, float dstLeft, float dstTop,
    499             float dstRight, float dstBottom, const SkPaint* paint) {
    500     if (srcLeft == 0 && srcTop == 0
    501             && srcRight == bitmap.width()
    502             && srcBottom == bitmap.height()
    503             && (srcBottom - srcTop == dstBottom - dstTop)
    504             && (srcRight - srcLeft == dstRight - dstLeft)) {
    505         // transform simple rect to rect drawing case into position bitmap ops, since they merge
    506         save(SaveFlags::Matrix);
    507         translate(dstLeft, dstTop);
    508         drawBitmap(bitmap, paint);
    509         restore();
    510     } else {
    511         addOp(alloc().create_trivial<BitmapRectOp>(
    512                 Rect(dstLeft, dstTop, dstRight, dstBottom),
    513                 *(mState.currentSnapshot()->transform),
    514                 getRecordedClip(),
    515                 refPaint(paint), refBitmap(bitmap),
    516                 Rect(srcLeft, srcTop, srcRight, srcBottom)));
    517     }
    518 }
    519 
    520 void RecordingCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
    521             const float* vertices, const int* colors, const SkPaint* paint) {
    522     int vertexCount = (meshWidth + 1) * (meshHeight + 1);
    523     addOp(alloc().create_trivial<BitmapMeshOp>(
    524             calcBoundsOfPoints(vertices, vertexCount * 2),
    525             *(mState.currentSnapshot()->transform),
    526             getRecordedClip(),
    527             refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight,
    528             refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex
    529             refBuffer<int>(colors, vertexCount))); // 1 color per vertex
    530 }
    531 
    532 void RecordingCanvas::drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& patch,
    533             float dstLeft, float dstTop, float dstRight, float dstBottom,
    534             const SkPaint* paint) {
    535     addOp(alloc().create_trivial<PatchOp>(
    536             Rect(dstLeft, dstTop, dstRight, dstBottom),
    537             *(mState.currentSnapshot()->transform),
    538             getRecordedClip(),
    539             refPaint(paint), refBitmap(bitmap), refPatch(&patch)));
    540 }
    541 
    542 // Text
    543 void RecordingCanvas::drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
    544             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
    545             float boundsRight, float boundsBottom, float totalAdvance) {
    546     if (!glyphs || !positions || glyphCount <= 0 || paint.nothingToDraw()) return;
    547     glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
    548     positions = refBuffer<float>(positions, glyphCount * 2);
    549 
    550     // TODO: either must account for text shadow in bounds, or record separate ops for text shadows
    551     addOp(alloc().create_trivial<TextOp>(
    552             Rect(boundsLeft, boundsTop, boundsRight, boundsBottom),
    553             *(mState.currentSnapshot()->transform),
    554             getRecordedClip(),
    555             refPaint(&paint), glyphs, positions, glyphCount, x, y));
    556     drawTextDecorations(x, y, totalAdvance, paint);
    557 }
    558 
    559 void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
    560         const SkPaint& paint, const SkPath& path, size_t start, size_t end) {
    561     uint16_t glyphs[1];
    562     for (size_t i = start; i < end; i++) {
    563         glyphs[0] = layout.getGlyphId(i);
    564         float x = hOffset + layout.getX(i);
    565         float y = vOffset + layout.getY(i);
    566         if (paint.nothingToDraw()) return;
    567         const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1);
    568         addOp(alloc().create_trivial<TextOnPathOp>(
    569                 *(mState.currentSnapshot()->transform),
    570                 getRecordedClip(),
    571                 refPaint(&paint), tempGlyphs, 1, refPath(&path), x, y));
    572     }
    573 }
    574 
    575 void RecordingCanvas::drawBitmap(Bitmap& bitmap, const SkPaint* paint) {
    576     addOp(alloc().create_trivial<BitmapOp>(
    577             Rect(bitmap.width(), bitmap.height()),
    578             *(mState.currentSnapshot()->transform),
    579             getRecordedClip(),
    580             refPaint(paint), refBitmap(bitmap)));
    581 }
    582 
    583 void RecordingCanvas::drawRenderNode(RenderNode* renderNode) {
    584     auto&& stagingProps = renderNode->stagingProperties();
    585     RenderNodeOp* op = alloc().create_trivial<RenderNodeOp>(
    586             Rect(stagingProps.getWidth(), stagingProps.getHeight()),
    587             *(mState.currentSnapshot()->transform),
    588             getRecordedClip(),
    589             renderNode);
    590     int opIndex = addOp(op);
    591     if (CC_LIKELY(opIndex >= 0)) {
    592         int childIndex = mDisplayList->addChild(op);
    593 
    594         // update the chunk's child indices
    595         DisplayList::Chunk& chunk = mDisplayList->chunks.back();
    596         chunk.endChildIndex = childIndex + 1;
    597 
    598         if (renderNode->stagingProperties().isProjectionReceiver()) {
    599             // use staging property, since recording on UI thread
    600             mDisplayList->projectionReceiveIndex = opIndex;
    601         }
    602     }
    603 }
    604 
    605 void RecordingCanvas::drawLayer(DeferredLayerUpdater* layerHandle) {
    606     // We ref the DeferredLayerUpdater due to its thread-safe ref-counting semantics.
    607     mDisplayList->ref(layerHandle);
    608 
    609     LOG_ALWAYS_FATAL_IF(layerHandle->getBackingLayerApi() != Layer::Api::OpenGL);
    610     // Note that the backing layer has *not* yet been updated, so don't trust
    611     // its width, height, transform, etc...!
    612     addOp(alloc().create_trivial<TextureLayerOp>(
    613             Rect(layerHandle->getWidth(), layerHandle->getHeight()),
    614             *(mState.currentSnapshot()->transform),
    615             getRecordedClip(), layerHandle));
    616 }
    617 
    618 void RecordingCanvas::callDrawGLFunction(Functor* functor,
    619         GlFunctorLifecycleListener* listener) {
    620     mDisplayList->functors.push_back({functor, listener});
    621     mDisplayList->ref(listener);
    622     addOp(alloc().create_trivial<FunctorOp>(
    623             *(mState.currentSnapshot()->transform),
    624             getRecordedClip(),
    625             functor));
    626 }
    627 
    628 int RecordingCanvas::addOp(RecordedOp* op) {
    629     // skip op with empty clip
    630     if (op->localClip && op->localClip->rect.isEmpty()) {
    631         // NOTE: this rejection happens after op construction/content ref-ing, so content ref'd
    632         // and held by renderthread isn't affected by clip rejection.
    633         // Could rewind alloc here if desired, but callers would have to not touch op afterwards.
    634         return -1;
    635     }
    636 
    637     int insertIndex = mDisplayList->ops.size();
    638     mDisplayList->ops.push_back(op);
    639     if (mDeferredBarrierType != DeferredBarrierType::None) {
    640         // op is first in new chunk
    641         mDisplayList->chunks.emplace_back();
    642         DisplayList::Chunk& newChunk = mDisplayList->chunks.back();
    643         newChunk.beginOpIndex = insertIndex;
    644         newChunk.endOpIndex = insertIndex + 1;
    645         newChunk.reorderChildren = (mDeferredBarrierType == DeferredBarrierType::OutOfOrder);
    646         newChunk.reorderClip = mDeferredBarrierClip;
    647 
    648         int nextChildIndex = mDisplayList->children.size();
    649         newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
    650         mDeferredBarrierType = DeferredBarrierType::None;
    651     } else {
    652         // standard case - append to existing chunk
    653         mDisplayList->chunks.back().endOpIndex = insertIndex + 1;
    654     }
    655     return insertIndex;
    656 }
    657 
    658 void RecordingCanvas::refBitmapsInShader(const SkShader* shader) {
    659     if (!shader) return;
    660 
    661     // If this paint has an SkShader that has an SkBitmap add
    662     // it to the bitmap pile
    663     SkBitmap bitmap;
    664     SkShader::TileMode xy[2];
    665     if (shader->isABitmap(&bitmap, nullptr, xy)) {
    666         Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
    667         refBitmap(*hwuiBitmap);
    668         return;
    669     }
    670     SkShader::ComposeRec rec;
    671     if (shader->asACompose(&rec)) {
    672         refBitmapsInShader(rec.fShaderA);
    673         refBitmapsInShader(rec.fShaderB);
    674         return;
    675     }
    676 }
    677 
    678 }; // namespace uirenderer
    679 }; // namespace android
    680