/**
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.keyframes.model;
import android.graphics.Matrix;
import android.graphics.Paint;
import java.util.List;
import com.facebook.keyframes.model.keyframedmodels.KeyFramedAnchorPoint;
import com.facebook.keyframes.model.keyframedmodels.KeyFramedOpacity;
import com.facebook.keyframes.model.keyframedmodels.KeyFramedPath;
import com.facebook.keyframes.model.keyframedmodels.KeyFramedStrokeWidth;
import com.facebook.keyframes.util.AnimationHelper;
import com.facebook.keyframes.util.ArgCheckUtil;
import com.facebook.keyframes.util.ListHelper;
/**
* An object which describes one feature layer to be drawn. This includes color, stroke, and
* animation information for just this feature. The shape is backed by a series of vector commands
* that describe how to draw the feature as a path.
*/
public class KFFeature {
/**
* The name of this feature, for ease of identification.
*/
public static final String NAME_JSON_FIELD = "name";
private final String mName;
/**
* The fill color to use for this feature.
*/
public static final String FILL_COLOR_JSON_FIELD = "fill_color";
private final int mFillColor;
/**
* The stroke color to use for this feature.
*/
public static final String STROKE_COLOR_JSON_FIELD = "stroke_color";
private final int mStrokeColor;
/**
* The width of the stroke used if tracing the path.
*/
public static final String STROKE_WIDTH_JSON_FIELD = "stroke_width";
private final float mStrokeWidth;
/**
* The frame that this feature stops showing up.
*/
public static final String FROM_FRAME_JSON_FIELD = "from_frame";
private final float mFromFrame;
/**
* The frame that this feature starts showing up.
*/
public static final String TO_FRAME_JSON_FIELD = "to_frame";
private final float mToFrame;
/**
* A list of {@link KFFeatureFrame}s which holds information about how the path of this
* feature changes throughout the duration of the animation.
*/
public static final String KEY_FRAMES_JSON_FIELD = "key_frames";
private final List<KFFeatureFrame> mKeyFrames;
/**
* Timing curves needed if mKeyFrames is present to describe how to animate between each key
* frame.
*/
public static final String TIMING_CURVES_JSON_FIELD = "timing_curves";
private final float[][][] mTimingCurves;
/**
* The animation layer that this feature belongs to. The final animation matrix of the group will
* be applied to this feature, and any animations belonging to the feature will be nested within
* this group.
*/
public static final String ANIMATION_GROUP_JSON_FIELD = "animation_group";
private final int mAnimationGroup;
/**
* The cap the use at the ends of stroke lines.
*/
public static final String STROKE_LINE_CAP_JSON_FIELD = "stroke_line_cap";
private final Paint.Cap mStrokeLineCap;
/**
* Masking layer that can be used for this feature.
*/
public static final String FEATURE_MASK_JSON_FIELD = "masking";
private final KFFeature mFeatureMask;
/**
* A list of animations to apply to just this feature layer.
*/
public static final String FEATURE_ANIMATIONS_JSON_FIELD = "feature_animations";
/**
* A KFAnimation just for the special cased stroke width animation. Package private for testing.
*/
final KFAnimation mStrokeWidthAnimation;
/**
* The remaining, matrix based animations from the feature_animations set.
* Package private for testing.
*/
final List<KFAnimation> mFeatureMatrixAnimations;
/**
* The anchor point for all animations in this feature.
*/
final KFAnimation mAnchorPoint;
/**
* The opacity for this feature.
*/
private final KFAnimation mOpacityAnimation;
/**
* An optional effect that this feature layer can have.
* Currently, only a simple linear gradient is supported.
*/
public static final String EFFECT_JSON_FIELD = "effects";
private final KFFeatureEffect mEffect;
/**
* EXPERIMENTAL optional "backedImage" name used to refer the bitmap name backing the feature.
*/
public static final String BACKED_IMAGE_NAME_JSON_FIELD = "backed_image";
private final String mBackedImageName;
/**
* A post-processed object containing cached information for this path, if keyframed.
*/
private final KeyFramedPath mKeyFramedPath;
public static class Builder {
public String name;
public int fillColor;
public int strokeColor;
public float strokeWidth;
public float fromFrame = 0;
public float toFrame = Float.MAX_VALUE;
public List<KFFeatureFrame> keyFrames;
public float[][][] timingCurves;
public int animationGroup;
public Paint.Cap strokeLineCap = Paint.Cap.ROUND;
public KFFeature featureMask;
public List<KFAnimation> featureAnimations;
public float[] anchorPoint;
public KFFeatureEffect effect;
public String backedImageName;
public KFFeature build() {
return new KFFeature(
name,
fillColor,
strokeColor,
strokeWidth,
fromFrame,
toFrame,
keyFrames,
timingCurves,
animationGroup,
strokeLineCap,
featureMask,
featureAnimations,
anchorPoint,
effect,
backedImageName);
}
}
public KFFeature(
String name,
int fillColor,
int strokeColor,
float strokeWidth,
float fromFrame,
float toFrame,
List<KFFeatureFrame> keyFrames,
float[][][] timingCurves,
int animationGroup,
Paint.Cap strokeLineCap,
KFFeature featureMask,
List<KFAnimation> featureAnimations,
float[] anchorPoint,
KFFeatureEffect effect,
String backedImageName) {
mName = name;
mFillColor = fillColor;
mStrokeColor = strokeColor;
mStrokeWidth = strokeWidth;
mFromFrame = fromFrame;
mToFrame = toFrame;
mKeyFrames = ListHelper.immutableOrEmpty(keyFrames);
mTimingCurves = ArgCheckUtil.checkArg(
timingCurves,
ArgCheckUtil.checkTimingCurveObjectValidity(timingCurves, mKeyFrames.size()),
TIMING_CURVES_JSON_FIELD);
mAnimationGroup = animationGroup;
mStrokeLineCap = strokeLineCap;
mFeatureMask = featureMask;
mStrokeWidthAnimation = AnimationHelper.extractSpecialAnimationAnimationSet(
featureAnimations,
KFAnimation.PropertyType.STROKE_WIDTH);
mAnchorPoint = AnimationHelper.extractSpecialAnimationAnimationSet(
featureAnimations,
KFAnimation.PropertyType.ANCHOR_POINT);
mOpacityAnimation = AnimationHelper.extractSpecialAnimationAnimationSet(
featureAnimations,
KFAnimation.PropertyType.OPACITY);
ListHelper.sort(featureAnimations, KFAnimation.ANIMATION_PROPERTY_COMPARATOR);
mFeatureMatrixAnimations = ListHelper.immutableOrEmpty(featureAnimations);
mEffect = effect;
mBackedImageName = backedImageName;
mKeyFramedPath = mKeyFrames.isEmpty() ? null : KeyFramedPath.fromFeature(this);
}
public String getName() {
return mName;
}
public int getFillColor() {
return mFillColor;
}
public int getStrokeColor() {
return mStrokeColor;
}
public float getFromFrame() {
return mFromFrame;
}
public float getToFrame() {
return mToFrame;
}
public List<KFFeatureFrame> getKeyFrames() {
return mKeyFrames;
}
public float[][][] getTimingCurves() {
return mTimingCurves;
}
public KeyFramedPath getPath() {
return mKeyFramedPath;
}
public int getAnimationGroup() {
return mAnimationGroup;
}
public Paint.Cap getStrokeLineCap() {
return mStrokeLineCap;
}
public KFFeature getFeatureMask() {
return mFeatureMask;
}
public void setStrokeWidth(
KeyFramedStrokeWidth.StrokeWidth strokeWidth,
float frameProgress) {
if (strokeWidth == null) {
return;
}
strokeWidth.setStrokeWidth(mStrokeWidth);
if (mStrokeWidthAnimation == null) {
return;
}
mStrokeWidthAnimation.getAnimation().apply(frameProgress, strokeWidth);
}
public void setOpacity(
KeyFramedOpacity.Opacity opacity,
float frameProgress) {
if (opacity == null || mOpacityAnimation == null) {
return;
}
mOpacityAnimation.getAnimation().apply(frameProgress, opacity);
}
public void setAnimationMatrix(Matrix featureMatrix, float frameProgress) {
if (featureMatrix == null) {
return;
}
featureMatrix.reset();
if (mFeatureMatrixAnimations == null) {
return;
}
if (mAnchorPoint != null) {
mAnchorPoint.getAnimation().apply(frameProgress, featureMatrix);
}
for (int i = 0, len = mFeatureMatrixAnimations.size(); i < len; i++) {
mFeatureMatrixAnimations.get(i).getAnimation().apply(frameProgress, featureMatrix);
}
}
public KFFeatureEffect getEffect() {
return mEffect;
}
public String getBackedImageName() {
return mBackedImageName;
}
}