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