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