public static void applyTrimPathIfNeeded(
      Path path, float startValue, float endValue, float offsetValue) {
    if (L.isTraceEnabled()) {
      L.beginSection("applyTrimPathIfNeeded");
    }
    final PathMeasure pathMeasure = threadLocalPathMeasure.get();
    final Path tempPath = threadLocalTempPath.get();
    final Path tempPath2 = threadLocalTempPath2.get();

    pathMeasure.setPath(path, false);

    float length = pathMeasure.getLength();
    if (startValue == 1f && endValue == 0f) {
      if (L.isTraceEnabled()) {
        L.endSection("applyTrimPathIfNeeded");
      }
      return;
    }
    if (length < 1f || Math.abs(endValue - startValue - 1) < .01) {
      if (L.isTraceEnabled()) {
        L.endSection("applyTrimPathIfNeeded");
      }
      return;
    }
    float start = length * startValue;
    float end = length * endValue;
    float newStart = Math.min(start, end);
    float newEnd = Math.max(start, end);

    float offset = offsetValue * length;
    newStart += offset;
    newEnd += offset;

    // If the trim path has rotated around the path, we need to shift it back.
    if (newStart >= length && newEnd >= length) {
      newStart = MiscUtils.floorMod(newStart, length);
      newEnd = MiscUtils.floorMod(newEnd, length);
    }

    if (newStart < 0) {
      newStart = MiscUtils.floorMod(newStart, length);
    }
    if (newEnd < 0) {
      newEnd = MiscUtils.floorMod(newEnd, length);
    }

    // If the start and end are equals, return an empty path.
    if (newStart == newEnd) {
      path.reset();
      if (L.isTraceEnabled()) {
        L.endSection("applyTrimPathIfNeeded");
      }
      return;
    }

    if (newStart >= newEnd) {
      newStart -= length;
    }

    tempPath.reset();
    pathMeasure.getSegment(
        newStart,
        newEnd,
        tempPath,
        true);

    if (newEnd > length) {
      tempPath2.reset();
      pathMeasure.getSegment(
          0,
          newEnd % length,
          tempPath2,
          true);
      tempPath.addPath(tempPath2);
    } else if (newStart < 0) {
      tempPath2.reset();
      pathMeasure.getSegment(
          length + newStart,
          length,
          tempPath2,
          true);
      tempPath.addPath(tempPath2);
    }
    path.set(tempPath);
    if (L.isTraceEnabled()) {
      L.endSection("applyTrimPathIfNeeded");
    }
  }
Wrap in Local Thread is necessary for prevent race condition in multi-threaded mode
lottie/src/main/java/com/airbnb/lottie/utils/Utils.java