private void renderHardwareShadow(Canvas targetCanvas, DropShadow shadow) {
    if (renderNode == null || shadowRenderNode == null) {
      throw new IllegalStateException("Cannot render to render node outside a start()/finish() block");
    }

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
      throw new RuntimeException("RenderEffect is not supported on API level <31");
    }

    // The render node canvas is in the pre-pixelscale space, so we have to scale the shadow parameters
    // by the pixel scale
    float pixelScaleX = preExistingTransform != null ? preExistingTransform[Matrix.MSCALE_X] : 1.0f;
    float pixelScaleY = preExistingTransform != null ? preExistingTransform[Matrix.MSCALE_Y] : 1.0f;

    if (lastRenderNodeShadow == null || !shadow.sameAs(lastRenderNodeShadow)) {
      RenderEffect effect = RenderEffect.createColorFilterEffect(new PorterDuffColorFilter(shadow.getColor(), PorterDuff.Mode.SRC_IN));
      if (shadow.getRadius() > 0.0f) {
        float scaledRadius = shadow.getRadius() * (pixelScaleX + pixelScaleY) / 2.0f;
        effect = RenderEffect.createBlurEffect(scaledRadius, scaledRadius, effect, Shader.TileMode.CLAMP);
      }
      shadowRenderNode.setRenderEffect(effect);
      lastRenderNodeShadow = shadow;
    }

    RectF rectIncludingShadow = calculateRectIncludingShadow(targetRect, shadow);
    RectF scaledRectIncludingShadow = new RectF(
        rectIncludingShadow.left * pixelScaleX,
        rectIncludingShadow.top * pixelScaleY,
        rectIncludingShadow.right * pixelScaleX,
        rectIncludingShadow.bottom * pixelScaleY
    );

    shadowRenderNode.setPosition(0, 0, (int)scaledRectIncludingShadow.width(), (int)scaledRectIncludingShadow.height());
    Canvas shadowCanvas = shadowRenderNode.beginRecording((int)scaledRectIncludingShadow.width(), (int)scaledRectIncludingShadow.height());
    // Offset so that the image starts at the top left, and then offset by shadow displacement
    shadowCanvas.translate(-scaledRectIncludingShadow.left + shadow.getDx() * pixelScaleX, -scaledRectIncludingShadow.top + shadow.getDy() * pixelScaleY);
    shadowCanvas.drawRenderNode(renderNode);
    shadowRenderNode.endRecording();

    targetCanvas.save();
    targetCanvas.translate(scaledRectIncludingShadow.left, scaledRectIncludingShadow.top);
    targetCanvas.drawRenderNode(shadowRenderNode);
    targetCanvas.restore();
  }
Renders a shadow (only the shadow) of this.renderNode to the provided canvas.
lottie/src/main/java/com/airbnb/lottie/utils/OffscreenLayer.java