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