public Canvas start(Canvas parentCanvas, RectF bounds, ComposeOp op) {
    if (this.parentCanvas != null) {
      throw new IllegalStateException("Cannot nest start() calls on a single OffscreenBitmap - call finish() first");
    }

    // Determine the scaling applied by the parentCanvas' pre-existing transform matrix. This is an optimization
    // to avoid creating bitmaps (or render nodes) with unreasonable sizes that will get scaled down when drawn
    // onto parentCanvas anyhow.
    if (preExistingTransform == null) preExistingTransform = new float[9];
    if (parentCanvasMatrix == null) parentCanvasMatrix = new Matrix();
    parentCanvas.getMatrix(parentCanvasMatrix);
    parentCanvasMatrix.getValues(preExistingTransform);

    float pixelScaleX = preExistingTransform[Matrix.MSCALE_X];
    float pixelScaleY = preExistingTransform[Matrix.MSCALE_Y];

    if (scaledBounds == null) scaledBounds = new RectF();
    scaledBounds.set(
        bounds.left * pixelScaleX,
        bounds.top * pixelScaleY,
        bounds.right * pixelScaleX,
        bounds.bottom * pixelScaleY
    );

    this.parentCanvas = parentCanvas;
    this.op = op;
    this.currentStrategy = chooseRenderStrategy(parentCanvas, op);
    if (this.targetRect == null) this.targetRect = new RectF();
    this.targetRect.set((int)bounds.left, (int)bounds.top, (int)bounds.right, (int)bounds.bottom);

    if (composePaint == null) composePaint = new LPaint();
    composePaint.reset();

    Canvas childCanvas;
    switch (currentStrategy) {
      case DIRECT:
        childCanvas = parentCanvas;
        childCanvas.save();
        break;

      case SAVE_LAYER:
        // Paint to use for composition
        composePaint.setAlpha(op.alpha);
        composePaint.setColorFilter(op.colorFilter);
        if (op.hasBlendMode()) {
          PaintCompat.setBlendMode(composePaint, op.blendMode);
        }

        // This adds an entry in the parentCanvas stack, will be popped and composited
        // when restore() is called
        Utils.saveLayerCompat(parentCanvas, bounds, composePaint);
        childCanvas = parentCanvas;
        break;

      case BITMAP:
        if (clearPaint == null) {
          clearPaint = new LPaint();
          clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        }

        if (needNewBitmap(bitmap, scaledBounds)) {
          if (bitmap != null) {
            deallocateBitmap(bitmap);
          }
          bitmap = allocateBitmap(scaledBounds, Bitmap.Config.ARGB_8888);
          bitmapCanvas = new Canvas(bitmap);
        } else {
          if (bitmapCanvas == null) {
            throw new IllegalStateException("If needNewBitmap() returns true, we should have a canvas ready");
          }
          bitmapCanvas.setMatrix(OffscreenLayer.IDENTITY_MATRIX);
          bitmapCanvas.drawRect(-1, -1, scaledBounds.width() + 1, scaledBounds.height() + 1, this.clearPaint);
        }

        PaintCompat.setBlendMode(composePaint, op.blendMode);
        composePaint.setColorFilter(op.colorFilter);
        composePaint.setAlpha(op.alpha);

        childCanvas = bitmapCanvas;
        childCanvas.scale(pixelScaleX, pixelScaleY); // Replicate scaling applied by parentCanvas
        childCanvas.translate(-bounds.left, -bounds.top); // So that the image begins at the top-left of the bitmap
        break;

      case RENDER_NODE:
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
          throw new IllegalStateException("RenderNode not supported but we chose it as render strategy");
        }

        if (renderNode == null) renderNode = new RenderNode("OffscreenLayer.main");
        if (op.hasShadow() && shadowRenderNode == null) {
          shadowRenderNode = new RenderNode("OffscreenLayer.shadow");
          lastRenderNodeShadow = null;
        }

        // The render node needs some data in advance
        if (op.hasBlendMode() || op.hasColorFilter()) {
          if (composePaint == null) composePaint = new LPaint();
          composePaint.reset();
          PaintCompat.setBlendMode(composePaint, op.blendMode);
          composePaint.setColorFilter(op.colorFilter);
          renderNode.setUseCompositingLayer(true, composePaint);

          if (op.hasShadow()) {
            if (shadowRenderNode == null) {
              throw new IllegalStateException("Must initialize shadowRenderNode when we have shadow");
            }
            shadowRenderNode.setUseCompositingLayer(true, composePaint);
          }
        }
        renderNode.setAlpha(op.alpha / 255.f);
        if (op.hasShadow()) {
          if (shadowRenderNode == null) {
            throw new IllegalStateException("Must initialize shadowRenderNode when we have shadow");
          }

          // lottie-web composes the shadow onto the canvas first, and then the
          // contents separately - mirror this behavior
          shadowRenderNode.setAlpha(op.alpha / 255.f);
        }
        renderNode.setHasOverlappingRendering(true);
        renderNode.setPosition((int)scaledBounds.left, (int)scaledBounds.top, (int)scaledBounds.right, (int)scaledBounds.bottom);

        childCanvas = renderNode.beginRecording((int) scaledBounds.width(), (int) scaledBounds.height());
        childCanvas.setMatrix(OffscreenLayer.IDENTITY_MATRIX);
        childCanvas.scale(pixelScaleX, pixelScaleY); // Replicate scaling applied by parentCanvas
        childCanvas.translate(-bounds.left, -bounds.top); // So that the image begins at the top-left of the bitmap
        break;

      default:
        throw new RuntimeException("Invalid render strategy for OffscreenLayer");
    }

    return childCanvas;
  }
parentCanvas' pre-existing matrix when start() was called
lottie/src/main/java/com/airbnb/lottie/utils/OffscreenLayer.java