InProgressStroke
public final class InProgressStroke
Use an InProgressStroke to efficiently build a stroke over multiple rendering frames with incremental inputs.
To use an InProgressStroke, you would typically:
-
Repeatedly update the stroke:
-
Call
enqueueInputswith any new real and predicted stroke inputs. -
Call
updateShapewhenisUpdateNeededistrueand new geometry is needed for rendering. -
Render the current stroke mesh or outlines, either via a provided renderer that accepts an
InProgressStrokeor by using the various getters on this type with a custom renderer. -
Call
finishInputonce there are no more inputs for this stroke (e.g. the user lifts the stylus from the screen). -
Continue to call
updateShapeand render afterfinishInputuntilisUpdateNeededreturns false (to allow any lingering brush shape animations to complete). -
Extract the completed stroke by calling
toImmutable. -
For best performance, reuse this object and go back to step 1 rather than allocating a new instance.
Summary
Public constructors |
|---|
Public methods |
|
|---|---|
final boolean |
Returns true if the stroke's geometry changes with the passage of time (denoted by new values being passed to |
final void |
enqueueInputs(Enqueues the incremental |
final void |
Indicates that the inputs for the current stroke are finished. |
final Brush |
getBrush()The |
final @IntRange(from = 0) int |
Returns the number of |
final @IntRange(from = 0) int |
Returns the number of |
final @IntRange(from = 0) int |
getOutlineCount(@IntRange(from = 0) int coatIndex)Returns the number of outlines for the specified brush coat. |
final @IntRange(from = 0) int |
getOutlineVertexCount(Returns the number of outline points for the specified outline and brush coat. |
final @IntRange(from = 0) int |
Returns the number of inputs in the current stroke prediction. |
final @IntRange(from = 0) int |
|
final boolean |
Returns |
final boolean |
Returns |
final @NonNull StrokeInput |
populateInput(@NonNull StrokeInput out, @IntRange(from = 0) int index)Gets the value of the i-th input and overwrites |
final @NonNull MutableStrokeInputBatch |
populateInputs(Replace the contents of the |
final @NonNull BoxAccumulator |
populateMeshBounds(Writes to outBoxAccumulator the bounding box of the vertex positions of the mesh for brush coat |
final @NonNull MutableVec |
populateOutlinePosition(Fills |
final @NonNull BoxAccumulator |
populateUpdatedRegion(@NonNull BoxAccumulator outUpdatedRegion)Returns the bounding rectangle of mesh positions added, modified, or removed by calls to |
final void |
Call after making use of a value from |
final void |
Clears and starts a new stroke with the given |
final void |
@ExperimentalInkCustomBrushApiClears and starts a new stroke with the given |
final @NonNull Stroke |
Copies the current input, brush, and geometry as of the last call to |
final void |
updateShape(long currentElapsedTimeMillis)Updates the stroke geometry up to the given duration since the start of the stroke. |
Protected methods |
|
|---|---|
final void |
finalize() |
Public constructors
Public methods
changesWithTime
public final boolean changesWithTime()
Returns true if the stroke's geometry changes with the passage of time (denoted by new values being passed to updateShape), even if no new input points are provided via enqueueInputs. This is the case if the brush has one or more timed animation behavior that are still active (which can be true even after inputs are finished).
This is similar to isUpdateNeeded, except that it ignores whether inputs are finished or pending.
enqueueInputs
public final void enqueueInputs(
@NonNull StrokeInputBatch realInputs,
@NonNull StrokeInputBatch predictedInputs
)
Enqueues the incremental realInputs and sets the prediction to predictedInputs, overwriting any previous prediction. Queued inputs will be processed on the next call to updateShape.
This method requires that:
-
finishInputhas not been called since the last call tostart. -
realInputsandpredictedInputsmust form a valid stroke input sequence together with previously added real input. In particular, this means that the first input inrealInputsmust be valid following the last input in previously added real inputs, and the first input inpredictedInputsmust be valid following the last input inrealInputs: They must have the sameInputToolType, theirStrokeInput.elapsedTimeMillisvalues must be monotonically non-decreasing, and they can not duplicate the previous input.
Either one or both of realInputs and predictedInputs may be empty.
| Throws | |
|---|---|
kotlin.IllegalStateException |
If |
kotlin.IllegalArgumentException |
If the input is not valid. Note that this can be a common occurrence with real user input on certain devices, in particular due to duplicate or out-of-order inputs. Therefore, users should either catch and handle this exception or sanitize the input to avoid ensure validity before passing it to this function. |
finishInput
public final void finishInput()
Indicates that the inputs for the current stroke are finished. After calling this, it is an error to call enqueueInputs until start is called again to start a new stroke. This method is idempotent; it has no effect if start was never called, or if this method has already been called since the last call to start. This method is synchronous, but the stroke may not be fully finished changing shape due to brush shape animations until isUpdateNeeded returns false. Until that condition is met, keep calling updateShape periodically and rendering the result.
getBrush
public final Brush getBrush()
The Brush currently being used to generate the stroke content. To set this, call start.
getBrushCoatCount
public final @IntRange(from = 0) int getBrushCoatCount()
Returns the number of BrushCoats for the current brush, or zero if start has not been called.
getInputCount
public final @IntRange(from = 0) int getInputCount()
Returns the number of StrokeInputs in the stroke so far. This counts all of the real inputs and the most-recently-processed sequence of predicted inputs.
getOutlineCount
public final @IntRange(from = 0) int getOutlineCount(@IntRange(from = 0) int coatIndex)
Returns the number of outlines for the specified brush coat.
Calls to functions that accept an outlineIndex must treat the result of this function as an upper bound. Coats with discontinuous geometry will always have multiple outlines, but even continuous geometry may be drawn with multiple overlapping outlines when this improves rendering quality or performance.
| Parameters | |
|---|---|
@IntRange(from = 0) int coatIndex |
Must be between 0 (inclusive) and the result of |
getOutlineVertexCount
public final @IntRange(from = 0) int getOutlineVertexCount(
@IntRange(from = 0) int coatIndex,
@IntRange(from = 0) int outlineIndex
)
Returns the number of outline points for the specified outline and brush coat. populateOutlinePosition must treat the result of this as the upper bound of its outlineVertexIndex parameter.
| Parameters | |
|---|---|
@IntRange(from = 0) int coatIndex |
Must be between 0 (inclusive) and the result of |
@IntRange(from = 0) int outlineIndex |
Must be between 0 (inclusive) and the result of |
getPredictedInputCount
public final @IntRange(from = 0) int getPredictedInputCount()
Returns the number of inputs in the current stroke prediction.
isInputFinished
public final boolean isInputFinished()
Returns true if finishInput has been called since the last call to start, or if start hasn't been called yet. If this returns true, it is an error to call enqueueInputs.
isUpdateNeeded
public final boolean isUpdateNeeded()
Returns true if calling updateShape would have any effect on the stroke (and should thus be called before the next render), or false if no calls to updateShape are currently needed. Specifically:
-
If the brush has one or more timed shape animation behavior that are still active (which can be true even after inputs are finished), returns
true. -
If there are no active shape animation behaviors, but there are pending inputs from an
enqueueInputscall that have not yet been consumed by a call toupdateShape, returnstrue. -
Otherwise, returns
false.
Once isInputFinished returns true and this method returns false, the stroke is considered "dry", and will not change any further until the next call to start.
populateInput
public final @NonNull StrokeInput populateInput(@NonNull StrokeInput out, @IntRange(from = 0) int index)
Gets the value of the i-th input and overwrites out. Requires that index is non-negative and less than getInputCount.
Returns the passed-in StrokeInput to make it easier to chain calls.
| Returns | |
|---|---|
@NonNull StrokeInput |
populateInputs
public final @NonNull MutableStrokeInputBatch populateInputs(
@NonNull MutableStrokeInputBatch out,
@IntRange(from = 0) int from,
@IntRange(from = 0) int to
)
Replace the contents of the MutableStrokeInputBatch with the specified range of inputs from the this InProgressStroke. By default, all inputs are copied.
Returns the passed-in MutableStrokeInputBatch to make it easier to chain calls.
| Returns | |
|---|---|
@NonNull MutableStrokeInputBatch |
populateMeshBounds
public final @NonNull BoxAccumulator populateMeshBounds(
@IntRange(from = 0) int coatIndex,
@NonNull BoxAccumulator outMeshBounds
)
Writes to outBoxAccumulator the bounding box of the vertex positions of the mesh for brush coat coatIndex.
Returns the passed in BoxAccumulator to make it easier to chain calls.
| Parameters | |
|---|---|
@IntRange(from = 0) int coatIndex |
The index of the coat to obtain the bounding box from. |
@NonNull BoxAccumulator outMeshBounds |
The pre-allocated |
| Returns | |
|---|---|
@NonNull BoxAccumulator |
populateOutlinePosition
public final @NonNull MutableVec populateOutlinePosition(
@IntRange(from = 0) int coatIndex,
@IntRange(from = 0) int outlineIndex,
@IntRange(from = 0) int outlineVertexIndex,
@NonNull MutableVec outPosition
)
Fills outPosition with the x and y coordinates of the specified outline vertex.
Returns the passed-in MutableVec to make it easier to chain calls.
| Parameters | |
|---|---|
@IntRange(from = 0) int coatIndex |
Must be between 0 (inclusive) and the result of |
@IntRange(from = 0) int outlineIndex |
Must be between 0 (inclusive) and the result of |
@IntRange(from = 0) int outlineVertexIndex |
Must be between 0 (inclusive) and the result of |
@NonNull MutableVec outPosition |
the pre-allocated |
populateUpdatedRegion
public final @NonNull BoxAccumulator populateUpdatedRegion(@NonNull BoxAccumulator outUpdatedRegion)
Returns the bounding rectangle of mesh positions added, modified, or removed by calls to updateShape since the most recent call to start or resetUpdatedRegion.
Returns the passed in BoxAccumulator to make it easier to chain calls.
| Parameters | |
|---|---|
@NonNull BoxAccumulator outUpdatedRegion |
The pre-allocated |
| Returns | |
|---|---|
@NonNull BoxAccumulator |
resetUpdatedRegion
public final void resetUpdatedRegion()
Call after making use of a value from populateUpdatedRegion to reset the accumulation.
start
public final void start(@NonNull Brush brush)
Clears and starts a new stroke with the given brush.
This includes clearing or resetting any existing inputs, mesh data, and updated region. This method must be called at least once after construction before making any calls to enqueueInputs or updateShape.
start
@ExperimentalInkCustomBrushApi
public final void start(@NonNull Brush brush, int noiseSeed)
Clears and starts a new stroke with the given brush, using the given per-stroke seed value to help seed the brush's noise behaviors, if any.
This includes clearing or resetting any existing inputs, mesh data, and updated region. This method must be called at least once after construction before making any calls to enqueueInputs or updateShape.
toImmutable
public final @NonNull Stroke toImmutable()
Copies the current input, brush, and geometry as of the last call to start or updateShape to a new Stroke.
The resulting Stroke will not be modified if further inputs are added to this InProgressStroke, and a Stroke created by another call to this method will not modify or be connected in any way to the prior Stroke.
updateShape
public final void updateShape(long currentElapsedTimeMillis)
Updates the stroke geometry up to the given duration since the start of the stroke. This will consume any inputs queued up by calls to enqueueInputs, and cause brush shape animations (if any) to progress up to the specified time. Any stroke geometry resulting from previously-predicted input from before the previous call to this method will be cleared.
This method requires that:
-
If passed, the value of
currentElapsedTimeMillispassed into this method over the course of a single stroke must be non-decreasing and non-negative. To have shape animations progress at their intended rate, pass in values for this field that are in the same time base as theStrokeInput.elapsedTimeMillisvalues being passed toenqueueInputs, repeatedly untilisInputFinishedreturnstrue.
Clients that do not use brushes with shape animation behaviors can omit currentElapsedTimeMillis. Doing so when using brushes with shape animation beaviors will cause the animation to be completed immediately.
| Throws | |
|---|---|
kotlin.IllegalStateException |
If |
kotlin.IllegalArgumentException |
If |