DisplayListCanvas.cpp revision d35dcb13115ca1dd8c07e397f43a186cd7fd1a01
1/*
2 * Copyright (C) 2010 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 "DisplayListCanvas.h"
18
19#include "ResourceCache.h"
20#include "DeferredDisplayList.h"
21#include "DeferredLayerUpdater.h"
22#include "DisplayListOp.h"
23#include "RenderNode.h"
24#include "utils/PaintUtils.h"
25
26#include <SkCamera.h>
27#include <SkCanvas.h>
28
29#include <private/hwui/DrawGlInfo.h>
30
31namespace android {
32namespace uirenderer {
33
34DisplayListCanvas::DisplayListCanvas(int width, int height)
35    : mState(*this)
36    , mResourceCache(ResourceCache::getInstance())
37    , mDisplayListData(nullptr)
38    , mTranslateX(0.0f)
39    , mTranslateY(0.0f)
40    , mHasDeferredTranslate(false)
41    , mDeferredBarrierType(kBarrier_None)
42    , mHighContrastText(false)
43    , mRestoreSaveCount(-1) {
44    reset(width, height);
45}
46
47DisplayListCanvas::~DisplayListCanvas() {
48    LOG_ALWAYS_FATAL_IF(mDisplayListData,
49            "Destroyed a DisplayListCanvas during a record!");
50}
51
52void DisplayListCanvas::reset(int width, int height) {
53    LOG_ALWAYS_FATAL_IF(mDisplayListData,
54            "prepareDirty called a second time during a recording!");
55    mDisplayListData = new DisplayListData();
56
57    mState.setViewport(width, height);
58    mState.initializeSaveStack(0, 0, mState.getWidth(), mState.getHeight(), Vector3());
59
60    mDeferredBarrierType = kBarrier_InOrder;
61    mState.setDirtyClip(false);
62    mRestoreSaveCount = -1;
63}
64
65
66///////////////////////////////////////////////////////////////////////////////
67// Operations
68///////////////////////////////////////////////////////////////////////////////
69
70DisplayListData* DisplayListCanvas::finishRecording() {
71    flushRestoreToCount();
72    flushTranslate();
73
74    mPaintMap.clear();
75    mRegionMap.clear();
76    mPathMap.clear();
77    DisplayListData* data = mDisplayListData;
78    mDisplayListData = nullptr;
79    mSkiaCanvasProxy.reset(nullptr);
80    return data;
81}
82
83void DisplayListCanvas::callDrawGLFunction(Functor *functor) {
84    addDrawOp(new (alloc()) DrawFunctorOp(functor));
85    mDisplayListData->functors.add(functor);
86}
87
88SkCanvas* DisplayListCanvas::asSkCanvas() {
89    LOG_ALWAYS_FATAL_IF(!mDisplayListData,
90            "attempting to get an SkCanvas when we are not recording!");
91    if (!mSkiaCanvasProxy) {
92        mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
93    }
94
95    // SkCanvas instances default to identity transform, but should inherit
96    // the state of this Canvas; if this code was in the SkiaCanvasProxy
97    // constructor, we couldn't cache mSkiaCanvasProxy.
98    SkMatrix parentTransform;
99    getMatrix(&parentTransform);
100    mSkiaCanvasProxy.get()->setMatrix(parentTransform);
101
102    return mSkiaCanvasProxy.get();
103}
104
105int DisplayListCanvas::save(SkCanvas::SaveFlags flags) {
106    addStateOp(new (alloc()) SaveOp((int) flags));
107    return mState.save((int) flags);
108}
109
110void DisplayListCanvas::restore() {
111    if (mRestoreSaveCount < 0) {
112        restoreToCount(getSaveCount() - 1);
113        return;
114    }
115
116    mRestoreSaveCount--;
117    flushTranslate();
118    mState.restore();
119}
120
121void DisplayListCanvas::restoreToCount(int saveCount) {
122    mRestoreSaveCount = saveCount;
123    flushTranslate();
124    mState.restoreToCount(saveCount);
125}
126
127int DisplayListCanvas::saveLayer(float left, float top, float right, float bottom,
128        const SkPaint* paint, SkCanvas::SaveFlags flags) {
129    // force matrix/clip isolation for layer
130    flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag;
131
132    paint = refPaint(paint);
133    addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, paint, (int) flags));
134    return mState.save((int) flags);
135}
136
137void DisplayListCanvas::translate(float dx, float dy) {
138    if (dx == 0.0f && dy == 0.0f) return;
139
140    mHasDeferredTranslate = true;
141    mTranslateX += dx;
142    mTranslateY += dy;
143    flushRestoreToCount();
144    mState.translate(dx, dy, 0.0f);
145}
146
147void DisplayListCanvas::rotate(float degrees) {
148    if (degrees == 0.0f) return;
149
150    addStateOp(new (alloc()) RotateOp(degrees));
151    mState.rotate(degrees);
152}
153
154void DisplayListCanvas::scale(float sx, float sy) {
155    if (sx == 1.0f && sy == 1.0f) return;
156
157    addStateOp(new (alloc()) ScaleOp(sx, sy));
158    mState.scale(sx, sy);
159}
160
161void DisplayListCanvas::skew(float sx, float sy) {
162    addStateOp(new (alloc()) SkewOp(sx, sy));
163    mState.skew(sx, sy);
164}
165
166void DisplayListCanvas::setMatrix(const SkMatrix& matrix) {
167    addStateOp(new (alloc()) SetMatrixOp(matrix));
168    mState.setMatrix(matrix);
169}
170
171void DisplayListCanvas::setLocalMatrix(const SkMatrix& matrix) {
172    addStateOp(new (alloc()) SetLocalMatrixOp(matrix));
173    mState.setMatrix(matrix);
174}
175
176void DisplayListCanvas::concat(const SkMatrix& matrix) {
177    addStateOp(new (alloc()) ConcatMatrixOp(matrix));
178    mState.concatMatrix(matrix);
179}
180
181bool DisplayListCanvas::getClipBounds(SkRect* outRect) const {
182    Rect bounds = mState.getLocalClipBounds();
183    *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom);
184    return !(outRect->isEmpty());
185}
186
187bool DisplayListCanvas::quickRejectRect(float left, float top, float right, float bottom) const {
188    return mState.quickRejectConservative(left, top, right, bottom);
189}
190
191bool DisplayListCanvas::quickRejectPath(const SkPath& path) const {
192    SkRect bounds = path.getBounds();
193    return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
194}
195
196
197bool DisplayListCanvas::clipRect(float left, float top, float right, float bottom,
198        SkRegion::Op op) {
199    addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op));
200    return mState.clipRect(left, top, right, bottom, op);
201}
202
203bool DisplayListCanvas::clipPath(const SkPath* path, SkRegion::Op op) {
204    path = refPath(path);
205    addStateOp(new (alloc()) ClipPathOp(path, op));
206    return mState.clipPath(path, op);
207}
208
209bool DisplayListCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) {
210    region = refRegion(region);
211    addStateOp(new (alloc()) ClipRegionOp(region, op));
212    return mState.clipRegion(region, op);
213}
214
215void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) {
216    LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode");
217    DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(
218            renderNode,
219            *mState.currentTransform(),
220            mState.clipIsSimple());
221    addRenderNodeOp(op);
222}
223
224void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) {
225    // We ref the DeferredLayerUpdater due to its thread-safe ref-counting
226    // semantics.
227    mDisplayListData->ref(layerHandle);
228    addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y));
229}
230
231void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
232    bitmap = refBitmap(*bitmap);
233    paint = refPaint(paint);
234
235    addDrawOp(new (alloc()) DrawBitmapOp(bitmap, paint));
236}
237
238void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top,
239        const SkPaint* paint) {
240    save(SkCanvas::kMatrix_SaveFlag);
241    translate(left, top);
242    drawBitmap(&bitmap, paint);
243    restore();
244}
245
246void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
247        const SkPaint* paint) {
248    if (matrix.isIdentity()) {
249        drawBitmap(&bitmap, paint);
250    } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
251            && MathUtils::isPositive(matrix.getScaleX())
252            && MathUtils::isPositive(matrix.getScaleY())) {
253        // SkMatrix::isScaleTranslate() not available in L
254        SkRect src;
255        SkRect dst;
256        bitmap.getBounds(&src);
257        matrix.mapRect(&dst, src);
258        drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
259                   dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
260    } else {
261        save(SkCanvas::kMatrix_SaveFlag);
262        concat(matrix);
263        drawBitmap(&bitmap, paint);
264        restore();
265    }
266}
267
268void DisplayListCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
269        float srcRight, float srcBottom, float dstLeft, float dstTop,
270        float dstRight, float dstBottom, const SkPaint* paint) {
271    if (srcLeft == 0 && srcTop == 0
272            && srcRight == bitmap.width()
273            && srcBottom == bitmap.height()
274            && (srcBottom - srcTop == dstBottom - dstTop)
275            && (srcRight - srcLeft == dstRight - dstLeft)) {
276        // transform simple rect to rect drawing case into position bitmap ops, since they merge
277        save(SkCanvas::kMatrix_SaveFlag);
278        translate(dstLeft, dstTop);
279        drawBitmap(&bitmap, paint);
280        restore();
281    } else {
282        paint = refPaint(paint);
283
284        if (paint && paint->getShader()) {
285            float scaleX = (dstRight - dstLeft) / (srcRight - srcLeft);
286            float scaleY = (dstBottom - dstTop) / (srcBottom - srcTop);
287            if (!MathUtils::areEqual(scaleX, 1.0f) || !MathUtils::areEqual(scaleY, 1.0f)) {
288                // Apply the scale transform on the canvas, so that the shader
289                // effectively calculates positions relative to src rect space
290
291                save(SkCanvas::kMatrix_SaveFlag);
292                translate(dstLeft, dstTop);
293                scale(scaleX, scaleY);
294
295                dstLeft = 0.0f;
296                dstTop = 0.0f;
297                dstRight = srcRight - srcLeft;
298                dstBottom = srcBottom - srcTop;
299
300                addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
301                        srcLeft, srcTop, srcRight, srcBottom,
302                        dstLeft, dstTop, dstRight, dstBottom, paint));
303                restore();
304                return;
305            }
306        }
307
308        addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
309                srcLeft, srcTop, srcRight, srcBottom,
310                dstLeft, dstTop, dstRight, dstBottom, paint));
311    }
312}
313
314void DisplayListCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
315        const float* vertices, const int* colors, const SkPaint* paint) {
316    int vertexCount = (meshWidth + 1) * (meshHeight + 1);
317    vertices = refBuffer<float>(vertices, vertexCount * 2); // 2 floats per vertex
318    paint = refPaint(paint);
319    colors = refBuffer<int>(colors, vertexCount); // 1 color per vertex
320
321    addDrawOp(new (alloc()) DrawBitmapMeshOp(refBitmap(bitmap), meshWidth, meshHeight,
322           vertices, colors, paint));
323}
324
325void DisplayListCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& patch,
326        float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) {
327    const SkBitmap* bitmapPtr = refBitmap(bitmap);
328    const Res_png_9patch* patchPtr = refPatch(&patch);
329    paint = refPaint(paint);
330
331    addDrawOp(new (alloc()) DrawPatchOp(bitmapPtr, patchPtr,
332            dstLeft, dstTop, dstRight, dstBottom, paint));
333}
334
335void DisplayListCanvas::drawColor(int color, SkXfermode::Mode mode) {
336    addDrawOp(new (alloc()) DrawColorOp(color, mode));
337}
338
339void DisplayListCanvas::drawPaint(const SkPaint& paint) {
340    SkRect bounds;
341    if (getClipBounds(&bounds)) {
342        drawRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, paint);
343    }
344}
345
346
347void DisplayListCanvas::drawRect(float left, float top, float right, float bottom,
348        const SkPaint& paint) {
349    addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, refPaint(&paint)));
350}
351
352void DisplayListCanvas::drawRoundRect(float left, float top, float right, float bottom,
353        float rx, float ry, const SkPaint& paint) {
354    addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, refPaint(&paint)));
355}
356
357void DisplayListCanvas::drawRoundRect(
358        CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
359        CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
360        CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
361        CanvasPropertyPaint* paint) {
362    mDisplayListData->ref(left);
363    mDisplayListData->ref(top);
364    mDisplayListData->ref(right);
365    mDisplayListData->ref(bottom);
366    mDisplayListData->ref(rx);
367    mDisplayListData->ref(ry);
368    mDisplayListData->ref(paint);
369    refBitmapsInShader(paint->value.getShader());
370    addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
371            &right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
372}
373
374void DisplayListCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
375    addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, refPaint(&paint)));
376}
377
378void DisplayListCanvas::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
379        CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
380    mDisplayListData->ref(x);
381    mDisplayListData->ref(y);
382    mDisplayListData->ref(radius);
383    mDisplayListData->ref(paint);
384    refBitmapsInShader(paint->value.getShader());
385    addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
386            &radius->value, &paint->value));
387}
388
389void DisplayListCanvas::drawOval(float left, float top, float right, float bottom,
390        const SkPaint& paint) {
391    addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, refPaint(&paint)));
392}
393
394void DisplayListCanvas::drawArc(float left, float top, float right, float bottom,
395        float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
396    if (fabs(sweepAngle) >= 360.0f) {
397        drawOval(left, top, right, bottom, paint);
398    } else {
399        addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
400                        startAngle, sweepAngle, useCenter, refPaint(&paint)));
401    }
402}
403
404void DisplayListCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
405    addDrawOp(new (alloc()) DrawPathOp(refPath(&path), refPaint(&paint)));
406}
407
408void DisplayListCanvas::drawLines(const float* points, int count, const SkPaint& paint) {
409    points = refBuffer<float>(points, count);
410
411    addDrawOp(new (alloc()) DrawLinesOp(points, count, refPaint(&paint)));
412}
413
414void DisplayListCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
415    points = refBuffer<float>(points, count);
416
417    addDrawOp(new (alloc()) DrawPointsOp(points, count, refPaint(&paint)));
418}
419
420void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
421        const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) {
422    if (!glyphs || count <= 0) return;
423
424    int bytesCount = 2 * count;
425    DrawOp* op = new (alloc()) DrawTextOnPathOp(refText((const char*) glyphs, bytesCount),
426            bytesCount, count, refPath(&path),
427            hOffset, vOffset, refPaint(&paint));
428    addDrawOp(op);
429}
430
431void DisplayListCanvas::drawPosText(const uint16_t* text, const float* positions,
432        int count, int posCount, const SkPaint& paint) {
433    if (!text || count <= 0) return;
434
435    int bytesCount = 2 * count;
436    positions = refBuffer<float>(positions, count * 2);
437
438    DrawOp* op = new (alloc()) DrawPosTextOp(refText((const char*) text, bytesCount),
439                                             bytesCount, count, positions, refPaint(&paint));
440    addDrawOp(op);
441}
442
443void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions,
444        int count, const SkPaint& paint, float x, float y,
445        float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
446        float totalAdvance) {
447
448    if (!glyphs || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
449
450    int bytesCount = count * 2;
451    const char* text = refText((const char*) glyphs, bytesCount);
452    positions = refBuffer<float>(positions, count * 2);
453    Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom);
454
455    DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
456            x, y, positions, refPaint(&paint), totalAdvance, bounds);
457    addDrawOp(op);
458}
459
460void DisplayListCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) {
461    if (paint.getStyle() != SkPaint::kFill_Style ||
462            (paint.isAntiAlias() && !mState.currentTransform()->isSimple())) {
463        SkRegion::Iterator it(region);
464        while (!it.done()) {
465            const SkIRect& r = it.rect();
466            drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
467            it.next();
468        }
469    } else {
470        int count = 0;
471        Vector<float> rects;
472        SkRegion::Iterator it(region);
473        while (!it.done()) {
474            const SkIRect& r = it.rect();
475            rects.push(r.fLeft);
476            rects.push(r.fTop);
477            rects.push(r.fRight);
478            rects.push(r.fBottom);
479            count += 4;
480            it.next();
481        }
482        drawRects(rects.array(), count, &paint);
483    }
484}
485
486void DisplayListCanvas::drawRects(const float* rects, int count, const SkPaint* paint) {
487    if (count <= 0) return;
488
489    rects = refBuffer<float>(rects, count);
490    paint = refPaint(paint);
491    addDrawOp(new (alloc()) DrawRectsOp(rects, count, paint));
492}
493
494void DisplayListCanvas::setDrawFilter(SkDrawFilter* filter) {
495    mDrawFilter.reset(SkSafeRef(filter));
496}
497
498void DisplayListCanvas::insertReorderBarrier(bool enableReorder) {
499    flushRestoreToCount();
500    flushTranslate();
501    mDeferredBarrierType = enableReorder ? kBarrier_OutOfOrder : kBarrier_InOrder;
502}
503
504void DisplayListCanvas::flushRestoreToCount() {
505    if (mRestoreSaveCount >= 0) {
506        addOpAndUpdateChunk(new (alloc()) RestoreToCountOp(mRestoreSaveCount));
507        mRestoreSaveCount = -1;
508    }
509}
510
511void DisplayListCanvas::flushTranslate() {
512    if (mHasDeferredTranslate) {
513        if (mTranslateX != 0.0f || mTranslateY != 0.0f) {
514            addOpAndUpdateChunk(new (alloc()) TranslateOp(mTranslateX, mTranslateY));
515            mTranslateX = mTranslateY = 0.0f;
516        }
517        mHasDeferredTranslate = false;
518    }
519}
520
521size_t DisplayListCanvas::addOpAndUpdateChunk(DisplayListOp* op) {
522    int insertIndex = mDisplayListData->displayListOps.size();
523    mDisplayListData->displayListOps.push_back(op);
524    if (mDeferredBarrierType != kBarrier_None) {
525        // op is first in new chunk
526        mDisplayListData->chunks.emplace_back();
527        DisplayListData::Chunk& newChunk = mDisplayListData->chunks.back();
528        newChunk.beginOpIndex = insertIndex;
529        newChunk.endOpIndex = insertIndex + 1;
530        newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder);
531
532        int nextChildIndex = mDisplayListData->children().size();
533        newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex;
534        mDeferredBarrierType = kBarrier_None;
535    } else {
536        // standard case - append to existing chunk
537        mDisplayListData->chunks.back().endOpIndex = insertIndex + 1;
538    }
539    return insertIndex;
540}
541
542size_t DisplayListCanvas::flushAndAddOp(DisplayListOp* op) {
543    flushRestoreToCount();
544    flushTranslate();
545    return addOpAndUpdateChunk(op);
546}
547
548size_t DisplayListCanvas::addStateOp(StateOp* op) {
549    return flushAndAddOp(op);
550}
551
552size_t DisplayListCanvas::addDrawOp(DrawOp* op) {
553    Rect localBounds;
554    if (op->getLocalBounds(localBounds)) {
555        bool rejected = quickRejectRect(localBounds.left, localBounds.top,
556                localBounds.right, localBounds.bottom);
557        op->setQuickRejected(rejected);
558    }
559
560    mDisplayListData->hasDrawOps = true;
561    return flushAndAddOp(op);
562}
563
564size_t DisplayListCanvas::addRenderNodeOp(DrawRenderNodeOp* op) {
565    int opIndex = addDrawOp(op);
566    int childIndex = mDisplayListData->addChild(op);
567
568    // update the chunk's child indices
569    DisplayListData::Chunk& chunk = mDisplayListData->chunks.back();
570    chunk.endChildIndex = childIndex + 1;
571
572    if (op->renderNode()->stagingProperties().isProjectionReceiver()) {
573        // use staging property, since recording on UI thread
574        mDisplayListData->projectionReceiveIndex = opIndex;
575    }
576    return opIndex;
577}
578
579void DisplayListCanvas::refBitmapsInShader(const SkShader* shader) {
580    if (!shader) return;
581
582    // If this paint has an SkShader that has an SkBitmap add
583    // it to the bitmap pile
584    SkBitmap bitmap;
585    SkShader::TileMode xy[2];
586    if (shader->asABitmap(&bitmap, nullptr, xy) == SkShader::kDefault_BitmapType) {
587        refBitmap(bitmap);
588        return;
589    }
590    SkShader::ComposeRec rec;
591    if (shader->asACompose(&rec)) {
592        refBitmapsInShader(rec.fShaderA);
593        refBitmapsInShader(rec.fShaderB);
594        return;
595    }
596}
597
598}; // namespace uirenderer
599}; // namespace android
600