Merge "add animation and float variables Test: RemoteCompose CTS test Bug: 339721781" into main
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 55f2dee..00262be 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -30,7 +30,7 @@
ArrayList<Operation> mOperations;
RemoteComposeState mRemoteComposeState = new RemoteComposeState();
-
+ TimeVariables mTimeVariables = new TimeVariables();
// Semantic version of the document
Version mVersion = new Version(0, 1, 0);
@@ -70,6 +70,7 @@
public void setWidth(int width) {
this.mWidth = width;
+ mRemoteComposeState.setWindowWidth(width);
}
public int getHeight() {
@@ -78,6 +79,8 @@
public void setHeight(int height) {
this.mHeight = height;
+ mRemoteComposeState.setWindowHeight(height);
+
}
public RemoteComposeBuffer getBuffer() {
@@ -111,21 +114,21 @@
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
+ * the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT
+ * - LAYOUT_WRAP_CONTENT
+ * or adding an horizontal mode and a vertical mode:
+ * - LAYOUT_HORIZONTAL_MATCH_PARENT
+ * - LAYOUT_HORIZONTAL_WRAP_CONTENT
+ * - LAYOUT_HORIZONTAL_FIXED
+ * - LAYOUT_VERTICAL_MATCH_PARENT
+ * - LAYOUT_VERTICAL_WRAP_CONTENT
+ * - LAYOUT_VERTICAL_FIXED
+ * The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
this.mContentScroll = scroll;
@@ -138,8 +141,8 @@
* Given dimensions w x h of where to paint the content, returns the corresponding scale factor
* according to the contentSizing information
*
- * @param w horizontal dimension of the rendering area
- * @param h vertical dimension of the rendering area
+ * @param w horizontal dimension of the rendering area
+ * @param h vertical dimension of the rendering area
* @param scaleOutput will contain the computed scale factor
*/
public void computeScale(float w, float h, float[] scaleOutput) {
@@ -154,37 +157,43 @@
float scale = Math.min(1f, Math.min(scaleX, scaleY));
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_FIT: {
float scaleX = w / mWidth;
float scaleY = h / mHeight;
float scale = Math.min(scaleX, scaleY);
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_FILL_WIDTH: {
float scale = w / mWidth;
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_FILL_HEIGHT: {
float scale = h / mHeight;
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_CROP: {
float scaleX = w / mWidth;
float scaleY = h / mHeight;
float scale = Math.max(scaleX, scaleY);
contentScaleX = scale;
contentScaleY = scale;
- } break;
+ }
+ break;
case RootContentBehavior.SCALE_FILL_BOUNDS: {
float scaleX = w / mWidth;
float scaleY = h / mHeight;
contentScaleX = scaleX;
contentScaleY = scaleY;
- } break;
+ }
+ break;
default:
// nothing
}
@@ -197,10 +206,10 @@
* Given dimensions w x h of where to paint the content, returns the corresponding translation
* according to the contentAlignment information
*
- * @param w horizontal dimension of the rendering area
- * @param h vertical dimension of the rendering area
- * @param contentScaleX the horizontal scale we are going to use for the content
- * @param contentScaleY the vertical scale we are going to use for the content
+ * @param w horizontal dimension of the rendering area
+ * @param h vertical dimension of the rendering area
+ * @param contentScaleX the horizontal scale we are going to use for the content
+ * @param contentScaleY the vertical scale we are going to use for the content
* @param translateOutput will contain the computed translation
*/
private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY,
@@ -215,26 +224,32 @@
switch (horizontalContentAlignment) {
case RootContentBehavior.ALIGNMENT_START: {
// nothing
- } break;
+ }
+ break;
case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: {
translateX = (w - contentWidth) / 2f;
- } break;
+ }
+ break;
case RootContentBehavior.ALIGNMENT_END: {
translateX = w - contentWidth;
- } break;
+ }
+ break;
default:
// nothing (same as alignment_start)
}
switch (verticalContentAlignment) {
case RootContentBehavior.ALIGNMENT_TOP: {
// nothing
- } break;
+ }
+ break;
case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: {
translateY = (h - contentHeight) / 2f;
- } break;
+ }
+ break;
case RootContentBehavior.ALIGNMENT_BOTTOM: {
translateY = h - contentHeight;
- } break;
+ }
+ break;
default:
// nothing (same as alignment_top)
}
@@ -291,7 +306,13 @@
this.mMetadata = metadata;
}
- public boolean contains(float x, float y) {
+ /**
+ * Returns true if x,y coordinate is within bounds
+ * @param x x-coordinate
+ * @param y y-coordinate
+ * @return x,y coordinate is within bounds
+ */
+ public boolean contains(float x, float y) {
return x >= mLeft && x < mRight
&& y >= mTop && y < mBottom;
}
@@ -341,16 +362,22 @@
public void initializeContext(RemoteContext context) {
mRemoteComposeState.reset();
mClickAreas.clear();
-
+ mRemoteComposeState.setNextId(RemoteComposeState.START_ID);
context.mDocument = this;
context.mRemoteComposeState = mRemoteComposeState;
-
// mark context to be in DATA mode, which will skip the painting ops.
context.mMode = RemoteContext.ContextMode.DATA;
- for (Operation op: mOperations) {
+ mTimeVariables.updateTime(context);
+
+ for (Operation op : mOperations) {
+ if (op instanceof VariableSupport) {
+ ((VariableSupport) op).updateVariables(context);
+ ((VariableSupport) op).registerListening(context);
+ }
op.apply(context);
}
context.mMode = RemoteContext.ContextMode.UNSET;
+
}
///////////////////////////////////////////////////////////////////////////////////////////////
@@ -375,7 +402,7 @@
* @param minorVersion minor version number, increased when adding new features
* @param patch patch level, increased upon bugfixes
*/
- void setVersion(int majorVersion, int minorVersion, int patch) {
+ void setVersion(int majorVersion, int minorVersion, int patch) {
mVersion = new Version(majorVersion, minorVersion, patch);
}
@@ -389,13 +416,13 @@
* the click coordinates will be the one reported; the order of addition of those click areas
* is therefore meaningful.
*
- * @param id the id of the area, which will be reported on click
+ * @param id the id of the area, which will be reported on click
* @param contentDescription the content description (used for accessibility)
- * @param left the left coordinate of the click area (in pixels)
- * @param top the top coordinate of the click area (in pixels)
- * @param right the right coordinate of the click area (in pixels)
- * @param bottom the bottom coordinate of the click area (in pixels)
- * @param metadata arbitrary metadata associated with the are, also reported on click
+ * @param left the left coordinate of the click area (in pixels)
+ * @param top the top coordinate of the click area (in pixels)
+ * @param right the right coordinate of the click area (in pixels)
+ * @param bottom the bottom coordinate of the click area (in pixels)
+ * @param metadata arbitrary metadata associated with the are, also reported on click
*/
public void addClickArea(int id, String contentDescription,
float left, float top, float right, float bottom, String metadata) {
@@ -417,7 +444,7 @@
* listeners.
*/
public void onClick(float x, float y) {
- for (ClickAreaRepresentation clickArea: mClickAreas) {
+ for (ClickAreaRepresentation clickArea : mClickAreas) {
if (clickArea.contains(x, y)) {
warnClickListeners(clickArea);
}
@@ -430,7 +457,7 @@
* @param id the click area id
*/
public void performClick(int id) {
- for (ClickAreaRepresentation clickArea: mClickAreas) {
+ for (ClickAreaRepresentation clickArea : mClickAreas) {
if (clickArea.mId == id) {
warnClickListeners(clickArea);
}
@@ -441,17 +468,36 @@
* Warn click listeners when a click area is activated
*/
private void warnClickListeners(ClickAreaRepresentation clickArea) {
- for (ClickCallbacks listener: mClickListeners) {
+ for (ClickCallbacks listener : mClickListeners) {
listener.click(clickArea.mId, clickArea.mMetadata);
}
}
- ///////////////////////////////////////////////////////////////////////////////////////////////
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (Operation op : mOperations) {
+ builder.append(op.toString());
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
// Painting
- ///////////////////////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
private final float[] mScaleOutput = new float[2];
private final float[] mTranslateOutput = new float[2];
+ private int mRepaintNext = -1; // delay to next repaint -1 = don't 1 = asap
+
+ /**
+ * Returns > 0 if it needs to repaint
+ * @return
+ */
+ public int needsRepaint() {
+ return mRepaintNext;
+ }
/**
* Paint the document
@@ -475,6 +521,11 @@
context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]);
context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]);
}
+ mTimeVariables.updateTime(context);
+ context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, getWidth());
+ context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, getHeight());
+ mRepaintNext = context.updateOps();
+
for (Operation op : mOperations) {
// operations will only be executed if no theme is set (ie UNSPECIFIED)
// or the theme is equal as the one passed in argument to paint.
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 54b277a..4b45ab6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -19,6 +19,7 @@
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
import com.android.internal.widget.remotecompose.core.operations.ClipRect;
+import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
@@ -28,9 +29,12 @@
import com.android.internal.widget.remotecompose.core.operations.DrawPath;
import com.android.internal.widget.remotecompose.core.operations.DrawRect;
import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawText;
+import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
-import com.android.internal.widget.remotecompose.core.operations.DrawTextRun;
import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
+import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.Header;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
@@ -42,7 +46,10 @@
import com.android.internal.widget.remotecompose.core.operations.PathData;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.TextData;
+import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
@@ -67,9 +74,10 @@
public static final int DRAW_BITMAP = 44;
public static final int DRAW_BITMAP_INT = 66;
public static final int DATA_BITMAP = 101;
+ public static final int DATA_SHADER = 45;
public static final int DATA_TEXT = 102;
-/////////////////////////////=====================
+ /////////////////////////////=====================
public static final int CLIP_PATH = 38;
public static final int CLIP_RECT = 39;
public static final int PAINT_VALUES = 40;
@@ -91,6 +99,12 @@
public static final int MATRIX_SAVE = 130;
public static final int MATRIX_RESTORE = 131;
public static final int MATRIX_SET = 132;
+ public static final int DATA_FLOAT = 80;
+ public static final int ANIMATED_FLOAT = 81;
+ public static final int DRAW_TEXT_ANCHOR = 133;
+ public static final int COLOR_EXPRESSIONS = 134;
+ public static final int TEXT_FROM_FLOAT = 135;
+ public static final int TEXT_MERGE = 136;
/////////////////////////////////////////======================
public static IntMap<CompanionOperation> map = new IntMap<>();
@@ -114,7 +128,7 @@
map.put(DRAW_RECT, DrawRect.COMPANION);
map.put(DRAW_ROUND_RECT, DrawRoundRect.COMPANION);
map.put(DRAW_TEXT_ON_PATH, DrawTextOnPath.COMPANION);
- map.put(DRAW_TEXT_RUN, DrawTextRun.COMPANION);
+ map.put(DRAW_TEXT_RUN, DrawText.COMPANION);
map.put(DRAW_TWEEN_PATH, DrawTweenPath.COMPANION);
map.put(DATA_PATH, PathData.COMPANION);
map.put(PAINT_VALUES, PaintData.COMPANION);
@@ -126,6 +140,13 @@
map.put(MATRIX_TRANSLATE, MatrixTranslate.COMPANION);
map.put(CLIP_PATH, ClipPath.COMPANION);
map.put(CLIP_RECT, ClipRect.COMPANION);
+ map.put(DATA_SHADER, ShaderData.COMPANION);
+ map.put(DATA_FLOAT, FloatConstant.COMPANION);
+ map.put(ANIMATED_FLOAT, FloatExpression.COMPANION);
+ map.put(DRAW_TEXT_ANCHOR, DrawTextAnchored.COMPANION);
+ map.put(COLOR_EXPRESSIONS, ColorExpression.COMPANION);
+ map.put(TEXT_FROM_FLOAT, TextFromFloat.COMPANION);
+ map.put(TEXT_MERGE, TextMerge.COMPANION);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index eece8ad52..ecd0efc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -68,7 +68,35 @@
public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset);
- public abstract void drawTextRun(int textID,
+ /**
+ * Return the dimensions (left, top, right, bottom).
+ * Relative to a drawTextRun x=0, y=0;
+ *
+ * @param textId
+ * @param start
+ * @param end if end is -1 it means the whole string
+ * @param monospace measure with better support for monospace
+ * @param bounds the bounds (left, top, right, bottom)
+ */
+ public abstract void getTextBounds(int textId,
+ int start,
+ int end,
+ boolean monospace,
+ float[]bounds);
+
+ /**
+ * Draw a text starting ast x,y
+ *
+ * @param textId reference to the text
+ * @param start
+ * @param end
+ * @param contextStart
+ * @param contextEnd
+ * @param x
+ * @param y
+ * @param rtl
+ */
+ public abstract void drawTextRun(int textId,
int start,
int end,
int contextStart,
@@ -77,6 +105,14 @@
float y,
boolean rtl);
+ /**
+ * Draw an interpolation between two paths
+ * @param path1Id
+ * @param path2Id
+ * @param tween 0.0 = is path1 1.0 is path2
+ * @param start
+ * @param stop
+ */
public abstract void drawTweenPath(int path1Id,
int path2Id,
float tween,
@@ -85,21 +121,70 @@
public abstract void applyPaint(PaintBundle mPaintData);
- public abstract void mtrixScale(float scaleX, float scaleY, float centerX, float centerY);
+ /**
+ * Scale the rendering by scaleX and saleY (1.0 = no scale).
+ * Scaling is done about centerX,centerY.
+ *
+ * @param scaleX
+ * @param scaleY
+ * @param centerX
+ * @param centerY
+ */
+ public abstract void matrixScale(float scaleX, float scaleY, float centerX, float centerY);
+ /**
+ * Translate the rendering
+ * @param translateX
+ * @param translateY
+ */
public abstract void matrixTranslate(float translateX, float translateY);
+ /**
+ * Skew the rendering
+ * @param skewX
+ * @param skewY
+ */
public abstract void matrixSkew(float skewX, float skewY);
+ /**
+ * Rotate the rendering.
+ * Note rotates are cumulative.
+ * @param rotate angle to rotate
+ * @param pivotX x-coordinate about which to rotate
+ * @param pivotY y-coordinate about which to rotate
+ */
public abstract void matrixRotate(float rotate, float pivotX, float pivotY);
+ /**
+ * Save the current state of the transform
+ */
public abstract void matrixSave();
+ /**
+ * Restore the previously saved state of the transform
+ */
public abstract void matrixRestore();
+ /**
+ * Set the clip to a rectangle.
+ * Drawing outside the current clip region will have no effect
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
public abstract void clipRect(float left, float top, float right, float bottom);
+ /**
+ * Clip based on a path.
+ * @param pathId
+ * @param regionOp
+ */
public abstract void clipPath(int pathId, int regionOp);
+ /**
+ * Reset the paint
+ */
+ public abstract void reset();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index c2e8131..52fc314 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -19,6 +19,7 @@
import com.android.internal.widget.remotecompose.core.operations.ClickArea;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
import com.android.internal.widget.remotecompose.core.operations.ClipRect;
+import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
@@ -28,9 +29,12 @@
import com.android.internal.widget.remotecompose.core.operations.DrawPath;
import com.android.internal.widget.remotecompose.core.operations.DrawRect;
import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawText;
+import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
-import com.android.internal.widget.remotecompose.core.operations.DrawTextRun;
import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
+import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.Header;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
@@ -43,8 +47,12 @@
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
import com.android.internal.widget.remotecompose.core.operations.TextData;
+import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
+import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
import java.io.File;
import java.io.FileInputStream;
@@ -58,9 +66,20 @@
* Provides an abstract buffer to encode/decode RemoteCompose operations
*/
public class RemoteComposeBuffer {
+ public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD;
+ public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE;
+ public static final int EASING_CUBIC_DECELERATE = FloatAnimation.CUBIC_DECELERATE;
+ public static final int EASING_CUBIC_LINEAR = FloatAnimation.CUBIC_LINEAR;
+ public static final int EASING_CUBIC_ANTICIPATE = FloatAnimation.CUBIC_ANTICIPATE;
+ public static final int EASING_CUBIC_OVERSHOOT = FloatAnimation.CUBIC_OVERSHOOT;
+ public static final int EASING_CUBIC_CUSTOM = FloatAnimation.CUBIC_CUSTOM;
+ public static final int EASING_SPLINE_CUSTOM = FloatAnimation.SPLINE_CUSTOM;
+ public static final int EASING_EASE_OUT_BOUNCE = FloatAnimation.EASE_OUT_BOUNCE;
+ public static final int EASING_EASE_OUT_ELASTIC = FloatAnimation.EASE_OUT_ELASTIC;
WireBuffer mBuffer = new WireBuffer();
Platform mPlatform = null;
RemoteComposeState mRemoteComposeState;
+ private static final boolean DEBUG = false;
/**
* Provides an abstract buffer to encode/decode RemoteCompose operations
@@ -171,7 +190,7 @@
*
* @param text the string to inject in the buffer
*/
- int addText(String text) {
+ public int addText(String text) {
int id = mRemoteComposeState.dataGetId(text);
if (id == -1) {
id = mRemoteComposeState.cache(text);
@@ -350,7 +369,6 @@
addDrawPath(id);
}
-
/**
* Draw the specified path
*
@@ -426,12 +444,160 @@
float y,
boolean rtl) {
int textId = addText(text);
- DrawTextRun.COMPANION.apply(
+ DrawText.COMPANION.apply(
mBuffer, textId, start, end,
contextStart, contextEnd, x, y, rtl);
}
/**
+ * Draw the text, with origin at (x,y). The origin is interpreted
+ * based on the Align setting in the paint.
+ *
+ * @param textId The text to be drawn
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
+ * @param contextStart
+ * @param contextEnd
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
+ * @param rtl Draw RTTL
+ */
+ public void addDrawTextRun(int textId,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
+ DrawText.COMPANION.apply(
+ mBuffer, textId, start, end,
+ contextStart, contextEnd, x, y, rtl);
+ }
+
+ /**
+ * Draw a text on canvas at relative to position (x, y),
+ * offset panX and panY.
+ * <br>
+ * The panning factors (panX, panY) mapped to the
+ * resulting bounding box of the text, in such a way that a
+ * panning factor of (0.0, 0.0) would center the text at (x, y)
+ * <ul>
+ * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
+ * <li>Panning of 1.0, 1.0 - the text is below and to the left</li>
+ * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li>
+ * </ul>
+ * Setting panY to NaN results in y being the baseline of the text.
+ *
+ * @param text text to draw
+ * @param x Coordinate of the Anchor
+ * @param y Coordinate of the Anchor
+ * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+ * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+ * @param flags 1 = RTL
+ */
+ public void drawTextAnchored(String text,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
+ int textId = addText(text);
+ DrawTextAnchored.COMPANION.apply(
+ mBuffer, textId,
+ x, y,
+ panX, panY,
+ flags);
+ }
+
+ /**
+ * Add a text and id so that it can be used
+ *
+ * @param text
+ * @return
+ */
+ public int createTextId(String text) {
+ return addText(text);
+ }
+
+ /**
+ * Merge two text (from id's) output one id
+ * @param id1 left id
+ * @param id2 right id
+ * @return new id that merges the two text
+ */
+ public int textMerge(int id1, int id2) {
+ int textId = addText(id1 + "+" + id2);
+ TextMerge.COMPANION.apply(mBuffer, textId, id1, id2);
+ return textId;
+ }
+
+ public static final int PAD_AFTER_SPACE = TextFromFloat.PAD_AFTER_SPACE;
+ public static final int PAD_AFTER_NONE = TextFromFloat.PAD_AFTER_NONE;
+ public static final int PAD_AFTER_ZERO = TextFromFloat.PAD_AFTER_ZERO;
+ public static final int PAD_PRE_SPACE = TextFromFloat.PAD_PRE_SPACE;
+ public static final int PAD_PRE_NONE = TextFromFloat.PAD_PRE_NONE;
+ public static final int PAD_PRE_ZERO = TextFromFloat.PAD_PRE_ZERO;
+
+ /**
+ * Create a TextFromFloat command which creates text from a Float.
+ *
+ * @param value The value to convert
+ * @param digitsBefore the digits before the decimal point
+ * @param digitsAfter the digits after the decimal point
+ * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
+ * @return id of the string that can be passed to drawTextAnchored
+ */
+ public int createTextFromFloat(float value, short digitsBefore,
+ short digitsAfter, int flags) {
+ String placeHolder = Utils.floatToString(value)
+ + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")";
+ int id = mRemoteComposeState.dataGetId(placeHolder);
+ if (id == -1) {
+ id = mRemoteComposeState.cache(placeHolder);
+ // TextData.COMPANION.apply(mBuffer, id, text);
+ }
+ TextFromFloat.COMPANION.apply(mBuffer, id, value, digitsBefore,
+ digitsAfter, flags);
+ return id;
+ }
+
+ /**
+ * Draw a text on canvas at relative to position (x, y),
+ * offset panX and panY.
+ * <br>
+ * The panning factors (panX, panY) mapped to the
+ * resulting bounding box of the text, in such a way that a
+ * panning factor of (0.0, 0.0) would center the text at (x, y)
+ * <ul>
+ * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
+ * <li>Panning of 1.0, 1.0 - the text is below and to the left</li>
+ * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li>
+ * </ul>
+ * Setting panY to NaN results in y being the baseline of the text.
+ *
+ * @param textId text to draw
+ * @param x Coordinate of the Anchor
+ * @param y Coordinate of the Anchor
+ * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+ * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+ * @param flags 1 = RTL
+ */
+ public void drawTextAnchored(int textId,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
+
+ DrawTextAnchored.COMPANION.apply(
+ mBuffer, textId,
+ x, y,
+ panX, panY,
+ flags);
+ }
+
+ /**
* draw an interpolation between two paths that have the same pattern
* <p>
* Warning paths objects are not immutable and this is not taken into consideration
@@ -490,6 +656,10 @@
return id;
}
+ /**
+ * Adds a paint Bundle to the doc
+ * @param paint
+ */
public void addPaint(PaintBundle paint) {
PaintData.COMPANION.apply(mBuffer, paint);
}
@@ -499,7 +669,9 @@
mBuffer.setIndex(0);
while (mBuffer.available()) {
int opId = mBuffer.readByte();
- System.out.println(">>> " + opId);
+ if (DEBUG) {
+ Utils.log(">> " + opId);
+ }
CompanionOperation operation = Operations.map.get(opId);
if (operation == null) {
throw new RuntimeException("Unknown operation encountered " + opId);
@@ -519,7 +691,6 @@
Theme.COMPANION.apply(mBuffer, theme);
}
-
static String version() {
return "v1.0";
}
@@ -654,8 +825,8 @@
/**
* Add a pre-concat of the current matrix with the specified scale.
*
- * @param scaleX The amount to scale in X
- * @param scaleY The amount to scale in Y
+ * @param scaleX The amount to scale in X
+ * @param scaleY The amount to scale in Y
*/
public void addMatrixScale(float scaleX, float scaleY) {
MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, Float.NaN, Float.NaN);
@@ -673,12 +844,174 @@
MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, centerX, centerY);
}
+ /**
+ * sets the clip based on clip id
+ * @param pathId 0 clears the clip
+ */
public void addClipPath(int pathId) {
ClipPath.COMPANION.apply(mBuffer, pathId);
}
+ /**
+ * Sets the clip based on clip rec
+ * @param left
+ * @param top
+ * @param right
+ * @param bottom
+ */
public void addClipRect(float left, float top, float right, float bottom) {
ClipRect.COMPANION.apply(mBuffer, left, top, right, bottom);
}
+
+ /**
+ * Add a float return a NaN number pointing to that float
+ * @param value
+ * @return
+ */
+ public float addFloat(float value) {
+ int id = mRemoteComposeState.cacheFloat(value);
+ FloatConstant.COMPANION.apply(mBuffer, id, value);
+ return Utils.asNan(id);
+ }
+
+ /**
+ * Add a float that is a computation based on variables
+ * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
+ * @return NaN id of the result of the calculation
+ */
+ public float addAnimatedFloat(float... value) {
+ int id = mRemoteComposeState.cache(value);
+ FloatExpression.COMPANION.apply(mBuffer, id, value, null);
+ return Utils.asNan(id);
+ }
+
+ /**
+ * Add a float that is a computation based on variables.
+ * see packAnimation
+ * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
+ * @param animation Array of floats that represents animation
+ * @return NaN id of the result of the calculation
+ */
+ public float addAnimatedFloat(float[] value, float[] animation) {
+ int id = mRemoteComposeState.cache(value);
+ FloatExpression.COMPANION.apply(mBuffer, id, value, animation);
+ return Utils.asNan(id);
+ }
+
+ /**
+ * Add a color that represents the tween between two colors
+ * @param color1
+ * @param color2
+ * @param tween
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(int color1, int color2, float tween) {
+ ColorExpression c = new ColorExpression(0, 0, color1, color2, tween);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Add a color that represents the tween between two colors where color1
+ * is the id of a color
+ * @param color1
+ * @param color2
+ * @param tween
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(short color1, int color2, float tween) {
+ ColorExpression c = new ColorExpression(0, 1, color1, color2, tween);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Add a color that represents the tween between two colors where color2
+ * is the id of a color
+ * @param color1
+ * @param color2
+ * @param tween
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(int color1, short color2, float tween) {
+ ColorExpression c = new ColorExpression(0, 2, color1, color2, tween);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Add a color that represents the tween between two colors where color1 &
+ * color2 are the ids of colors
+ * @param color1
+ * @param color2
+ * @param tween
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(short color1, short color2, float tween) {
+ ColorExpression c = new ColorExpression(0, 3, color1, color2, tween);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Color calculated by Hue saturation and value.
+ * (as floats they can be variables used to create color transitions)
+ * @param hue
+ * @param sat
+ * @param value
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(float hue, float sat, float value) {
+ ColorExpression c = new ColorExpression(0, hue, sat, value);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * Color calculated by Alpha, Hue saturation and value.
+ * (as floats they can be variables used to create color transitions)
+ * @param alpha
+ * @param hue
+ * @param sat
+ * @param value
+ * @return id of the color (color ids are short)
+ */
+ public short addColorExpression(int alpha, float hue, float sat, float value) {
+ ColorExpression c = new ColorExpression(0, alpha, hue, sat, value);
+ short id = (short) mRemoteComposeState.cache(c);
+ c.mId = id;
+ c.write(mBuffer);
+ return id;
+ }
+
+ /**
+ * create and animation based on description and return as an array of
+ * floats. see addAnimatedFloat
+ * @param duration
+ * @param type
+ * @param spec
+ * @param initialValue
+ * @param wrap
+ * @return
+ */
+ public static float[] packAnimation(float duration,
+ int type,
+ float[] spec,
+ float initialValue,
+ float wrap) {
+
+ return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap);
+ }
+
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 17e8c83..66a37e67 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -15,26 +15,53 @@
*/
package com.android.internal.widget.remotecompose.core;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH;
+
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
+import java.util.ArrayList;
import java.util.HashMap;
/**
* Represents runtime state for a RemoteCompose document
+ * State includes things like the value of variables
*/
public class RemoteComposeState {
-
+ public static final int START_ID = 42;
+ private static final int MAX_FLOATS = 500;
private final IntMap<Object> mIntDataMap = new IntMap<>();
private final IntMap<Boolean> mIntWrittenMap = new IntMap<>();
private final HashMap<Object, Integer> mDataIntMap = new HashMap();
+ private final float[] mFloatMap = new float[MAX_FLOATS]; // efficient cache
+ private final int[] mColorMap = new int[MAX_FLOATS]; // efficient cache
+ private int mNextId = START_ID;
- private static int sNextId = 42;
+ {
+ for (int i = 0; i < mFloatMap.length; i++) {
+ mFloatMap[i] = Float.NaN;
+ }
+ }
- public Object getFromId(int id) {
+ /**
+ * Get Object based on id. The system will cache things like bitmaps
+ * Paths etc. They can be accessed with this command
+ * @param id
+ * @return
+ */
+ public Object getFromId(int id) {
return mIntDataMap.get(id);
}
- public boolean containsId(int id) {
+ /**
+ * true if the cache contain this id
+ * @param id
+ * @return
+ */
+ public boolean containsId(int id) {
return mIntDataMap.get(id) != null;
}
@@ -69,6 +96,65 @@
}
/**
+ * Insert an item in the cache
+ */
+ public void update(int id, Object item) {
+ mDataIntMap.remove(mIntDataMap.get(id));
+ mDataIntMap.put(item, id);
+ mIntDataMap.put(id, item);
+ }
+
+ /**
+ * Insert an item in the cache
+ */
+ public int cacheFloat(float item) {
+ int id = nextId();
+ mFloatMap[id] = item;
+ return id;
+ }
+
+ /**
+ * Insert an item in the cache
+ */
+ public void cacheFloat(int id, float item) {
+ mFloatMap[id] = item;
+ }
+
+ /**
+ * Insert an item in the cache
+ */
+ public void updateFloat(int id, float item) {
+ mFloatMap[id] = item;
+ }
+
+ /**
+ * get float
+ */
+ public float getFloat(int id) {
+ return mFloatMap[id];
+ }
+
+ /**
+ * Get the float value
+ *
+ * @param id
+ * @return
+ */
+ public int getColor(int id) {
+ return mColorMap[id];
+ }
+
+ /**
+ * Modify the color at id.
+ * @param id
+ * @param color
+ */
+ public void updateColor(int id, int color) {
+ mColorMap[id] = color;
+ }
+
+
+ /**
* Method to determine if a cached value has been written to the documents WireBuffer based on
* its id.
*/
@@ -79,22 +165,90 @@
/**
* Method to mark that a value, represented by its id, has been written to the WireBuffer
*/
- public void markWritten(int id) {
+ public void markWritten(int id) {
mIntWrittenMap.put(id, true);
}
/**
- * Clear the record of the values that have been written to the WireBuffer.
+ * Clear the record of the values that have been written to the WireBuffer.
*/
void reset() {
mIntWrittenMap.clear();
}
- public static int nextId() {
- return sNextId++;
+ /**
+ * Get the next available id
+ * @return
+ */
+ public int nextId() {
+ return mNextId++;
}
- public static void setNextId(int id) {
- sNextId = id;
+
+ /**
+ * Set the next id
+ * @param id
+ */
+ public void setNextId(int id) {
+ mNextId = id;
+ }
+
+ IntMap<ArrayList<VariableSupport>> mVarListeners = new IntMap<>();
+ ArrayList<VariableSupport> mAllVarListeners = new ArrayList<>();
+
+ private void add(int id, VariableSupport variableSupport) {
+ ArrayList<VariableSupport> v = mVarListeners.get(id);
+ if (v == null) {
+ v = new ArrayList<VariableSupport>();
+ mVarListeners.put(id, v);
+ }
+ v.add(variableSupport);
+ mAllVarListeners.add(variableSupport);
+ }
+
+ /**
+ * Commands that listen to variables add themselves.
+ * @param id
+ * @param variableSupport
+ */
+ public void listenToVar(int id, VariableSupport variableSupport) {
+ add(id, variableSupport);
+ }
+
+ /**
+ * List of Commands that need to be updated
+ * @param context
+ * @return
+ */
+ public int getOpsToUpdate(RemoteContext context) {
+ for (VariableSupport vs : mAllVarListeners) {
+ vs.updateVariables(context);
+ }
+ if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) {
+ return 1;
+ }
+ if (mVarListeners.get(ID_TIME_IN_SEC) != null) {
+ return 1000;
+ }
+ if (mVarListeners.get(ID_TIME_IN_MIN) != null) {
+ return 1000 * 60;
+ }
+ return -1;
+ }
+
+ /**
+ * Set the width of the overall document on screen.
+ * @param width
+ */
+ public void setWindowWidth(float width) {
+ updateFloat(ID_WINDOW_WIDTH, width);
+ }
+
+ /**
+ * Set the width of the overall document on screen.
+ * @param height
+ */
+ public void setWindowHeight(float height) {
+ updateFloat(ID_WINDOW_HEIGHT, height);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index d16cbc5..7e72168 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,10 @@
*/
package com.android.internal.widget.remotecompose.core;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
/**
* Specify an abstract context used to playback RemoteCompose documents
@@ -27,7 +30,7 @@
public abstract class RemoteContext {
protected CoreDocument mDocument;
public RemoteComposeState mRemoteComposeState;
-
+ long mStart = System.nanoTime(); // todo This should be set at a hi level
protected PaintContext mPaintContext = null;
ContextMode mMode = ContextMode.UNSET;
@@ -37,9 +40,40 @@
public float mWidth = 0f;
public float mHeight = 0f;
+ /**
+ * Load a path under an id.
+ * Paths can be use in clip drawPath and drawTweenPath
+ * @param instanceId
+ * @param floatPath
+ */
public abstract void loadPathData(int instanceId, float[] floatPath);
/**
+ * Associate a name with a give id.
+ *
+ * @param varName
+ * @param varId
+ * @param varType
+ */
+ public abstract void loadVariableName(String varName, int varId, int varType);
+
+ /**
+ * Save a color under a given id
+ * @param id
+ * @param color
+ */
+ public abstract void loadColor(int id, int color);
+
+ /**
+ * gets the time animation clock as float in seconds
+ * @return a monotonic time in seconds (arbitrary zero point)
+ */
+ public float getAnimationTime() {
+ return (System.nanoTime() - mStart) * 1E-9f;
+ }
+
+
+ /**
* The context can be used in a few different mode, allowing operations to skip being executed:
* - UNSET : all operations will get executed
* - DATA : only operations dealing with DATA (eg loading a bitmap) should execute
@@ -96,6 +130,8 @@
public void header(int majorVersion, int minorVersion, int patchVersion,
int width, int height, long capabilities
) {
+ mRemoteComposeState.setWindowWidth(width);
+ mRemoteComposeState.setWindowHeight(height);
mDocument.setVersion(majorVersion, minorVersion, patchVersion);
mDocument.setWidth(width);
mDocument.setHeight(height);
@@ -137,9 +173,105 @@
///////////////////////////////////////////////////////////////////////////////////////////////
// Data handling
///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Save a bitmap under an imageId
+ * @param imageId
+ * @param width
+ * @param height
+ * @param bitmap
+ */
public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap);
+
+ /**
+ * Save a string under a given id
+ * @param id
+ * @param text
+ */
public abstract void loadText(int id, String text);
+ /**
+ * Get a string given an id
+ * @param id
+ * @return
+ */
+ public abstract String getText(int id);
+
+ /**
+ * Load a float
+ * @param id
+ * @param value
+ */
+ public abstract void loadFloat(int id, float value);
+
+ /**
+ * Load an animated float associated with an id
+ * Todo: Remove?
+ * @param id
+ * @param animatedFloat
+ */
+ public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat);
+
+ /**
+ * Save a shader under and ID
+ * @param id
+ * @param value
+ */
+ public abstract void loadShader(int id, ShaderData value);
+
+ /**
+ * Get a float given an id
+ * @param id
+ * @return
+ */
+ public abstract float getFloat(int id);
+
+ /**
+ * Get the color given and ID
+ * @param id
+ * @return
+ */
+ public abstract int getColor(int id);
+
+ /**
+ * called to notify system that a command is interested in a variable
+ * @param id
+ * @param variableSupport
+ */
+ public abstract void listensTo(int id, VariableSupport variableSupport);
+
+ /**
+ * Notify commands with variables have changed
+ * @return
+ */
+ public abstract int updateOps();
+
+ /**
+ * Get a shader given the id
+ * @param id
+ * @return
+ */
+ public abstract ShaderData getShader(int id);
+
+ public static final int ID_CONTINUOUS_SEC = 1;
+ public static final int ID_TIME_IN_SEC = 2;
+ public static final int ID_TIME_IN_MIN = 3;
+ public static final int ID_TIME_IN_HR = 4;
+ public static final int ID_WINDOW_WIDTH = 5;
+ public static final int ID_WINDOW_HEIGHT = 6;
+ public static final int ID_COMPONENT_WIDTH = 7;
+ public static final int ID_COMPONENT_HEIGHT = 8;
+ public static final int ID_CALENDAR_MONTH = 9;
+
+ public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC);
+ public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC);
+ public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN);
+ public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR);
+ public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH);
+ public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH);
+ public static final float FLOAT_WINDOW_HEIGHT = Utils.asNan(ID_WINDOW_HEIGHT);
+ public static final float FLOAT_COMPONENT_WIDTH = Utils.asNan(ID_COMPONENT_WIDTH);
+ public static final float FLOAT_COMPONENT_HEIGHT = Utils.asNan(ID_COMPONENT_HEIGHT);
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
new file mode 100644
index 0000000..e9708b7
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core;
+
+import java.time.LocalDateTime;
+
+/**
+ * This generates the standard system variables for time.
+ */
+public class TimeVariables {
+ /**
+ * This class populates all time variables in the system
+ * @param context
+ */
+ public void updateTime(RemoteContext context) {
+ LocalDateTime dateTime = LocalDateTime.now();
+ // This define the time in the format
+ // seconds run from Midnight=0 quantized to seconds hour 0..3599
+ // minutes run from Midnight=0 quantized to minutes 0..1439
+ // hours run from Midnight=0 quantized to Hours 0-23
+ // CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600
+ // CONTINUOUS_SEC is accurate to milliseconds due to float precession
+ int month = dateTime.getDayOfMonth();
+ int hour = dateTime.getHour();
+ int minute = dateTime.getMinute();
+ int seconds = dateTime.getSecond();
+ int currentMinute = hour * 60 + minute;
+ int currentSeconds = minute * 60 + seconds;
+ float sec = currentSeconds + dateTime.getNano() * 1E-9f;
+
+ context.loadFloat(RemoteContext.ID_CONTINUOUS_SEC, sec);
+ context.loadFloat(RemoteContext.ID_TIME_IN_SEC, currentSeconds);
+ context.loadFloat(RemoteContext.ID_TIME_IN_MIN, currentMinute);
+ context.loadFloat(RemoteContext.ID_TIME_IN_HR, hour);
+ context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
+
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
new file mode 100644
index 0000000..d59b1bc
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core;
+
+/**
+ * Interface for operators that interact with variables
+ * Threw this they register to listen to particular variables
+ * and are notified when they change
+ */
+public interface VariableSupport {
+ /**
+ * Call to allow an operator to register interest in variables.
+ * Typically they call context.listensTo(id, this)
+ * @param context
+ */
+ void registerListening(RemoteContext context);
+
+ /**
+ * Called to be notified that the variables you are interested have changed.
+ * @param context
+ */
+ void updateVariables(RemoteContext context);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 76b7144..f186322 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -51,11 +51,12 @@
@Override
public String toString() {
- return "BITMAP DATA $imageId";
+ return "BITMAP DATA " + mImageId;
}
public static class Companion implements CompanionOperation {
- private Companion() {}
+ private Companion() {
+ }
@Override
public String name() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index 8d4a787..e6d5fe7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -24,6 +24,11 @@
import java.util.List;
+/**
+ * Defines a path that clips a the subsequent drawing commands
+ * Use MatrixSave and MatrixRestore commands to remove clip
+ * TODO allow id 0 to mean null?
+ */
public class ClipPath extends PaintOperation {
public static final Companion COMPANION = new Companion();
int mId;
@@ -93,5 +98,4 @@
public void paint(PaintContext context) {
context.clipPath(mId, mRegionOp);
}
-}
-
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
index 803618a..613eceb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
@@ -15,88 +15,36 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class ClipRect extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
+/**
+ * Support clip with a rectangle
+ */
+public class ClipRect extends DrawBase4 {
+ public static final Companion COMPANION =
+ new Companion(Operations.CLIP_RECT) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return new ClipRect(x1, y1, x2, y2);
+ }
+ };
public ClipRect(
float left,
float top,
float right,
float bottom) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
-
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
- }
-
- @Override
- public String toString() {
- return "ClipRect " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
-
- ClipRect op = new ClipRect(sLeft, srcTop, srcRight, srcBottom);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "ClipRect";
- }
-
- @Override
- public int id() {
- return Operations.CLIP_RECT;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom) {
- buffer.start(Operations.CLIP_RECT);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- }
+ super(left, top, right, bottom);
+ mName = "ClipRect";
}
@Override
public void paint(PaintContext context) {
- context.clipRect(mLeft,
- mTop,
- mRight,
- mBottom);
+ context.clipRect(mX1, mY1, mX2, mY2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
new file mode 100644
index 0000000..7d28cea
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to Colors
+ * Color modes
+ * mMode = 0 two colors and a tween
+ * mMode = 1 color1 is a colorID.
+ * mMode = 2 color2 is a colorID.
+ * mMode = 3 color1 & color2 are ids
+ * mMode = 4 H S V mode
+ */
+public class ColorExpression implements Operation, VariableSupport {
+ public int mId;
+ int mMode;
+ public int mColor1;
+ public int mColor2;
+ public float mTween = 0.0f;
+
+
+ public float mHue = 0; // only in Mode 4
+ public float mSat = 0;
+ public float mValue = 0;
+ public float mOutHue = 0; // only in Mode 4
+ public float mOutSat = 0;
+ public float mOutValue = 0;
+ public int mAlpha = 0xFF; // only used in hsv mode
+
+ public float mOutTween = 0.0f;
+ public int mOutColor1;
+ public int mOutColor2;
+ public static final Companion COMPANION = new Companion();
+ public static final int HSV_MODE = 4;
+ public ColorExpression(int id, float hue, float sat, float value) {
+ mMode = HSV_MODE;
+ mAlpha = 0xFF;
+ mOutHue = mHue = hue;
+ mOutSat = mSat = sat;
+ mOutValue = mValue = value;
+ mColor1 = Float.floatToRawIntBits(hue);
+ mColor2 = Float.floatToRawIntBits(sat);
+ mTween = value;
+ }
+ public ColorExpression(int id, int alpha, float hue, float sat, float value) {
+ mMode = HSV_MODE;
+ mAlpha = alpha;
+ mOutHue = mHue = hue;
+ mOutSat = mSat = sat;
+ mOutValue = mValue = value;
+ mColor1 = Float.floatToRawIntBits(hue);
+ mColor2 = Float.floatToRawIntBits(sat);
+ mTween = value;
+ }
+
+ public ColorExpression(int id, int mode, int color1, int color2, float tween) {
+ this.mId = id;
+ this.mMode = mode & 0xFF;
+ this.mAlpha = (mode >> 16) & 0xFF;
+ if (mMode == HSV_MODE) {
+ mOutHue = mHue = Float.intBitsToFloat(color1);
+ mOutSat = mSat = Float.intBitsToFloat(color2);
+ mOutValue = mValue = tween;
+ }
+ this.mColor1 = color1;
+ this.mColor2 = color2;
+ this.mTween = tween;
+ this.mOutTween = tween;
+ this.mOutColor1 = color1;
+ this.mOutColor2 = color2;
+
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (mMode == 4) {
+ if (Float.isNaN(mHue)) {
+ mOutHue = context.getFloat(Utils.idFromNan(mHue));
+ }
+ if (Float.isNaN(mSat)) {
+ mOutSat = context.getFloat(Utils.idFromNan(mSat));
+ }
+ if (Float.isNaN(mValue)) {
+ mOutValue = context.getFloat(Utils.idFromNan(mValue));
+ }
+ }
+ if (Float.isNaN(mTween)) {
+ mOutTween = context.getFloat(Utils.idFromNan(mTween));
+ }
+ if ((mMode & 1) == 1) {
+ mOutColor1 = context.getColor(mColor1);
+ }
+ if ((mMode & 2) == 2) {
+ mOutColor2 = context.getColor(mColor2);
+ }
+ }
+
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (mMode == 4) {
+ if (Float.isNaN(mHue)) {
+ context.listensTo(Utils.idFromNan(mHue), this);
+ }
+ if (Float.isNaN(mSat)) {
+ context.listensTo(Utils.idFromNan(mSat), this);
+ }
+ if (Float.isNaN(mValue)) {
+ context.listensTo(Utils.idFromNan(mValue), this);
+ }
+ return;
+ }
+ if (Float.isNaN(mTween)) {
+ context.listensTo(Utils.idFromNan(mTween), this);
+ }
+ if ((mMode & 1) == 1) {
+ context.listensTo(mColor1, this);
+ }
+ if ((mMode & 2) == 2) {
+ context.listensTo(mColor2, this);
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ if (mMode == 4) {
+ context.loadColor(mId, (mAlpha << 24)
+ | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
+ return;
+ }
+ if (mOutTween == 0.0) {
+ context.loadColor(mId, mColor1);
+ } else {
+ if ((mMode & 1) == 1) {
+ mOutColor1 = context.getColor(mColor1);
+ }
+ if ((mMode & 2) == 2) {
+ mOutColor2 = context.getColor(mColor2);
+ }
+
+ context.loadColor(mId,
+ Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
+ }
+
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ int mode = mMode | (mAlpha << 16);
+ COMPANION.apply(buffer, mId, mode, mColor1, mColor2, mTween);
+ }
+
+ @Override
+ public String toString() {
+ if (mMode == 4) {
+ return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue)
+ + ", " + Utils.floatToString(mSat)
+ + ", " + Utils.floatToString(mValue) + ")";
+ }
+
+ String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
+ String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
+ return "ColorExpression[" + mId + "] = tween(" + c1
+ + ", " + c2 + ", "
+ + Utils.floatToString(mTween) + ")";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "ColorExpression";
+ }
+
+ @Override
+ public int id() {
+ return Operations.COLOR_EXPRESSIONS;
+ }
+
+ /**
+ * Call to write a ColorExpression object on the buffer
+ * @param buffer
+ * @param id of the ColorExpression object
+ * @param mode if colors are id or actual values
+ * @param color1
+ * @param color2
+ * @param tween
+ */
+ public void apply(WireBuffer buffer,
+ int id, int mode,
+ int color1, int color2, float tween) {
+ buffer.start(Operations.COLOR_EXPRESSIONS);
+ buffer.writeInt(id);
+ buffer.writeInt(mode);
+ buffer.writeInt(color1);
+ buffer.writeInt(color2);
+ buffer.writeFloat(tween);
+
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int mode = buffer.readInt();
+ int color1 = buffer.readInt();
+ int color2 = buffer.readInt();
+ float tween = buffer.readFloat();
+
+ operations.add(new ColorExpression(id, mode, color1, color2, tween));
+ }
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
index e829975..c176864 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
@@ -15,107 +15,36 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
+public class DrawArc extends DrawBase6 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_ARC) {
+ @Override
+ public Operation construct(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ return new DrawArc(v1, v2, v3, v4, v5, v6);
+ }
+ };
-public class DrawArc extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
- float mStartAngle;
- float mSweepAngle;
-
- public DrawArc(
- float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- mStartAngle = startAngle;
- mSweepAngle = sweepAngle;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft,
- mTop,
- mRight,
- mBottom,
- mStartAngle,
- mSweepAngle);
- }
-
- @Override
- public String toString() {
- return "DrawArc " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + " "
- + "- " + mStartAngle + " " + mSweepAngle + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
- float mStartAngle = buffer.readFloat();
- float mSweepAngle = buffer.readFloat();
- DrawArc op = new DrawArc(sLeft, srcTop, srcRight, srcBottom,
- mStartAngle, mSweepAngle);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawArc";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_ARC;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle) {
- buffer.start(Operations.DRAW_ARC);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- buffer.writeFloat(startAngle);
- buffer.writeFloat(sweepAngle);
- }
+ public DrawArc(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ super(v1, v2, v3, v4, v5, v6);
+ mName = "DrawArc";
}
@Override
public void paint(PaintContext context) {
- context.drawArc(mLeft,
- mTop,
- mRight,
- mBottom,
- mStartAngle,
- mSweepAngle);
+ context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
new file mode 100644
index 0000000..0963c13
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for commands that take 3 float
+ */
+public abstract class DrawBase2 extends PaintOperation
+ implements VariableSupport {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_CIRCLE) {
+ @Override
+ public Operation construct(float x1, float y1) {
+ // subclass should return new DrawX(x1, y1);
+ return null;
+ }
+ };
+ protected String mName = "DrawRectBase";
+ float mV1;
+ float mV2;
+ float mValue1;
+ float mValue2;
+
+ public DrawBase2(float v1, float v2) {
+ mValue1 = v1;
+ mValue2 = v2;
+ mV1 = v1;
+ mV2 = v2;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mV1 = (Float.isNaN(mValue1))
+ ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = (Float.isNaN(mValue2))
+ ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mValue1)) {
+ context.listensTo(Utils.idFromNan(mValue1), this);
+ }
+ if (Float.isNaN(mValue2)) {
+ context.listensTo(Utils.idFromNan(mValue2), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mV1, mV2);
+ }
+
+ @Override
+ public String toString() {
+ return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
+ }
+
+ public static class Companion implements CompanionOperation {
+ public final int OP_CODE;
+
+ protected Companion(int code) {
+ OP_CODE = code;
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ float v1 = buffer.readFloat();
+ float v2 = buffer.readFloat();
+
+ Operation op = construct(v1, v2);
+ operations.add(op);
+ }
+
+ /**
+ * Override to construct a 2 float value operation
+ * @param x1
+ * @param y1
+ * @return
+ */
+ public Operation construct(float x1, float y1) {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return "DrawRect";
+ }
+
+ @Override
+ public int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param x1
+ * @param y1
+ */
+ public void apply(WireBuffer buffer,
+ float x1,
+ float y1) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x1);
+ buffer.writeFloat(y1);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
new file mode 100644
index 0000000..56b2f1f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for commands that take 3 float
+ */
+public abstract class DrawBase3 extends PaintOperation
+ implements VariableSupport {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_CIRCLE) {
+ @Override
+ public Operation construct(float x1, float y1, float x2) {
+ // subclass should return new DrawX(x1, y1, x2, y2);
+ return null;
+ }
+ };
+ protected String mName = "DrawRectBase";
+ float mV1;
+ float mV2;
+ float mV3;
+ float mValue1;
+ float mValue2;
+ float mValue3;
+
+ public DrawBase3(
+ float v1,
+ float v2,
+ float v3) {
+ mValue1 = v1;
+ mValue2 = v2;
+ mValue3 = v3;
+
+ mV1 = v1;
+ mV2 = v2;
+ mV3 = v3;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mV1 = (Float.isNaN(mValue1))
+ ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = (Float.isNaN(mValue2))
+ ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV3 = (Float.isNaN(mValue3))
+ ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mValue1)) {
+ context.listensTo(Utils.idFromNan(mValue1), this);
+ }
+ if (Float.isNaN(mValue2)) {
+ context.listensTo(Utils.idFromNan(mValue2), this);
+ }
+ if (Float.isNaN(mValue3)) {
+ context.listensTo(Utils.idFromNan(mValue3), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mV1, mV2, mV3);
+ }
+
+ @Override
+ public String toString() {
+ return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
+ + " " + floatToString(mV3);
+ }
+
+ public static class Companion implements CompanionOperation {
+ public final int OP_CODE;
+
+ protected Companion(int code) {
+ OP_CODE = code;
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ float v1 = buffer.readFloat();
+ float v2 = buffer.readFloat();
+ float v3 = buffer.readFloat();
+
+ Operation op = construct(v1, v2, v3);
+ operations.add(op);
+ }
+
+ /**
+ * Construct and Operation from the 3 variables.
+ * This must be overridden by subclasses
+ * @param x1
+ * @param y1
+ * @param x2
+ * @return
+ */
+ public Operation construct(float x1,
+ float y1,
+ float x2) {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return "DrawRect";
+ }
+
+ @Override
+ public int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param x1
+ * @param y1
+ * @param x2
+ */
+ public void apply(WireBuffer buffer,
+ float x1,
+ float y1,
+ float x2) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x1);
+ buffer.writeFloat(y1);
+ buffer.writeFloat(x2);
+
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
new file mode 100644
index 0000000..ec35a16
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for draw commands that take 4 floats
+ */
+public abstract class DrawBase4 extends PaintOperation
+ implements VariableSupport {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_RECT) {
+ @Override
+ public Operation construct(float x1, float y1, float x2, float y2) {
+ // return new DrawRectBase(x1, y1, x2, y2);
+ return null;
+ }
+ };
+ protected String mName = "DrawRectBase";
+ float mX1;
+ float mY1;
+ float mX2;
+ float mY2;
+ float mX1Value;
+ float mY1Value;
+ float mX2Value;
+ float mY2Value;
+
+ public DrawBase4(
+ float x1,
+ float y1,
+ float x2,
+ float y2) {
+ mX1Value = x1;
+ mY1Value = y1;
+ mX2Value = x2;
+ mY2Value = y2;
+
+ mX1 = x1;
+ mY1 = y1;
+ mX2 = x2;
+ mY2 = y2;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mX1 = (Float.isNaN(mX1Value))
+ ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
+ mY1 = (Float.isNaN(mY1Value))
+ ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
+ mX2 = (Float.isNaN(mX2Value))
+ ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
+ mY2 = (Float.isNaN(mY2Value))
+ ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mX1Value)) {
+ context.listensTo(Utils.idFromNan(mX1Value), this);
+ }
+ if (Float.isNaN(mY1Value)) {
+ context.listensTo(Utils.idFromNan(mY1Value), this);
+ }
+ if (Float.isNaN(mX2Value)) {
+ context.listensTo(Utils.idFromNan(mX2Value), this);
+ }
+ if (Float.isNaN(mY2Value)) {
+ context.listensTo(Utils.idFromNan(mY2Value), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mX1, mY1, mX2, mY2);
+ }
+
+ @Override
+ public String toString() {
+ return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1)
+ + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2);
+ }
+
+ public static class Companion implements CompanionOperation {
+ public final int OP_CODE;
+
+ protected Companion(int code) {
+ OP_CODE = code;
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ float sLeft = buffer.readFloat();
+ float srcTop = buffer.readFloat();
+ float srcRight = buffer.readFloat();
+ float srcBottom = buffer.readFloat();
+
+ Operation op = construct(sLeft, srcTop, srcRight, srcBottom);
+ operations.add(op);
+ }
+
+ /**
+ * Construct and Operation from the 3 variables.
+ * @param x1
+ * @param y1
+ * @param x2
+ * @param y2
+ * @return
+ */
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return "DrawRect";
+ }
+
+ @Override
+ public int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param x1
+ * @param y1
+ * @param x2
+ * @param y2
+ */
+ public void apply(WireBuffer buffer,
+ float x1,
+ float y1,
+ float x2,
+ float y2) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(x1);
+ buffer.writeFloat(y1);
+ buffer.writeFloat(x2);
+ buffer.writeFloat(y2);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
new file mode 100644
index 0000000..2f4335e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for draw commands the take 6 floats
+ */
+public abstract class DrawBase6 extends PaintOperation
+ implements VariableSupport {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_RECT) {
+ public Operation construct(float x1, float y1, float x2, float y2) {
+ // return new DrawRectBase(x1, y1, x2, y2);
+ return null;
+ }
+ };
+ protected String mName = "DrawRectBase";
+ float mV1;
+ float mV2;
+ float mV3;
+ float mV4;
+ float mV5;
+ float mV6;
+ float mValue1;
+ float mValue2;
+ float mValue3;
+ float mValue4;
+ float mValue5;
+ float mValue6;
+
+ public DrawBase6(
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ mValue1 = v1;
+ mValue2 = v2;
+ mValue3 = v3;
+ mValue4 = v4;
+ mValue5 = v5;
+ mValue6 = v6;
+
+ mV1 = v1;
+ mV2 = v2;
+ mV3 = v3;
+ mV4 = v4;
+ mV5 = v5;
+ mV6 = v6;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mV1 = (Float.isNaN(mValue1))
+ ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = (Float.isNaN(mValue2))
+ ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV3 = (Float.isNaN(mValue3))
+ ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+ mV4 = (Float.isNaN(mValue4))
+ ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
+ mV5 = (Float.isNaN(mValue5))
+ ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
+ mV6 = (Float.isNaN(mValue6))
+ ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mValue1)) {
+ context.listensTo(Utils.idFromNan(mValue1), this);
+ }
+ if (Float.isNaN(mValue2)) {
+ context.listensTo(Utils.idFromNan(mValue2), this);
+ }
+ if (Float.isNaN(mValue3)) {
+ context.listensTo(Utils.idFromNan(mValue3), this);
+ }
+ if (Float.isNaN(mValue4)) {
+ context.listensTo(Utils.idFromNan(mValue4), this);
+ }
+ if (Float.isNaN(mValue5)) {
+ context.listensTo(Utils.idFromNan(mValue5), this);
+ }
+ if (Float.isNaN(mValue6)) {
+ context.listensTo(Utils.idFromNan(mValue6), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mV1, mV2, mV3, mV4, mV5, mV6);
+ }
+
+ @Override
+ public String toString() {
+ return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
+ + " " + floatToString(mV3) + " " + floatToString(mV4);
+ }
+
+ public static class Companion implements CompanionOperation {
+ public final int OP_CODE;
+
+ protected Companion(int code) {
+ OP_CODE = code;
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ float sv1 = buffer.readFloat();
+ float sv2 = buffer.readFloat();
+ float sv3 = buffer.readFloat();
+ float sv4 = buffer.readFloat();
+ float sv5 = buffer.readFloat();
+ float sv6 = buffer.readFloat();
+
+ Operation op = construct(sv1, sv2, sv3, sv4, sv5, sv6);
+ operations.add(op);
+ }
+
+ /**
+ * writes out a the operation to the buffer.
+ * @param v1
+ * @param v2
+ * @param v3
+ * @param v4
+ * @param v5
+ * @param v6
+ * @return
+ */
+ public Operation construct(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ return null;
+ }
+
+ @Override
+ public String name() {
+ return "DrawRect";
+ }
+
+ @Override
+ public int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param v1
+ * @param v2
+ * @param v3
+ * @param v4
+ * @param v5
+ * @param v6
+ */
+ public void apply(WireBuffer buffer,
+ float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(v1);
+ buffer.writeFloat(v2);
+ buffer.writeFloat(v3);
+ buffer.writeFloat(v4);
+ buffer.writeFloat(v5);
+ buffer.writeFloat(v6);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index 2e971f5..ca40d12 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -20,16 +20,22 @@
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import java.util.List;
-public class DrawBitmap extends PaintOperation {
+public class DrawBitmap extends PaintOperation implements VariableSupport {
public static final Companion COMPANION = new Companion();
float mLeft;
float mTop;
float mRight;
float mBottom;
+ float mOutputLeft;
+ float mOutputTop;
+ float mOutputRight;
+ float mOutputBottom;
int mId;
int mDescriptionId = 0;
@@ -49,6 +55,34 @@
}
@Override
+ public void updateVariables(RemoteContext context) {
+ mOutputLeft = (Float.isNaN(mLeft))
+ ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
+ mOutputTop = (Float.isNaN(mTop))
+ ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
+ mOutputRight = (Float.isNaN(mRight))
+ ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
+ mOutputBottom = (Float.isNaN(mBottom))
+ ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mLeft)) {
+ context.listensTo(Utils.idFromNan(mLeft), this);
+ }
+ if (Float.isNaN(mTop)) {
+ context.listensTo(Utils.idFromNan(mTop), this);
+ }
+ if (Float.isNaN(mRight)) {
+ context.listensTo(Utils.idFromNan(mRight), this);
+ }
+ if (Float.isNaN(mBottom)) {
+ context.listensTo(Utils.idFromNan(mBottom), this);
+ }
+ }
+
+ @Override
public void write(WireBuffer buffer) {
COMPANION.apply(buffer, mId, mLeft, mTop, mRight, mBottom, mDescriptionId);
}
@@ -105,9 +139,9 @@
@Override
public void paint(PaintContext context) {
- context.drawBitmap(mId, mLeft,
- mTop,
- mRight,
- mBottom);
+ context.drawBitmap(mId, mOutputLeft,
+ mOutputTop,
+ mOutputRight,
+ mOutputBottom);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
index 9ce754d..3a22e4f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
@@ -1,89 +1,31 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
+public class DrawCircle extends DrawBase3 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_CIRCLE) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2
+ ) {
+ return new DrawCircle(x1, y1, x2);
+ }
+ };
-public class DrawCircle extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mCenterX;
- float mCenterY;
- float mRadius;
-
- public DrawCircle(float centerX, float centerY, float radius) {
- mCenterX = centerX;
- mCenterY = centerY;
- mRadius = radius;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mCenterX,
- mCenterY,
- mRadius);
- }
-
- @Override
- public String toString() {
- return "";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float centerX = buffer.readFloat();
- float centerY = buffer.readFloat();
- float radius = buffer.readFloat();
-
- DrawCircle op = new DrawCircle(centerX, centerY, radius);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "";
- }
-
- @Override
- public int id() {
- return 0;
- }
-
- public void apply(WireBuffer buffer, float centerX, float centerY, float radius) {
- buffer.start(Operations.DRAW_CIRCLE);
- buffer.writeFloat(centerX);
- buffer.writeFloat(centerY);
- buffer.writeFloat(radius);
- }
+ public DrawCircle(
+ float left,
+ float top,
+ float right) {
+ super(left, top, right);
+ mName = "DrawCircle";
}
@Override
public void paint(PaintContext context) {
- context.drawCircle(mCenterX,
- mCenterY,
- mRadius);
+ context.drawCircle(mV1, mV2, mV3);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
index c7a8315..c70c6ea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
@@ -15,83 +15,28 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class DrawLine extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mX1;
- float mY1;
- float mX2;
- float mY2;
+public class DrawLine extends DrawBase4 {
+ public static final Companion COMPANION = new Companion(Operations.DRAW_LINE) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return new DrawLine(x1, y1, x2, y2);
+ }
+ };
public DrawLine(
- float x1,
- float y1,
- float x2,
- float y2) {
- mX1 = x1;
- mY1 = y1;
- mX2 = x2;
- mY2 = y2;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mX1,
- mY1,
- mX2,
- mY2);
- }
-
- @Override
- public String toString() {
- return "DrawArc " + mX1 + " " + mY1
- + " " + mX2 + " " + mY2 + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float x1 = buffer.readFloat();
- float y1 = buffer.readFloat();
- float x2 = buffer.readFloat();
- float y2 = buffer.readFloat();
-
- DrawLine op = new DrawLine(x1, y1, x2, y2);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawLine";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_LINE;
- }
-
- public void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
- buffer.start(Operations.DRAW_LINE);
- buffer.writeFloat(x1);
- buffer.writeFloat(y1);
- buffer.writeFloat(x2);
- buffer.writeFloat(y2);
- }
+ float left,
+ float top,
+ float right,
+ float bottom) {
+ super(left, top, right, bottom);
+ mName = "DrawLine";
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
index 7143753..ba17994 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
@@ -15,88 +15,33 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class DrawOval extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
-
+public class DrawOval extends DrawBase4 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_OVAL) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return new DrawOval(x1, y1, x2, y2);
+ }
+ };
public DrawOval(
float left,
float top,
float right,
float bottom) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
- }
-
- @Override
- public String toString() {
- return "DrawOval " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
-
- DrawOval op = new DrawOval(sLeft, srcTop, srcRight, srcBottom);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawOval";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_OVAL;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom) {
- buffer.start(Operations.DRAW_OVAL);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- }
+ super(left, top, right, bottom);
+ mName = "DrawOval";
}
@Override
public void paint(PaintContext context) {
- context.drawOval(mLeft,
- mTop,
- mRight,
- mBottom);
+ context.drawOval(mX1, mY1, mX2, mY2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index 7b8a9e9..6dbc5a6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -41,7 +41,7 @@
@Override
public String toString() {
- return "DrawPath " + ";";
+ return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd;
}
public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
index 4775241..633aed4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
@@ -15,88 +15,37 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class DrawRect extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
+/**
+ * Draw a Rectangle
+ */
+public class DrawRect extends DrawBase4 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_RECT) {
+ @Override
+ public Operation construct(float x1,
+ float y1,
+ float x2,
+ float y2) {
+ return new DrawRect(x1, y1, x2, y2);
+ }
+ };
public DrawRect(
float left,
float top,
float right,
float bottom) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
- }
-
- @Override
- public String toString() {
- return "DrawRect " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
-
- DrawRect op = new DrawRect(sLeft, srcTop, srcRight, srcBottom);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawRect";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_RECT;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom) {
- buffer.start(Operations.DRAW_RECT);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- }
+ super(left, top, right, bottom);
+ mName = "DrawRect";
}
@Override
public void paint(PaintContext context) {
- context.drawRect(mLeft,
- mTop,
- mRight,
- mBottom);
+ context.drawRect(mX1, mY1, mX2, mY2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index 8da16e7..b9d0a67 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -15,104 +15,40 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
+/**
+ * Draw a rounded rectangle
+ */
+public class DrawRoundRect extends DrawBase6 {
+ public static final Companion COMPANION =
+ new Companion(Operations.DRAW_ROUND_RECT) {
+ @Override
+ public Operation construct(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ return new DrawRoundRect(v1, v2, v3, v4, v5, v6);
+ }
+ };
-public class DrawRoundRect extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mLeft;
- float mTop;
- float mRight;
- float mBottom;
- float mRadiusX;
- float mRadiusY;
-
- public DrawRoundRect(
- float left,
- float top,
- float right,
- float bottom,
- float radiusX,
- float radiusY) {
- mLeft = left;
- mTop = top;
- mRight = right;
- mBottom = bottom;
- mRadiusX = radiusX;
- mRadiusY = radiusY;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom, mRadiusX, mRadiusY);
- }
-
- @Override
- public String toString() {
- return "DrawRoundRect " + mLeft + " " + mTop
- + " " + mRight + " " + mBottom
- + " (" + mRadiusX + " " + mRadiusY + ");";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float sLeft = buffer.readFloat();
- float srcTop = buffer.readFloat();
- float srcRight = buffer.readFloat();
- float srcBottom = buffer.readFloat();
- float srcRadiusX = buffer.readFloat();
- float srcRadiusY = buffer.readFloat();
-
- DrawRoundRect op = new DrawRoundRect(sLeft, srcTop, srcRight,
- srcBottom, srcRadiusX, srcRadiusY);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "DrawOval";
- }
-
- @Override
- public int id() {
- return Operations.DRAW_ROUND_RECT;
- }
-
- public void apply(WireBuffer buffer,
- float left,
- float top,
- float right,
- float bottom,
- float radiusX,
- float radiusY) {
- buffer.start(Operations.DRAW_ROUND_RECT);
- buffer.writeFloat(left);
- buffer.writeFloat(top);
- buffer.writeFloat(right);
- buffer.writeFloat(bottom);
- buffer.writeFloat(radiusX);
- buffer.writeFloat(radiusY);
- }
+ public DrawRoundRect(float v1,
+ float v2,
+ float v3,
+ float v4,
+ float v5,
+ float v6) {
+ super(v1, v2, v3, v4, v5, v6);
+ mName = "ClipRect";
}
@Override
public void paint(PaintContext context) {
- context.drawRoundRect(mLeft,
- mTop,
- mRight,
- mBottom,
- mRadiusX,
- mRadiusY
+ context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6
);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
new file mode 100644
index 0000000..f8f8afd
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Draw Text
+ */
+public class DrawText extends PaintOperation {
+ public static final Companion COMPANION = new Companion();
+ int mTextID;
+ int mStart = 0;
+ int mEnd = 0;
+ int mContextStart = 0;
+ int mContextEnd = 0;
+ float mX = 0f;
+ float mY = 0f;
+ boolean mRtl = false;
+
+ public DrawText(int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
+ mTextID = textID;
+ mStart = start;
+ mEnd = end;
+ mContextStart = contextStart;
+ mContextEnd = contextEnd;
+ mX = x;
+ mY = y;
+ mRtl = rtl;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
+
+ }
+
+ @Override
+ public String toString() {
+ return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", " + mX + ", " + mY;
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int text = buffer.readInt();
+ int start = buffer.readInt();
+ int end = buffer.readInt();
+ int contextStart = buffer.readInt();
+ int contextEnd = buffer.readInt();
+ float x = buffer.readFloat();
+ float y = buffer.readFloat();
+ boolean rtl = buffer.readBoolean();
+ DrawText op = new DrawText(text, start, end, contextStart, contextEnd, x, y, rtl);
+
+ operations.add(op);
+ }
+
+ @Override
+ public String name() {
+ return "";
+ }
+
+ @Override
+ public int id() {
+ return 0;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textID
+ * @param start
+ * @param end
+ * @param contextStart
+ * @param contextEnd
+ * @param x
+ * @param y
+ * @param rtl
+ */
+ public void apply(WireBuffer buffer,
+ int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
+ buffer.start(Operations.DRAW_TEXT_RUN);
+ buffer.writeInt(textID);
+ buffer.writeInt(start);
+ buffer.writeInt(end);
+ buffer.writeInt(contextStart);
+ buffer.writeInt(contextEnd);
+ buffer.writeFloat(x);
+ buffer.writeFloat(y);
+ buffer.writeBoolean(rtl);
+ }
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
new file mode 100644
index 0000000..4f0641f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Draw Text in Anchored to a point
+ */
+public class DrawTextAnchored extends PaintOperation implements VariableSupport {
+ public static final Companion COMPANION = new Companion();
+ int mTextID;
+ float mX;
+ float mY;
+ float mPanX;
+ float mPanY;
+ int mFlags;
+ float mOutX;
+ float mOutY;
+ float mOutPanX;
+ float mOutPanY;
+
+ public static final int ANCHOR_TEXT_RTL = 1;
+ public static final int ANCHOR_MONOSPACE_MEASURE = 2;
+
+ public DrawTextAnchored(int textID,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
+ mTextID = textID;
+ mX = x;
+ mY = y;
+ mOutX = mX;
+ mOutY = mY;
+ mFlags = flags;
+ mOutPanX = mPanX = panX;
+ mOutPanY = mPanY = panY;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mOutX = (Float.isNaN(mX))
+ ? context.getFloat(Utils.idFromNan(mX)) : mX;
+ mOutY = (Float.isNaN(mY))
+ ? context.getFloat(Utils.idFromNan(mY)) : mY;
+ mOutPanX = (Float.isNaN(mPanX))
+ ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
+ mOutPanY = (Float.isNaN(mPanY))
+ ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mX)) {
+ context.listensTo(Utils.idFromNan(mX), this);
+ }
+ if (Float.isNaN(mY)) {
+ context.listensTo(Utils.idFromNan(mY), this);
+ }
+ if (Float.isNaN(mPanX)) {
+ context.listensTo(Utils.idFromNan(mPanX), this);
+ }
+ if (Float.isNaN(mPanY) && Utils.idFromNan(mPanY) > 0) {
+ context.listensTo(Utils.idFromNan(mPanY), this);
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextID, mX,
+ mY,
+ mPanX,
+ mPanY,
+ mFlags);
+ }
+
+ @Override
+ public String toString() {
+ return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", "
+ + floatToStr(mY) + ", "
+ + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", "
+ + Integer.toBinaryString(mFlags);
+ }
+
+ private static String floatToStr(float v) {
+ if (Float.isNaN(v)) {
+ return "[" + Utils.idFromNan(v) + "]";
+ }
+ return Float.toString(v);
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int textID = buffer.readInt();
+ float x = buffer.readFloat();
+ float y = buffer.readFloat();
+ float panX = buffer.readFloat();
+ float panY = buffer.readFloat();
+ int flags = buffer.readInt();
+
+ DrawTextAnchored op = new DrawTextAnchored(textID,
+ x, y,
+ panX, panY,
+ flags);
+
+ operations.add(op);
+ }
+
+ @Override
+ public String name() {
+ return "";
+ }
+
+ @Override
+ public int id() {
+ return 0;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textID
+ * @param x
+ * @param y
+ * @param panX
+ * @param panY
+ * @param flags
+ */
+ public void apply(WireBuffer buffer,
+ int textID,
+ float x,
+ float y,
+ float panX,
+ float panY,
+ int flags) {
+ buffer.start(Operations.DRAW_TEXT_ANCHOR);
+ buffer.writeInt(textID);
+ buffer.writeFloat(x);
+ buffer.writeFloat(y);
+ buffer.writeFloat(panX);
+ buffer.writeFloat(panY);
+ buffer.writeInt(flags);
+ }
+ }
+
+ float[] mBounds = new float[4];
+
+ private float getHorizontalOffset() {
+ // TODO scale TextSize / BaseTextSize;
+ float scale = 1.0f;
+
+ float textWidth = scale * (mBounds[2] - mBounds[0]);
+ float boxWidth = 0;
+ return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f
+ - (scale * mBounds[0]);
+ }
+
+ private float getVerticalOffset() {
+ // TODO scale TextSize / BaseTextSize;
+ float scale = 1.0f;
+ float boxHeight = 0;
+ float textHeight = scale * (mBounds[3] - mBounds[1]);
+ return (boxHeight - textHeight) * (1 - mOutPanY) / 2
+ - (scale * mBounds[1]);
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ context.getTextBounds(mTextID, 0, -1,
+ (mFlags & ANCHOR_MONOSPACE_MEASURE) != 0, mBounds);
+ float x = mOutX + getHorizontalOffset();
+ float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset();
+ context.drawTextRun(mTextID, 0, -1, 0, 1, x, y,
+ (mFlags & ANCHOR_TEXT_RTL) == 1);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index 1856e30..b1a0172 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -24,6 +24,9 @@
import java.util.List;
+/**
+ * Draw text along a path.
+ */
public class DrawTextOnPath extends PaintOperation {
public static final Companion COMPANION = new Companion();
int mPathId;
@@ -45,7 +48,8 @@
@Override
public String toString() {
- return "DrawTextOnPath " + " " + mPathId + ";";
+ return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] "
+ + mHOffset + ", " + mVOffset;
}
public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index ef0a4ad..48fc94e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -58,7 +58,7 @@
public String toString() {
return "DrawTweenPath " + mPath1Id + " " + mPath2Id
+ " " + mTween + " " + mStart + " "
- + "- " + mStop + ";";
+ + "- " + mStop;
}
public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
new file mode 100644
index 0000000..576b53f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class FloatConstant implements Operation {
+ public int mTextId;
+ public float mValue;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public FloatConstant(int textId, float value) {
+ this.mTextId = textId;
+ this.mValue = value;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextId, mValue);
+ }
+
+ @Override
+ public String toString() {
+ return "FloatConstant[" + mTextId + "] = " + mValue + "";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {}
+
+ @Override
+ public String name() {
+ return "FloatExpression";
+ }
+
+ @Override
+ public int id() {
+ return Operations.DATA_FLOAT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textId
+ * @param value
+ */
+ public void apply(WireBuffer buffer, int textId, float value) {
+ buffer.start(Operations.DATA_FLOAT);
+ buffer.writeInt(textId);
+ buffer.writeFloat(value);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+
+ float value = buffer.readFloat();
+ operations.add(new FloatConstant(textId, value));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadFloat(mTextId, mValue);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
new file mode 100644
index 0000000..354f41b
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Operation to deal with AnimatedFloats
+ * This is designed to be an optimized calculation for things like
+ * injecting the width of the component int draw rect
+ * As well as supporting generalized animation floats.
+ * The floats represent a RPN style calculator
+ */
+public class FloatExpression implements Operation, VariableSupport {
+ public int mId;
+ public float[] mSrcValue;
+ public float[] mSrcAnimation;
+ public FloatAnimation mFloatAnimation;
+ public float[] mPreCalcValue;
+ private float mLastChange = Float.NaN;
+ AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public FloatExpression(int id, float[] value, float[] animation) {
+ this.mId = id;
+ this.mSrcValue = value;
+ this.mSrcAnimation = animation;
+ if (mSrcAnimation != null) {
+ mFloatAnimation = new FloatAnimation(mSrcAnimation);
+ }
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) {
+ mPreCalcValue = new float[mSrcValue.length];
+ }
+ //Utils.log("updateVariables ");
+ boolean value_changed = false;
+ for (int i = 0; i < mSrcValue.length; i++) {
+ float v = mSrcValue[i];
+ if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) {
+ float newValue = context.getFloat(Utils.idFromNan(v));
+ if (mFloatAnimation != null) {
+ if (mPreCalcValue[i] != newValue) {
+ mLastChange = context.getAnimationTime();
+ value_changed = true;
+ mPreCalcValue[i] = newValue;
+ }
+ } else {
+ mPreCalcValue[i] = newValue;
+ }
+ } else {
+ mPreCalcValue[i] = mSrcValue[i];
+ }
+ }
+ if (value_changed && mFloatAnimation != null) {
+ float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
+ if (Float.isNaN(mFloatAnimation.getTargetValue())) {
+ mFloatAnimation.setInitialValue(v);
+ } else {
+ mFloatAnimation.setInitialValue(mFloatAnimation.getTargetValue());
+ }
+ mFloatAnimation.setTargetValue(v);
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ for (int i = 0; i < mSrcValue.length; i++) {
+ float v = mSrcValue[i];
+ if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) {
+ context.listensTo(Utils.idFromNan(v), this);
+ }
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ updateVariables(context);
+ float t = context.getAnimationTime();
+ if (Float.isNaN(mLastChange)) {
+ mLastChange = t;
+ }
+ if (mFloatAnimation != null) {
+ float f = mFloatAnimation.get(t - mLastChange);
+ context.loadFloat(mId, f);
+ } else {
+ context.loadFloat(mId, mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)));
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mId, mSrcValue, mSrcAnimation);
+ }
+
+ @Override
+ public String toString() {
+ String[] labels = new String[mSrcValue.length];
+ for (int i = 0; i < mSrcValue.length; i++) {
+ if (Float.isNaN(mSrcValue[i])) {
+ labels[i] = "[" + Utils.idFromNan(mSrcValue[i]) + "]";
+ }
+
+ }
+ return "FloatExpression[" + mId + "] = ("
+ + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "FloatExpression";
+ }
+
+ @Override
+ public int id() {
+ return Operations.ANIMATED_FLOAT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param id
+ * @param value
+ * @param animation
+ */
+ public void apply(WireBuffer buffer, int id, float[] value, float[] animation) {
+ buffer.start(Operations.ANIMATED_FLOAT);
+ buffer.writeInt(id);
+
+ int len = value.length;
+ if (animation != null) {
+ len |= (animation.length << 16);
+ }
+ buffer.writeInt(len);
+
+ for (int i = 0; i < value.length; i++) {
+ buffer.writeFloat(value[i]);
+ }
+ if (animation != null) {
+ for (int i = 0; i < animation.length; i++) {
+ buffer.writeFloat(animation[i]);
+ }
+ }
+
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int len = buffer.readInt();
+ int valueLen = len & 0xFFFF;
+ int animLen = (len >> 16) & 0xFFFF;
+ float[] values = new float[valueLen];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = buffer.readFloat();
+ }
+
+ float[] animation;
+ if (animLen != 0) {
+ animation = new float[animLen];
+ for (int i = 0; i < animation.length; i++) {
+ animation[i] = buffer.readFloat();
+ }
+ } else {
+ animation = null;
+ }
+ operations.add(new FloatExpression(id, values, animation));
+ }
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index 482e0e2..0dad45c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -37,7 +37,7 @@
@Override
public String toString() {
- return "MatrixRestore;";
+ return "MatrixRestore";
}
public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
index d6c89e0..bbf4135 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
@@ -15,68 +15,29 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class MatrixRotate extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mRotate, mPivotX, mPivotY;
+public class MatrixRotate extends DrawBase3 {
+ public static final Companion COMPANION =
+ new Companion(Operations.MATRIX_ROTATE) {
+ @Override
+ public Operation construct(float rotate,
+ float pivotX,
+ float pivotY
+ ) {
+ return new MatrixRotate(rotate, pivotX, pivotY);
+ }
+ };
public MatrixRotate(float rotate, float pivotX, float pivotY) {
- mRotate = rotate;
- mPivotX = pivotX;
- mPivotY = pivotY;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mRotate, mPivotX, mPivotY);
- }
-
- @Override
- public String toString() {
- return "DrawArc " + mRotate + ", " + mPivotX + ", " + mPivotY + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float rotate = buffer.readFloat();
- float pivotX = buffer.readFloat();
- float pivotY = buffer.readFloat();
- MatrixRotate op = new MatrixRotate(rotate, pivotX, pivotY);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "Matrix";
- }
-
- @Override
- public int id() {
- return Operations.MATRIX_ROTATE;
- }
-
- public void apply(WireBuffer buffer, float rotate, float pivotX, float pivotY) {
- buffer.start(Operations.MATRIX_ROTATE);
- buffer.writeFloat(rotate);
- buffer.writeFloat(pivotX);
- buffer.writeFloat(pivotY);
- }
+ super(rotate, pivotX, pivotY);
+ mName = "MatrixRotate";
}
@Override
public void paint(PaintContext context) {
- context.matrixRotate(mRotate, mPivotX, mPivotY);
+ context.matrixRotate(mV1, mV2, mV3);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
index 28aa68dd..04b940b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
@@ -15,74 +15,30 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class MatrixScale extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mScaleX, mScaleY;
- float mCenterX, mCenterY;
+public class MatrixScale extends DrawBase4 {
+ public static final Companion COMPANION =
+ new Companion(Operations.MATRIX_SCALE) {
+ @Override
+ public Operation construct(float scaleX,
+ float scaleY,
+ float centerX,
+ float centerY
+ ) {
+ return new MatrixScale(scaleX, scaleY, centerX, centerY);
+ }
+ };
public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) {
- mScaleX = scaleX;
- mScaleY = scaleY;
- mCenterX = centerX;
- mCenterY = centerY;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mScaleX, mScaleY, mCenterX, mCenterY);
- }
-
- @Override
- public String toString() {
- return "MatrixScale " + mScaleY + ", " + mScaleY + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float scaleX = buffer.readFloat();
- float scaleY = buffer.readFloat();
- float centerX = buffer.readFloat();
- float centerY = buffer.readFloat();
- MatrixScale op = new MatrixScale(scaleX, scaleY, centerX, centerY);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "Matrix";
- }
-
- @Override
- public int id() {
- return Operations.MATRIX_SCALE;
- }
-
- public void apply(WireBuffer buffer, float scaleX, float scaleY,
- float centerX, float centerY) {
- buffer.start(Operations.MATRIX_SCALE);
- buffer.writeFloat(scaleX);
- buffer.writeFloat(scaleY);
- buffer.writeFloat(centerX);
- buffer.writeFloat(centerY);
-
- }
+ super(scaleX, scaleY, centerX, centerY);
+ mName = "MatrixScale";
}
@Override
public void paint(PaintContext context) {
- context.mtrixScale(mScaleX, mScaleY, mCenterX, mCenterY);
+ context.matrixScale(mX1, mY1, mX2, mY2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
index 3298752..4f34e98 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
@@ -15,65 +15,28 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
-import java.util.List;
-
-public class MatrixTranslate extends PaintOperation {
- public static final Companion COMPANION = new Companion();
- float mTranslateX, mTranslateY;
+public class MatrixTranslate extends DrawBase2 {
+ public static final Companion COMPANION =
+ new Companion(Operations.MATRIX_TRANSLATE) {
+ @Override
+ public Operation construct(float x1,
+ float y1
+ ) {
+ return new MatrixTranslate(x1, y1);
+ }
+ };
public MatrixTranslate(float translateX, float translateY) {
- mTranslateX = translateX;
- mTranslateY = translateY;
- }
-
- @Override
- public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mTranslateX, mTranslateY);
- }
-
- @Override
- public String toString() {
- return "DrawArc " + mTranslateY + ", " + mTranslateY + ";";
- }
-
- public static class Companion implements CompanionOperation {
- private Companion() {
- }
-
- @Override
- public void read(WireBuffer buffer, List<Operation> operations) {
- float translateX = buffer.readFloat();
- float translateY = buffer.readFloat();
- MatrixTranslate op = new MatrixTranslate(translateX, translateY);
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "Matrix";
- }
-
- @Override
- public int id() {
- return Operations.MATRIX_TRANSLATE;
- }
-
- public void apply(WireBuffer buffer, float translateX, float translateY) {
- buffer.start(Operations.MATRIX_TRANSLATE);
- buffer.writeFloat(translateX);
- buffer.writeFloat(translateY);
- }
+ super(translateX, translateY);
+ mName = "MatrixTranslate";
}
@Override
public void paint(PaintContext context) {
- context.matrixTranslate(mTranslateX, mTranslateY);
+ context.matrixTranslate(mV1, mV2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
new file mode 100644
index 0000000..0c5b286
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class NamedVariable implements Operation {
+ public int mVarId;
+ public String mVarName;
+ public int mVarType;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public NamedVariable(int varId, int varType, String name) {
+ this.mVarId = varId;
+ this.mVarType = varType;
+ this.mVarName = name;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mVarId, mVarType, mVarName);
+ }
+
+ @Override
+ public String toString() {
+ return "VariableName[" + mVarId + "] = \""
+ + Utils.trimString(mVarName, 10) + "\" type=" + mVarType;
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "TextData";
+ }
+
+ @Override
+ public int id() {
+ return Operations.DATA_TEXT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param varId
+ * @param varType
+ * @param text
+ */
+ public void apply(WireBuffer buffer, int varId, int varType, String text) {
+ buffer.start(Operations.DATA_TEXT);
+ buffer.writeInt(varId);
+ buffer.writeInt(varType);
+ buffer.writeUTF8(text);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int varId = buffer.readInt();
+ int varType = buffer.readInt();
+ String text = buffer.readUTF8(MAX_STRING_SIZE);
+ operations.add(new NamedVariable(varId, varType, text));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadVariableName(mVarName, mVarId, mVarType);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index e5683ec..0807bcd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -20,12 +20,14 @@
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import java.util.List;
-public class PaintData extends PaintOperation {
+public class PaintData extends PaintOperation implements VariableSupport {
public PaintBundle mPaintData = new PaintBundle();
public static final Companion COMPANION = new Companion();
public static final int MAX_STRING_SIZE = 4000;
@@ -34,6 +36,16 @@
}
@Override
+ public void updateVariables(RemoteContext context) {
+ mPaintData.updateVariables(context);
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ mPaintData.registerVars(context, this);
+ }
+
+ @Override
public void write(WireBuffer buffer) {
COMPANION.apply(buffer, mPaintData);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 2646b27..e467e7b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -18,27 +18,50 @@
import com.android.internal.widget.remotecompose.core.CompanionOperation;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
+import java.util.Arrays;
import java.util.List;
-public class PathData implements Operation {
+public class PathData implements Operation, VariableSupport {
public static final Companion COMPANION = new Companion();
int mInstanceId;
- float[] mRef;
float[] mFloatPath;
- float[] mRetFloats;
+ float[] mOutputPath;
PathData(int instanceId, float[] floatPath) {
mInstanceId = instanceId;
mFloatPath = floatPath;
+ mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length);
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ for (int i = 0; i < mFloatPath.length; i++) {
+ float v = mFloatPath[i];
+ if (Utils.isVariable(v)) {
+ mOutputPath[i] = (Float.isNaN(v))
+ ? context.getFloat(Utils.idFromNan(v)) : v;
+ } else {
+ mOutputPath[i] = v;
+ }
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ for (int i = 0; i < mFloatPath.length; i++) {
+ if (Float.isNaN(mFloatPath[i])) {
+ context.listensTo(Utils.idFromNan(mFloatPath[i]), this);
+ }
+ }
}
@Override
public void write(WireBuffer buffer) {
- COMPANION.apply(buffer, mInstanceId, mFloatPath);
+ COMPANION.apply(buffer, mInstanceId, mOutputPath);
}
@Override
@@ -46,29 +69,35 @@
return pathString(mFloatPath);
}
- public float[] getFloatPath(PaintContext context) {
- float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
- if (ret == null) {
- return mFloatPath; // Assume floatPath is declared elsewhere
- }
- float[] localRef = mRef; // Assume ref is of type Float[]
- if (localRef == null) {
- for (int i = 0; i < mFloatPath.length; i++) {
- ret[i] = mFloatPath[i];
- }
- } else {
- for (int i = 0; i < mFloatPath.length; i++) {
- float lr = localRef[i];
- if (Float.isNaN(lr)) {
- ret[i] = Utils.getActualValue(lr);
- } else {
- ret[i] = mFloatPath[i];
- }
- }
- }
- return ret;
+ @Override
+ public String toString() {
+ return "PathData[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\"";
}
+ /**
+ * public float[] getFloatPath(PaintContext context) {
+ * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
+ * if (ret == null) {
+ * return mFloatPath; // Assume floatPath is declared elsewhere
+ * }
+ * float[] localRef = mRef; // Assume ref is of type Float[]
+ * if (localRef == null) {
+ * for (int i = 0; i < mFloatPath.length; i++) {
+ * ret[i] = mFloatPath[i];
+ * }
+ * } else {
+ * for (int i = 0; i < mFloatPath.length; i++) {
+ * float lr = localRef[i];
+ * if (Float.isNaN(lr)) {
+ * ret[i] = Utils.getActualValue(lr);
+ * } else {
+ * ret[i] = mFloatPath[i];
+ * }
+ * }
+ * }
+ * return ret;
+ * }
+ */
public static final int MOVE = 10;
public static final int LINE = 11;
public static final int QUADRATIC = 12;
@@ -155,7 +184,7 @@
str.append(".");
break;
default:
- str.append("X");
+ str.append("[" + id + "]");
break;
}
} else {
@@ -170,7 +199,7 @@
@Override
public void apply(RemoteContext context) {
- context.loadPathData(mInstanceId, mFloatPath);
+ context.loadPathData(mInstanceId, mOutputPath);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 6d924eb..997e8dc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -94,7 +94,6 @@
public static final int SCALE_CROP = 5;
public static final int SCALE_FILL_BOUNDS = 6;
-
public static final Companion COMPANION = new Companion();
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
index 64c7f3e..076b28e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
@@ -48,7 +48,7 @@
@Override
public String toString() {
- return "ROOT_CONTENT_DESCRIPTION " + mContentDescription;
+ return "RootContentDescription " + mContentDescription;
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
new file mode 100644
index 0000000..8463ac5
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Operation to deal with bitmap data
+ * On getting an Image during a draw call the bitmap is compressed and saved
+ * in playback the image is decompressed
+ */
+public class ShaderData implements Operation, VariableSupport {
+ int mShaderTextId; // the actual text of a shader
+ int mShaderID; // allows shaders to be referenced by number
+ HashMap<String, float[]> mUniformRawFloatMap = null;
+ HashMap<String, float[]> mUniformFloatMap = null;
+ HashMap<String, int[]> mUniformIntMap = null;
+ HashMap<String, Integer> mUniformBitmapMap = null;
+
+ public static final int MAX_IMAGE_DIMENSION = 8000;
+
+ public static final Companion COMPANION = new Companion();
+
+ public ShaderData(int shaderID,
+ int shaderTextId,
+ HashMap<String, float[]> floatMap,
+ HashMap<String, int[]> intMap,
+ HashMap<String, Integer> bitmapMap) {
+ mShaderID = shaderID;
+ mShaderTextId = shaderTextId;
+ if (floatMap != null) {
+ mUniformFloatMap = new HashMap<>();
+ mUniformRawFloatMap = new HashMap<>();
+
+ for (String name : floatMap.keySet()) {
+ mUniformRawFloatMap.put(name, floatMap.get(name));
+ mUniformFloatMap.put(name, floatMap.get(name));
+ }
+ }
+
+ if (intMap != null) {
+ mUniformIntMap = new HashMap<>();
+ for (String name : intMap.keySet()) {
+ mUniformIntMap.put(name, intMap.get(name));
+ }
+ }
+ if (bitmapMap != null) {
+ mUniformBitmapMap = new HashMap<>();
+ for (String name : bitmapMap.keySet()) {
+ mUniformBitmapMap.put(name, bitmapMap.get(name));
+ }
+ }
+
+ }
+
+ public int getShaderTextId() {
+ return mShaderTextId;
+ }
+
+ /**
+ * get names of all known floats
+ * @return
+ */
+ public String[] getUniformFloatNames() {
+ if (mUniformFloatMap == null) return new String[0];
+ return mUniformFloatMap.keySet().toArray(new String[0]);
+ }
+
+ /**
+ * Get float values associated with the name
+ * @param name
+ * @return
+ */
+ public float[] getUniformFloats(String name) {
+ return mUniformFloatMap.get(name);
+ }
+
+ /**
+ * get the name of all know uniform integers
+ * @return
+ */
+ public String[] getUniformIntegerNames() {
+ if (mUniformIntMap == null) return new String[0];
+ return mUniformIntMap.keySet().toArray(new String[0]);
+ }
+
+ /**
+ * Get Int value associated with the name
+ * @param name
+ * @return
+ */
+ public int[] getUniformInts(String name) {
+ return mUniformIntMap.get(name);
+ }
+
+ /**
+ * get list of uniform Bitmaps
+ * @return
+ */
+ public String[] getUniformBitmapNames() {
+ if (mUniformBitmapMap == null) return new String[0];
+ return mUniformBitmapMap.keySet().toArray(new String[0]);
+ }
+
+ /**
+ * Get a bitmap stored under that name
+ * @param name
+ * @return
+ */
+ public int getUniformBitmapId(String name) {
+ return mUniformBitmapMap.get(name);
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mShaderID, mShaderTextId,
+ mUniformFloatMap, mUniformIntMap, mUniformBitmapMap);
+ }
+
+ @Override
+ public String toString() {
+ return "SHADER DATA " + mShaderID;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ for (String name : mUniformRawFloatMap.keySet()) {
+ float[] value = mUniformRawFloatMap.get(name);
+ float[] out = null;
+ for (int i = 0; i < value.length; i++) {
+ if (Float.isNaN(value[i])) {
+ if (out == null) { // need to copy
+ out = Arrays.copyOf(value, value.length);
+ }
+ out[i] = context.getFloat(Utils.idFromNan(value[i]));
+ }
+ }
+ mUniformFloatMap.put(name, out == null ? value : out);
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ for (String name : mUniformRawFloatMap.keySet()) {
+ float[] value = mUniformRawFloatMap.get(name);
+ for (int i = 0; i < value.length; i++) {
+ if (Float.isNaN(value[i])) {
+ context.listensTo(Utils.idFromNan(value[i]), this);
+ }
+ }
+ }
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "BitmapData";
+ }
+
+ @Override
+ public int id() {
+ return Operations.DATA_SHADER;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param shaderID
+ * @param shaderTextId
+ * @param floatMap
+ * @param intMap
+ * @param bitmapMap
+ */
+ public void apply(WireBuffer buffer, int shaderID, int shaderTextId,
+ HashMap<String, float[]> floatMap,
+ HashMap<String, int[]> intMap,
+ HashMap<String, Integer> bitmapMap) {
+ buffer.start(Operations.DATA_SHADER);
+ buffer.writeInt(shaderID);
+
+ buffer.writeInt(shaderTextId);
+ int floatSize = (floatMap == null) ? 0 : floatMap.size();
+ int intSize = (intMap == null) ? 0 : intMap.size();
+ int bitmapSize = (bitmapMap == null) ? 0 : bitmapMap.size();
+ int sizes = floatSize | (intSize << 8) | (bitmapSize << 16);
+ buffer.writeInt(sizes);
+
+ if (floatSize > 0) {
+
+ for (String name : floatMap.keySet()) {
+ buffer.writeUTF8(name);
+ float[] values = floatMap.get(name);
+ buffer.writeInt(values.length);
+
+ for (int i = 0; i < values.length; i++) {
+ buffer.writeFloat(values[i]);
+ }
+ }
+ }
+
+ if (intSize > 0) {
+ for (String name : intMap.keySet()) {
+ buffer.writeUTF8(name);
+ int[] values = intMap.get(name);
+ buffer.writeInt(values.length);
+ for (int i = 0; i < values.length; i++) {
+ buffer.writeInt(values[i]);
+ }
+ }
+ }
+ if (bitmapSize > 0) {
+ for (String name : bitmapMap.keySet()) {
+ buffer.writeUTF8(name);
+ int value = bitmapMap.get(name);
+ buffer.writeInt(value);
+ }
+ }
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int shaderID = buffer.readInt();
+ int shaderTextId = buffer.readInt();
+ HashMap<String, float[]> floatMap = null;
+ HashMap<String, int[]> intMap = null;
+ HashMap<String, Integer> bitmapMap = null;
+
+ int sizes = buffer.readInt();
+
+ int floatMapSize = sizes & 0xFF;
+ if (floatMapSize > 0) {
+ floatMap = new HashMap<>();
+ for (int i = 0; i < floatMapSize; i++) {
+ String name = buffer.readUTF8();
+ int len = buffer.readInt();
+ float[] val = new float[len];
+
+ for (int j = 0; j < len; j++) {
+ val[j] = buffer.readFloat();
+ }
+
+ floatMap.put(name, val);
+ }
+ }
+ int intMapSize = (sizes >> 8) & 0xFF;
+
+ if (intMapSize > 0) {
+
+ intMap = new HashMap<>();
+ for (int i = 0; i < intMapSize; i++) {
+ String name = buffer.readUTF8();
+ int len = buffer.readInt();
+ int[] val = new int[len];
+ for (int j = 0; j < len; j++) {
+ val[j] = buffer.readInt();
+ }
+ intMap.put(name, val);
+ }
+ }
+ int bitmapMapSize = (sizes >> 16) & 0xFF;
+
+ if (bitmapMapSize > 0) {
+ bitmapMap = new HashMap<>();
+ for (int i = 0; i < bitmapMapSize; i++) {
+ String name = buffer.readUTF8();
+ int val = buffer.readInt();
+ bitmapMap.put(name, val);
+ }
+ }
+ operations.add(new ShaderData(shaderID, shaderTextId,
+ floatMap, intMap, bitmapMap));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadShader(mShaderID, this);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index 5b622ae..ed13449 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -44,11 +44,13 @@
@Override
public String toString() {
- return "TEXT DATA " + mTextId + "\"" + mText + "\"";
+ return "TextData[" + mTextId + "] = \""
+ + Utils.trimString(mText, 10) + "\"";
}
public static class Companion implements CompanionOperation {
- private Companion() {}
+ private Companion() {
+ }
@Override
public String name() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
new file mode 100644
index 0000000..65a39a1e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils;
+
+import java.util.List;
+
+/**
+ * Operation convert floats to text
+ * This command is structured [command][textID][before,after][flags]
+ * before and after define number of digits before and after the decimal point
+ */
+public class TextFromFloat implements Operation, VariableSupport {
+ public int mTextId;
+ public float mValue;
+ public float mOutValue;
+ public short mDigitsBefore;
+ public short mDigitsAfter;
+ public int mFlags;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+ char mPre = ' ';
+ char mAfter = ' ';
+ // Theses flags define what how to/if fill the space
+ public static final int PAD_AFTER_SPACE = 0; // pad past point with space
+ public static final int PAD_AFTER_NONE = 1; // do not pad past last digit
+ public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit
+ public static final int PAD_PRE_SPACE = 0; // pad before number with spaces
+ public static final int PAD_PRE_NONE = 4; // pad before number with 0s
+ public static final int PAD_PRE_ZERO = 12; // do not pad before number
+
+ public TextFromFloat(int textId, float value, short digitsBefore,
+ short digitsAfter, int flags) {
+ this.mTextId = textId;
+ this.mValue = value;
+ this.mDigitsAfter = digitsAfter;
+ this.mDigitsBefore = digitsBefore;
+ this.mFlags = flags;
+ mOutValue = mValue;
+ switch (mFlags & 3) {
+ case PAD_AFTER_SPACE:
+ mAfter = ' ';
+ break;
+ case PAD_AFTER_NONE:
+ mAfter = 0;
+ break;
+ case PAD_AFTER_ZERO:
+ mAfter = '0';
+ break;
+ }
+ switch (mFlags & 12) {
+ case PAD_PRE_SPACE:
+ mPre = ' ';
+ break;
+ case PAD_PRE_NONE:
+ mPre = 0;
+ break;
+ case PAD_PRE_ZERO:
+ mPre = '0';
+ break;
+ }
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags);
+ }
+
+ @Override
+ public String toString() {
+ return "TextFromFloat[" + mTextId + "] = "
+ + Utils.floatToString(mValue) + " " + mDigitsBefore
+ + "." + mDigitsAfter + " " + mFlags;
+ }
+
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (Float.isNaN(mValue)) {
+ mOutValue = context.getFloat(Utils.idFromNan(mValue));
+ }
+
+ }
+
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mValue)) {
+ context.listensTo(Utils.idFromNan(mValue), this);
+ }
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "TextData";
+ }
+
+ @Override
+ public int id() {
+ return Operations.TEXT_FROM_FLOAT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textId
+ * @param value
+ * @param digitsBefore
+ * @param digitsAfter
+ * @param flags
+ */
+ public void apply(WireBuffer buffer, int textId,
+ float value, short digitsBefore,
+ short digitsAfter, int flags) {
+ buffer.start(Operations.TEXT_FROM_FLOAT);
+ buffer.writeInt(textId);
+ buffer.writeFloat(value);
+ buffer.writeInt((digitsBefore << 16) | digitsAfter);
+ buffer.writeInt(flags);
+
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+ float value = buffer.readFloat();
+ int tmp = buffer.readInt();
+ short post = (short) (tmp & 0xFFFF);
+ short pre = (short) ((tmp >> 16) & 0xFFFF);
+
+ int flags = buffer.readInt();
+ operations.add(new TextFromFloat(textId, value, pre, post, flags));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ float v = mOutValue;
+ String s = StringUtils.floatToString(v, mDigitsBefore,
+ mDigitsAfter, mPre, mAfter);
+ context.loadText(mTextId, s);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
new file mode 100644
index 0000000..a0fc854
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class TextMerge implements Operation {
+ public int mTextId;
+ public int mSrcId1;
+ public int mSrcId2;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public TextMerge(int textId, int srcId1, int srcId2) {
+ this.mTextId = textId;
+ this.mSrcId1 = srcId1;
+ this.mSrcId2 = srcId2;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mTextId, mSrcId1, mSrcId2);
+ }
+
+ @Override
+ public String toString() {
+ return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "TextData";
+ }
+
+ @Override
+ public int id() {
+ return Operations.TEXT_MERGE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ * @param buffer
+ * @param textId
+ * @param srcId1
+ * @param srcId2
+ */
+ public void apply(WireBuffer buffer, int textId, int srcId1, int srcId2) {
+ buffer.start(Operations.TEXT_MERGE);
+ buffer.writeInt(textId);
+ buffer.writeInt(srcId1);
+ buffer.writeInt(srcId2);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+ int srcId1 = buffer.readInt();
+ int srcId2 = buffer.readInt();
+
+ operations.add(new TextMerge(textId, srcId1, srcId2));
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ String str1 = context.getText(mSrcId1);
+ String str2 = context.getText(mSrcId2);
+ context.loadText(mTextId, str1 + str2);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index 00e2f20..fdc6860 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -15,13 +15,16 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
+/**
+ * Utilities to be used across all core operations
+ */
public class Utils {
public static float asNan(int v) {
return Float.intBitsToFloat(v | -0x800000);
}
public static int idFromNan(float value) {
- int b = Float.floatToRawIntBits(value);
+ int b = Float.floatToRawIntBits(value);
return b & 0xFFFFF;
}
@@ -29,13 +32,194 @@
return 0;
}
- String getFloatString(float value) {
- if (Float.isNaN(value)) {
- int id = idFromNan(value);
- if (id > 0) {
- return "NaN(" + id + ")";
- }
+ /**
+ * trim a string to n characters if needing to trim
+ * end in "..."
+ *
+ * @param str
+ * @param n
+ * @return
+ */
+ static String trimString(String str, int n) {
+ if (str.length() > n) {
+ str = str.substring(0, n - 3) + "...";
}
- return "" + value;
+ return str;
}
+
+ /**
+ * print the id and the value of a float
+ * @param idvalue
+ * @param value
+ * @return
+ */
+ public static String floatToString(float idvalue, float value) {
+ if (Float.isNaN(idvalue)) {
+ return "[" + idFromNan(idvalue) + "]" + floatToString(value);
+ }
+ return floatToString(value);
+ }
+
+ /**
+ * Convert float to string but render nan id in brackets [n]
+ * @param value
+ * @return
+ */
+ public static String floatToString(float value) {
+ if (Float.isNaN(value)) {
+ return "[" + idFromNan(value) + "]";
+ }
+ return Float.toString(value);
+ }
+
+ /**
+ * Debugging util to print a message and include the file/line it came from
+ * @param str
+ */
+ public static void log(String str) {
+ StackTraceElement s = new Throwable().getStackTrace()[1];
+ System.out.println("(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str);
+ }
+
+ /**
+ * Debugging util to print the stack
+ * @param str
+ * @param n
+ */
+ public static void logStack(String str, int n) {
+ StackTraceElement[] st = new Throwable().getStackTrace();
+ for (int i = 1; i < n + 1; i++) {
+ StackTraceElement s = st[i];
+ String space = new String(new char[i]).replace('\0', ' ');
+ System.out.println(space + "(" + s.getFileName()
+ + ":" + s.getLineNumber() + ")." + str);
+ }
+ }
+
+ /**
+ * Is a variable Allowed int calculation and references.
+ *
+ * @param v
+ * @return
+ */
+ public static boolean isVariable(float v) {
+ if (Float.isNaN(v)) {
+ int id = idFromNan(v);
+ return id > 40 || id < 10;
+ }
+ return false;
+ }
+
+ /**
+ * print a color in the familiar 0xAARRGGBB pattern
+ *
+ * @param color
+ * @return
+ */
+ public static String colorInt(int color) {
+ String str = "000000000000" + Integer.toHexString(color);
+ return "0x" + str.substring(str.length() - 8);
+ }
+
+ /**
+ * Interpolate two colors.
+ * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t
+ *
+ * @param c1
+ * @param c2
+ * @param t
+ * @return
+ */
+ public static int interpolateColor(int c1, int c2, float t) {
+ if (Float.isNaN(t) || t == 0.0f) {
+ return c1;
+ } else if (t == 1.0f) {
+ return c2;
+ }
+ int a = 0xFF & (c1 >> 24);
+ int r = 0xFF & (c1 >> 16);
+ int g = 0xFF & (c1 >> 8);
+ int b = 0xFF & c1;
+ float f_r = (float) Math.pow(r / 255.0f, 2.2);
+ float f_g = (float) Math.pow(g / 255.0f, 2.2);
+ float f_b = (float) Math.pow(b / 255.0f, 2.2);
+ float c1fr = f_r;
+ float c1fg = f_g;
+ float c1fb = f_b;
+ float c1fa = a / 255f;
+
+ a = 0xFF & (c2 >> 24);
+ r = 0xFF & (c2 >> 16);
+ g = 0xFF & (c2 >> 8);
+ b = 0xFF & c2;
+ f_r = (float) Math.pow(r / 255.0f, 2.2);
+ f_g = (float) Math.pow(g / 255.0f, 2.2);
+ f_b = (float) Math.pow(b / 255.0f, 2.2);
+ float c2fr = f_r;
+ float c2fg = f_g;
+ float c2fb = f_b;
+ float c2fa = a / 255f;
+ f_r = c1fr + t * (c2fr - c1fr);
+ f_g = c1fg + t * (c2fg - c1fg);
+ f_b = c1fb + t * (c2fb - c1fb);
+ float f_a = c1fa + t * (c2fa - c1fa);
+
+ int outr = clamp((int) ((float) Math.pow(f_r, 1.0 / 2.2) * 255.0f));
+ int outg = clamp((int) ((float) Math.pow(f_g, 1.0 / 2.2) * 255.0f));
+ int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f));
+ int outa = clamp((int) (f_a * 255.0f));
+
+
+ return (outa << 24 | outr << 16 | outg << 8 | outb);
+ }
+
+ /**
+ * Efficient clamping function
+ *
+ * @param c
+ * @return number between 0 and 255
+ */
+ public static int clamp(int c) {
+ int n = 255;
+ c &= ~(c >> 31);
+ c -= n;
+ c &= (c >> 31);
+ c += n;
+ return c;
+ }
+
+ /**
+ * convert hue saturation and value to RGB
+ *
+ * @param hue 0..1
+ * @param saturation 0..1 0=on the gray scale
+ * @param value 0..1 0=black
+ * @return
+ */
+ public static int hsvToRgb(float hue, float saturation, float value) {
+ int h = (int) (hue * 6);
+ float f = hue * 6 - h;
+ int p = (int) (0.5f + 255 * value * (1 - saturation));
+ int q = (int) (0.5f + 255 * value * (1 - f * saturation));
+ int t = (int) (0.5f + 255 * value * (1 - (1 - f) * saturation));
+ int v = (int) (0.5f + 255 * value);
+ switch (h) {
+ case 0:
+ return 0XFF000000 | (v << 16) + (t << 8) + p;
+ case 1:
+ return 0XFF000000 | (q << 16) + (v << 8) + p;
+ case 2:
+ return 0XFF000000 | (p << 16) + (v << 8) + t;
+ case 3:
+ return 0XFF000000 | (p << 16) + (q << 8) + v;
+ case 4:
+ return 0XFF000000 | (t << 16) + (p << 8) + v;
+ case 5:
+ return 0XFF000000 | (v << 16) + (p << 8) + q;
+
+ }
+ return 0;
+ }
+
+
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 8abb0bf..a7d0ac6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -15,43 +15,60 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
import java.util.Arrays;
+/**
+ * Paint Bundle represents a delta of changes to a paint object
+ */
public class PaintBundle {
int[] mArray = new int[200];
+ int[] mOutArray = null;
int mPos = 0;
- public void applyPaintChange(PaintChanges p) {
+ /**
+ * Apply changes to a PaintChanges interface
+ * @param paintContext
+ * @param p
+ */
+ public void applyPaintChange(PaintContext paintContext, PaintChanges p) {
int i = 0;
int mask = 0;
+ if (mOutArray == null) {
+ mOutArray = mArray;
+ }
while (i < mPos) {
- int cmd = mArray[i++];
+ int cmd = mOutArray[i++];
mask = mask | (1 << (cmd - 1));
switch (cmd & 0xFFFF) {
case TEXT_SIZE: {
- p.setTextSize(Float.intBitsToFloat(mArray[i++]));
+ p.setTextSize(Float.intBitsToFloat(mOutArray[i++]));
break;
}
case TYPEFACE:
int style = (cmd >> 16);
int weight = style & 0x3ff;
boolean italic = (style >> 10) > 0;
- int font_type = mArray[i++];
+ int font_type = mOutArray[i++];
p.setTypeFace(font_type, weight, italic);
break;
+ case COLOR_ID: // mOutArray should have already decoded it
case COLOR: {
- p.setColor(mArray[i++]);
+ p.setColor(mOutArray[i++]);
break;
}
case STROKE_WIDTH: {
- p.setStrokeWidth(Float.intBitsToFloat(mArray[i++]));
+ p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++]));
break;
}
case STROKE_MITER: {
- p.setStrokeMiter(Float.intBitsToFloat(mArray[i++]));
+ p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++]));
break;
}
case STROKE_CAP: {
@@ -63,6 +80,7 @@
break;
}
case SHADER: {
+ p.setShader(mOutArray[i++]);
break;
}
case STROKE_JOIN: {
@@ -81,17 +99,16 @@
p.setFilterBitmap(!((cmd >> 16) == 0));
break;
}
-
case GRADIENT: {
- i = callSetGradient(cmd, mArray, i, p);
+ i = callSetGradient(cmd, mOutArray, i, p);
break;
}
case COLOR_FILTER: {
- p.setColorFilter(mArray[i++], cmd >> 16);
+ p.setColorFilter(mOutArray[i++], cmd >> 16);
break;
}
case ALPHA: {
- p.setAlpha(Float.intBitsToFloat(mArray[i++]));
+ p.setAlpha(Float.intBitsToFloat(mOutArray[i++]));
break;
}
}
@@ -106,7 +123,6 @@
switch (id) {
case TEXT_SIZE:
return "TEXT_SIZE";
-
case COLOR:
return "COLOR";
case STROKE_WIDTH:
@@ -133,7 +149,6 @@
return "ALPHA";
case COLOR_FILTER:
return "COLOR_FILTER";
-
}
return "????" + id + "????";
}
@@ -154,6 +169,14 @@
return str + "]";
}
+ private static String asFloatStr(int value) {
+ float fValue = Float.intBitsToFloat(value);
+ if (Float.isNaN(fValue)) {
+ return "[" + Utils.idFromNan(fValue) + "]";
+ }
+ return Float.toString(fValue);
+ }
+
@Override
public String toString() {
StringBuilder ret = new StringBuilder("\n");
@@ -164,7 +187,8 @@
switch (type) {
case TEXT_SIZE: {
- ret.append(" TextSize(" + Float.intBitsToFloat(mArray[i++]));
+ ret.append(" TextSize("
+ + asFloatStr(mArray[i++]));
}
break;
@@ -181,14 +205,18 @@
ret.append(" Color(" + colorInt(mArray[i++]));
}
break;
+ case COLOR_ID: {
+ ret.append(" ColorId([" + mArray[i++] + "]");
+ }
+ break;
case STROKE_WIDTH: {
ret.append(" StrokeWidth("
- + (Float.intBitsToFloat(mArray[i++])));
+ + (asFloatStr(mArray[i++])));
}
break;
case STROKE_MITER: {
ret.append(" StrokeMiter("
- + (Float.intBitsToFloat(mArray[i++])));
+ + (asFloatStr(mArray[i++])));
}
break;
case STROKE_CAP: {
@@ -207,11 +235,12 @@
}
break;
case SHADER: {
+ ret.append(" Shader(" + mArray[i++]);
}
break;
case ALPHA: {
ret.append(" Alpha("
- + (Float.intBitsToFloat(mArray[i++])));
+ + (asFloatStr(mArray[i++])));
}
break;
case IMAGE_FILTER_QUALITY: {
@@ -244,7 +273,6 @@
return ret.toString();
}
-
int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) {
int ret = i;
int type = (cmd >> 16);
@@ -258,26 +286,25 @@
colors = new int[len];
for (int j = 0; j < colors.length; j++) {
colors[j] = array[ret++];
-
}
}
len = array[ret++];
- float[] stops = null;
+ String[] stops = null;
if (len > 0) {
- stops = new float[len];
+ stops = new String[len];
for (int j = 0; j < stops.length; j++) {
- stops[j] = Float.intBitsToFloat(array[ret++]);
+ stops[j] = asFloatStr(array[ret++]);
}
}
p.append(" colors = " + colorInt(colors) + ",\n");
p.append(" stops = " + Arrays.toString(stops) + ",\n");
p.append(" start = ");
- p.append("[" + Float.intBitsToFloat(array[ret++]));
- p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+ p.append("[" + asFloatStr(array[ret++]));
+ p.append(", " + asFloatStr(array[ret++]) + "],\n");
p.append(" end = ");
- p.append("[" + Float.intBitsToFloat(array[ret++]));
- p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+ p.append("[" + asFloatStr(array[ret++]));
+ p.append(", " + asFloatStr(array[ret++]) + "],\n");
int tileMode = array[ret++];
p.append(" tileMode = " + tileMode + "\n ");
}
@@ -295,21 +322,21 @@
}
}
len = array[ret++];
- float[] stops = null;
+ String[] stops = null;
if (len > 0) {
- stops = new float[len];
+ stops = new String[len];
for (int j = 0; j < stops.length; j++) {
- stops[j] = Float.intBitsToFloat(array[ret++]);
+ stops[j] = asFloatStr(array[ret++]);
}
}
p.append(" colors = " + colorInt(colors) + ",\n");
p.append(" stops = " + Arrays.toString(stops) + ",\n");
p.append(" center = ");
- p.append("[" + Float.intBitsToFloat(array[ret++]));
- p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+ p.append("[" + asFloatStr(array[ret++]));
+ p.append(", " + asFloatStr(array[ret++]) + "],\n");
p.append(" radius =");
- p.append(" " + Float.intBitsToFloat(array[ret++]) + ",\n");
+ p.append(" " + asFloatStr(array[ret++]) + ",\n");
int tileMode = array[ret++];
p.append(" tileMode = " + tileMode + "\n ");
}
@@ -327,20 +354,19 @@
}
}
len = array[ret++];
- float[] stops = null;
+ String[] stops = null;
if (len > 0) {
- stops = new float[len];
+ stops = new String[len];
for (int j = 0; j < stops.length; j++) {
- stops[j] = Float.intBitsToFloat(array[ret++]);
+ stops[j] = asFloatStr(array[ret++]);
}
}
-
p.append(" colors = " + colorInt(colors) + ",\n");
p.append(" stops = " + Arrays.toString(stops) + ",\n");
p.append(" center = ");
- p.append("[" + Float.intBitsToFloat(array[ret++]));
- p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n ");
-
+ p.append("[" + asFloatStr(array[ret++]));
+ p.append(", "
+ + asFloatStr(array[ret++]) + "],\n ");
}
break;
default: {
@@ -376,7 +402,6 @@
return ret;
}
-
switch (gradientType) {
case LINEAR_GRADIENT: {
@@ -433,7 +458,7 @@
public static final int COLOR = 4; // int
public static final int STROKE_WIDTH = 5; // float
public static final int STROKE_MITER = 6;
- public static final int STROKE_CAP = 7; // int
+ public static final int STROKE_CAP = 7; // int
public static final int STYLE = 8; // int
public static final int SHADER = 9; // int
public static final int IMAGE_FILTER_QUALITY = 10; // int
@@ -445,7 +470,7 @@
public static final int TYPEFACE = 16;
public static final int FILTER_BITMAP = 17;
public static final int BLEND_MODE = 18;
-
+ public static final int COLOR_ID = 19; // int
public static final int BLEND_MODE_CLEAR = 0;
public static final int BLEND_MODE_SRC = 1;
@@ -634,8 +659,8 @@
/**
* @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
- * @param weight 100-1000
- * @param italic tur
+ * @param weight 100-1000
+ * @param italic tur
*/
public void setTextStyle(int fontType, int weight, boolean italic) {
int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic
@@ -658,6 +683,10 @@
mPos++;
}
+ /**
+ * Set the Color based on Color
+ * @param color
+ */
public void setColor(int color) {
mArray[mPos] = COLOR;
mPos++;
@@ -666,6 +695,18 @@
}
/**
+ * Set the Color based on ID
+ * @param color
+ */
+ public void setColorId(int color) {
+ mArray[mPos] = COLOR_ID;
+ mPos++;
+ mArray[mPos] = color;
+ mPos++;
+ }
+
+
+ /**
* Set the paint's Cap.
*
* @param cap set the paint's line cap style, used whenever the paint's
@@ -676,16 +717,29 @@
mPos++;
}
+ /**
+ * Set the style STROKE and/or FILL
+ * @param style
+ */
public void setStyle(int style) {
mArray[mPos] = STYLE | (style << 16);
mPos++;
}
- public void setShader(int shader, String shaderString) {
- mArray[mPos] = SHADER | (shader << 16);
+ /**
+ * Set the shader id to use
+ * @param shaderId
+ */
+ public void setShader(int shaderId) {
+ mArray[mPos] = SHADER;
+ mPos++;
+ mArray[mPos] = shaderId;
mPos++;
}
+ /**
+ * Set the Alpha value
+ */
public void setAlpha(float alpha) {
mArray[mPos] = ALPHA;
mPos++;
@@ -729,7 +783,6 @@
* destination pixels
* (content of the render target).
*
- *
* @param blendmode The blend mode to be installed in the paint
*/
public void setBlendMode(int blendmode) {
@@ -825,5 +878,216 @@
return "null";
}
-}
+ /**
+ * Check all the floats for Nan(id) floats and call listenTo
+ * @param context
+ * @param support
+ */
+ public void registerVars(RemoteContext context, VariableSupport support) {
+ int i = 0;
+ while (i < mPos) {
+ int cmd = mArray[i++];
+ int type = cmd & 0xFFFF;
+ switch (type) {
+ case STROKE_MITER:
+ case STROKE_WIDTH:
+ case ALPHA:
+ case TEXT_SIZE:
+ float v = Float.intBitsToFloat(mArray[i++]);
+ if (Float.isNaN(v)) {
+ context.listensTo(Utils.idFromNan(v), support);
+ }
+ break;
+ case COLOR_ID:
+ context.listensTo(mArray[i++], support);
+ break;
+ case COLOR:
+ case TYPEFACE:
+ case SHADER:
+ case COLOR_FILTER:
+ i++;
+ break;
+ case STROKE_JOIN:
+ case FILTER_BITMAP:
+ case STROKE_CAP:
+ case STYLE:
+ case IMAGE_FILTER_QUALITY:
+ case BLEND_MODE:
+ case ANTI_ALIAS:
+ break;
+
+ case GRADIENT: {
+ // TODO gradients should be handled correctly
+ i = callPrintGradient(cmd, mArray, i, new StringBuilder());
+ }
+ }
+ }
+ }
+
+ /**
+ * Update variables if any are float ids
+ * @param context
+ */
+ public void updateVariables(RemoteContext context) {
+ if (mOutArray == null) {
+ mOutArray = Arrays.copyOf(mArray, mArray.length);
+ } else {
+ System.arraycopy(mArray, 0, mOutArray, 0, mArray.length);
+ }
+ int i = 0;
+ while (i < mPos) {
+ int cmd = mArray[i++];
+ int type = cmd & 0xFFFF;
+ switch (type) {
+ case STROKE_MITER:
+ case STROKE_WIDTH:
+ case ALPHA:
+ case TEXT_SIZE:
+ mOutArray[i] = fixFloatVar(mArray[i], context);
+ i++;
+ break;
+ case COLOR_ID:
+ mOutArray[i] = fixColor(mArray[i], context);
+ i++;
+ break;
+ case COLOR:
+ case TYPEFACE:
+ case SHADER:
+ case COLOR_FILTER:
+ i++;
+ break;
+ case STROKE_JOIN:
+ case FILTER_BITMAP:
+ case STROKE_CAP:
+ case STYLE:
+ case IMAGE_FILTER_QUALITY:
+ case BLEND_MODE:
+ case ANTI_ALIAS:
+ break;
+
+ case GRADIENT: {
+ // TODO gradients should be handled correctly
+ i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context);
+ }
+ }
+ }
+ }
+
+ private int fixFloatVar(int val, RemoteContext context) {
+ float v = Float.intBitsToFloat(val);
+ if (Float.isNaN(v)) {
+ int id = Utils.idFromNan(v);
+ return Float.floatToRawIntBits(context.getFloat(id));
+ }
+ return val;
+ }
+
+ private int fixColor(int colorId, RemoteContext context) {
+ int n = context.getColor(colorId);
+ return n;
+ }
+
+ int updateFloatsInGradient(int cmd, int[] out, int[] array,
+ int i,
+ RemoteContext context) {
+ int ret = i;
+ int type = (cmd >> 16);
+ switch (type) {
+ case 0: {
+ int len = array[ret++];
+ if (len > 0) {
+ for (int j = 0; j < len; j++) {
+ ret++;
+ }
+ }
+ len = array[ret++];
+
+ if (len > 0) {
+ for (int j = 0; j < len; j++) {
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ }
+ }
+
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+
+ // end
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ ret++; // tileMode
+ }
+
+ break;
+ case 1: {
+ // RadialGradient
+ int len = array[ret++];
+ if (len > 0) {
+ for (int j = 0; j < len; j++) {
+ ret++;
+ }
+ }
+ len = array[ret++];
+ if (len > 0) {
+ for (int j = 0; j < len; j++) {
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ }
+ }
+
+
+ // center
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ // radius
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ ret++; // tileMode
+
+ }
+
+ break;
+ case 2: {
+ // SweepGradient
+ int len = array[ret++];
+ int[] colors = null;
+ if (len > 0) {
+ colors = new int[len];
+ for (int j = 0; j < colors.length; j++) {
+ colors[j] = array[ret++];
+
+ }
+ }
+ len = array[ret++];
+ float[] stops = null;
+ if (len > 0) {
+ stops = new float[len];
+ for (int j = 0; j < stops.length; j++) {
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ }
+ }
+
+ // center
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ out[ret] = fixFloatVar(array[ret], context);
+ ret++;
+ }
+ break;
+ default: {
+ System.err.println("gradient type unknown");
+ }
+ }
+
+ return ret;
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
index 994bf6d..28fe63a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
@@ -27,7 +27,6 @@
}
-
@Override
public void setStrokeWidth(float width) {
@@ -49,7 +48,7 @@
}
@Override
- public void setShader(int shader, String shaderString) {
+ public void setShader(int shader) {
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
index 87e58ac..d5dc388 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
@@ -15,9 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
+/**
+ * Interface to a paint object
+ * For more details see Android Paint
+ */
public interface PaintChanges {
-
+ // MASK to be set/cleared
+ int CLEAR_TEXT_SIZE = 1 << (PaintBundle.TEXT_SIZE - 1);
int CLEAR_TEXT_STYLE = 1 << (PaintBundle.TYPEFACE - 1);
int CLEAR_COLOR = 1 << (PaintBundle.COLOR - 1);
int CLEAR_STROKE_WIDTH = 1 << (PaintBundle.STROKE_WIDTH - 1);
@@ -32,21 +37,101 @@
int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1);
int VALID_BITS = 0x1FFF; // only the first 13 bit are valid now
-
+ /**
+ * Set the size of text
+ * @param size
+ */
void setTextSize(float size);
+
+ /**
+ * Set the width of lines
+ * @param width
+ */
void setStrokeWidth(float width);
+
+ /**
+ * Set the color to use
+ * @param color
+ */
void setColor(int color);
+
+ /**
+ * Set the Stroke Cap
+ * @param cap
+ */
void setStrokeCap(int cap);
+
+ /**
+ * Set the Stroke style FILL and/or STROKE
+ * @param style
+ */
void setStyle(int style);
- void setShader(int shader, String shaderString);
+
+ /**
+ * Set the id of the shader to use
+ * @param shader
+ */
+ void setShader(int shader);
+
+ /**
+ * Set the way image is interpolated
+ * @param quality
+ */
void setImageFilterQuality(int quality);
+
+ /**
+ * Set the alpha to draw under
+ * @param a
+ */
void setAlpha(float a);
+
+ /**
+ * Set the Stroke Miter
+ * @param miter
+ */
void setStrokeMiter(float miter);
+
+ /**
+ * Set the Stroke Join
+ * @param join
+ */
void setStrokeJoin(int join);
+
+ /**
+ * Should bitmaps be interpolated
+ * @param filter
+ */
void setFilterBitmap(boolean filter);
+
+ /**
+ * Set the blend mode can be porterduff + others
+ * @param mode
+ */
void setBlendMode(int mode);
+
+ /**
+ * Set the AntiAlias. Typically true
+ * Set to off when you need pixilated look (e.g. QR codes)
+ * @param aa
+ */
void setAntiAlias(boolean aa);
+
+ /**
+ * Clear some sub set of the settings
+ * @param mask
+ */
void clear(long mask);
+
+ /**
+ * Set a linear gradient fill
+ * @param colorsArray
+ * @param stopsArray
+ * @param startX
+ * @param startY
+ * @param endX
+ * @param endY
+ * @param tileMode
+ */
void setLinearGradient(
int[] colorsArray,
float[] stopsArray,
@@ -57,6 +142,15 @@
int tileMode
);
+ /**
+ * Set a radial gradient fill
+ * @param colorsArray
+ * @param stopsArray
+ * @param centerX
+ * @param centerY
+ * @param radius
+ * @param tileMode
+ */
void setRadialGradient(
int[] colorsArray,
float[] stopsArray,
@@ -66,6 +160,13 @@
int tileMode
);
+ /**
+ * Set a sweep gradient fill
+ * @param colorsArray
+ * @param stopsArray
+ * @param centerX
+ * @param centerY
+ */
void setSweepGradient(
int[] colorsArray,
float[] stopsArray,
@@ -73,9 +174,19 @@
float centerY
);
-
+ /**
+ * Set Color filter mod
+ * @param color
+ * @param mode
+ */
void setColorFilter(int color, int mode);
+ /**
+ * Set TypeFace 0,1,2
+ * TODO above should point to a string to be decoded
+ * @param fontType
+ * @param weight
+ * @param italic
+ */
void setTypeFace(int fontType, int weight, boolean italic);
-}
-
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
new file mode 100644
index 0000000..616048d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+/**
+ * high performance floating point expression evaluator used in animation
+ */
+public class AnimatedFloatExpression {
+ static IntMap<String> sNames = new IntMap<>();
+ public static final int OFFSET = 0x100;
+ public static final float ADD = asNan(OFFSET + 1);
+ public static final float SUB = asNan(OFFSET + 2);
+ public static final float MUL = asNan(OFFSET + 3);
+ public static final float DIV = asNan(OFFSET + 4);
+ public static final float MOD = asNan(OFFSET + 5);
+ public static final float MIN = asNan(OFFSET + 6);
+ public static final float MAX = asNan(OFFSET + 7);
+ public static final float POW = asNan(OFFSET + 8);
+ public static final float SQRT = asNan(OFFSET + 9);
+ public static final float ABS = asNan(OFFSET + 10);
+ public static final float SIGN = asNan(OFFSET + 11);
+ public static final float COPY_SIGN = asNan(OFFSET + 12);
+ public static final float EXP = asNan(OFFSET + 13);
+ public static final float FLOOR = asNan(OFFSET + 14);
+ public static final float LOG = asNan(OFFSET + 15);
+ public static final float LN = asNan(OFFSET + 16);
+ public static final float ROUND = asNan(OFFSET + 17);
+ public static final float SIN = asNan(OFFSET + 18);
+ public static final float COS = asNan(OFFSET + 19);
+ public static final float TAN = asNan(OFFSET + 20);
+ public static final float ASIN = asNan(OFFSET + 21);
+ public static final float ACOS = asNan(OFFSET + 22);
+
+ public static final float ATAN = asNan(OFFSET + 23);
+
+ public static final float ATAN2 = asNan(OFFSET + 24);
+ public static final float MAD = asNan(OFFSET + 25);
+ public static final float IFELSE = asNan(OFFSET + 26);
+
+ public static final float CLAMP = asNan(OFFSET + 27);
+ public static final float CBRT = asNan(OFFSET + 28);
+ public static final float DEG = asNan(OFFSET + 29);
+ public static final float RAD = asNan(OFFSET + 30);
+ public static final float CEIL = asNan(OFFSET + 31);
+
+
+ public static final float LAST_OP = 31;
+
+
+ public static final float VAR1 = asNan(OFFSET + 27);
+ public static final float VAR2 = asNan(OFFSET + 28);
+
+ // TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR
+ private static final float FP_PI = (float) Math.PI;
+ private static final float FP_TO_RAD = 57.29577951f; // 180/PI
+ private static final float FP_TO_DEG = 0.01745329252f; // 180/PI
+
+ float[] mStack;
+ float[] mLocalStack = new float[128];
+ float[] mVar;
+
+ /**
+ * is float a math operator
+ * @param v
+ * @return
+ */
+ public static boolean isMathOperator(float v) {
+ if (Float.isNaN(v)) {
+ int pos = fromNaN(v);
+ return pos > OFFSET && pos <= OFFSET + LAST_OP;
+ }
+ return false;
+ }
+
+ interface Op {
+ int eval(int sp);
+ }
+
+ /**
+ * Evaluate a float expression
+ * @param exp
+ * @param var
+ * @return
+ */
+ public float eval(float[] exp, float... var) {
+ mStack = exp;
+ mVar = var;
+ int sp = -1;
+ for (int i = 0; i < mStack.length; i++) {
+ float v = mStack[i];
+ if (Float.isNaN(v)) {
+ sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ } else {
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
+
+ /**
+ * Evaluate a float expression
+ * @param exp
+ * @param len
+ * @param var
+ * @return
+ */
+ public float eval(float[] exp, int len, float... var) {
+ System.arraycopy(exp, 0, mLocalStack, 0, len);
+ mStack = mLocalStack;
+ mVar = var;
+ int sp = -1;
+ for (int i = 0; i < len; i++) {
+ float v = mStack[i];
+ if (Float.isNaN(v)) {
+ sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ } else {
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
+
+ /**
+ * Evaluate a float expression
+ * @param exp
+ * @param var
+ * @return
+ */
+ public float evalDB(float[] exp, float... var) {
+ mStack = exp;
+ mVar = var;
+ int sp = -1;
+ for (float v : exp) {
+ if (Float.isNaN(v)) {
+ System.out.print(" " + sNames.get((fromNaN(v) - OFFSET)));
+ sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+ } else {
+ System.out.print(" " + v);
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
+
+ Op[] mOps = {
+ null,
+ (sp) -> { // ADD
+ mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // SUB
+ mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // MUL
+ mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // DIV
+ mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // MOD
+ mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // MIN
+ mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // MAX
+ mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // POW
+ mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // SQRT
+ mStack[sp] = (float) Math.sqrt(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ABS
+ mStack[sp] = (float) Math.abs(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // SIGN
+ mStack[sp] = (float) Math.signum(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // copySign
+ mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // EXP
+ mStack[sp] = (float) Math.exp(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // FLOOR
+ mStack[sp] = (float) Math.floor(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // LOG
+ mStack[sp] = (float) Math.log10(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // LN
+ mStack[sp] = (float) Math.log(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ROUND
+ mStack[sp] = (float) Math.round(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // SIN
+ mStack[sp] = (float) Math.sin(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // COS
+ mStack[sp] = (float) Math.cos(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // TAN
+ mStack[sp] = (float) Math.tan(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ASIN
+ mStack[sp] = (float) Math.asin(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ACOS
+ mStack[sp] = (float) Math.acos(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ATAN
+ mStack[sp] = (float) Math.atan(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // ATAN2
+ mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // MAD
+ mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+ return sp - 2;
+ },
+ (sp) -> { // Ternary conditional
+ mStack[sp - 2] = (mStack[sp] > 0)
+ ? mStack[sp - 1] : mStack[sp - 2];
+ return sp - 2;
+ },
+ (sp) -> { // CLAMP(min,max, val)
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
+ mStack[sp - 1]);
+ return sp - 2;
+ },
+ (sp) -> { // CBRT cuberoot
+ mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.);
+ return sp;
+ },
+ (sp) -> { // DEG
+ mStack[sp] = mStack[sp] * FP_TO_RAD;
+ return sp;
+ },
+ (sp) -> { // RAD
+ mStack[sp] = mStack[sp] * FP_TO_DEG;
+ return sp;
+ },
+ (sp) -> { // CEIL
+ mStack[sp] = (float) Math.ceil(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // first var =
+ mStack[sp] = mVar[0];
+ return sp;
+ },
+ (sp) -> { // second var y?
+ mStack[sp] = mVar[1];
+ return sp;
+ },
+ (sp) -> { // 3rd var z?
+ mStack[sp] = mVar[2];
+ return sp;
+ },
+ };
+
+ static {
+ int k = 0;
+ sNames.put(k++, "NOP");
+ sNames.put(k++, "+");
+ sNames.put(k++, "-");
+ sNames.put(k++, "*");
+ sNames.put(k++, "/");
+ sNames.put(k++, "%");
+ sNames.put(k++, "min");
+ sNames.put(k++, "max");
+ sNames.put(k++, "pow");
+ sNames.put(k++, "sqrt");
+ sNames.put(k++, "abs");
+ sNames.put(k++, "sign");
+ sNames.put(k++, "copySign");
+ sNames.put(k++, "exp");
+ sNames.put(k++, "floor");
+ sNames.put(k++, "log");
+ sNames.put(k++, "ln");
+ sNames.put(k++, "round");
+ sNames.put(k++, "sin");
+ sNames.put(k++, "cos");
+ sNames.put(k++, "tan");
+ sNames.put(k++, "asin");
+ sNames.put(k++, "acos");
+ sNames.put(k++, "atan");
+ sNames.put(k++, "atan2");
+ sNames.put(k++, "mad");
+ sNames.put(k++, "ifElse");
+ sNames.put(k++, "clamp");
+ sNames.put(k++, "cbrt");
+ sNames.put(k++, "deg");
+ sNames.put(k++, "rad");
+ sNames.put(k++, "ceil");
+ sNames.put(k++, "a[0]");
+ sNames.put(k++, "a[1]");
+ sNames.put(k++, "a[2]");
+ }
+
+ /**
+ * given a float command return its math name (e.g sin, cos etc.)
+ * @param f
+ * @return
+ */
+ public static String toMathName(float f) {
+ int id = fromNaN(f) - OFFSET;
+ return sNames.get(id);
+ }
+
+ /**
+ * Convert an expression encoded as an array of floats int ot a string
+ * @param exp
+ * @param labels
+ * @return
+ */
+ public static String toString(float[] exp, String[] labels) {
+ StringBuilder s = new StringBuilder();
+ for (int i = 0; i < exp.length; i++) {
+ float v = exp[i];
+ if (Float.isNaN(v)) {
+ if (isMathOperator(v)) {
+ s.append(toMathName(v));
+ } else {
+ s.append("[");
+ s.append(fromNaN(v));
+ s.append("]");
+ }
+ } else {
+ if (labels[i] != null) {
+ s.append(labels[i]);
+ }
+ s.append(v);
+ }
+ s.append(" ");
+ }
+ return s.toString();
+ }
+
+ static String toString(float[] exp, int sp) {
+ String[] str = new String[exp.length];
+ if (Float.isNaN(exp[sp])) {
+ int id = fromNaN(exp[sp]) - OFFSET;
+ switch (NO_OF_OPS[id]) {
+ case -1:
+ return "nop";
+ case 1:
+ return sNames.get(id) + "(" + toString(exp, sp + 1) + ") ";
+ case 2:
+ if (infix(id)) {
+ return "(" + toString(exp, sp + 1)
+ + sNames.get(id) + " "
+ + toString(exp, sp + 2) + ") ";
+ } else {
+ return sNames.get(id) + "("
+ + toString(exp, sp + 1) + ", "
+ + toString(exp, sp + 2) + ")";
+ }
+ case 3:
+ if (infix(id)) {
+ return "((" + toString(exp, sp + 1) + ") ? "
+ + toString(exp, sp + 2) + ":"
+ + toString(exp, sp + 3) + ")";
+ } else {
+ return sNames.get(id)
+ + "(" + toString(exp, sp + 1)
+ + ", " + toString(exp, sp + 2)
+ + ", " + toString(exp, sp + 3) + ")";
+ }
+ }
+ }
+ return Float.toString(exp[sp]);
+ }
+
+ static final int[] NO_OF_OPS = {
+ -1, // no op
+ 2, 2, 2, 2, 2, // + - * / %
+ 2, 2, 2, // min max, power
+ 1, 1, 1, 1, 1, 1, 1, 1, //sqrt,abs,CopySign,exp,floor,log,ln
+ 1, 1, 1, 1, 1, 1, 1, 2, // round,sin,cos,tan,asin,acos,atan,atan2
+ 3, 3, 3, 1, 1, 1, 1,
+ 0, 0, 0 // mad, ?:,
+ // a[0],a[1],a[2]
+ };
+
+ /**
+ * to be used by parser to determine if command is infix
+ * @param n
+ * @return
+ */
+ static boolean infix(int n) {
+ return ((n < 6) || (n == 25) || (n == 26));
+ }
+
+ /**
+ * Convert an id into a NaN object
+ * @param v
+ * @return
+ */
+ public static float asNan(int v) {
+ return Float.intBitsToFloat(v | -0x800000);
+ }
+
+ /**
+ * Get ID from a NaN float
+ * @param v
+ * @return
+ */
+ public static int fromNaN(float v) {
+ int b = Float.floatToRawIntBits(v);
+ return b & 0xFFFFF;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
new file mode 100644
index 0000000..0ea28a8
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+/**
+ * These are tools to use long Color as variables
+ * long colors are stored a 0xXXXXXXXX XXXXXX??
+ * in SRGB the colors are stored 0xAARRGGBB,00000000
+ * SRGB color sapce is color space 0
+ * Our Color will use color float with a
+ * Current android supports
+ * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020,
+ * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES,
+ * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
+ *
+ * Our color space will be 62 (MAX_ID-1). (0x3E)
+ * Storing the default value in SRGB format and having the
+ * id of the color between the ARGB values and the 62 i.e.
+ * 0xAARRGGBB 00 00 00 3E
+ *
+ */
+public class ColorUtils {
+ public static int RC_COLOR = 62;
+
+ long packRCColor(int defaultARGB, int id) {
+ long l = defaultARGB;
+ return (l << 32) | id << 8 | RC_COLOR;
+ }
+
+ boolean isRCColor(long color) {
+ return ((color & 0x3F) == 62);
+ }
+
+ int getID(long color) {
+ if (isRCColor(color)) {
+ return (int) ((color & 0xFFFFFF00) >> 8);
+ }
+ return -1;
+ }
+
+ /**
+ * get default color from long color
+ * @param color
+ * @return
+ */
+ public int getDefaultColor(long color) {
+ if (isRCColor(color)) {
+ return (int) (color >> 32);
+ }
+ if (((color & 0xFF) == 0)) {
+ return (int) (color >> 32);
+ }
+ return 0;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index 8051ef1..0512fa6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -50,7 +50,6 @@
return insert(key, value);
}
-
public T get(int key) {
int index = findKey(key);
if (index == -1) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
new file mode 100644
index 0000000..f4cd504
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+
+/**
+ * This defines the major id maps and ranges used by remote compose
+ * Generally ids ranging from 0 ... FFF (4095) are for ids
+ * 0x1000-0x1100 are used for path operations in PathData
+ * 0x1100-0x1200 are used for math operations in Animated float
+ * 0x
+ */
+public class NanMap {
+
+ public static final int MOVE = 0x1000;
+ public static final int LINE = 0x1001;
+ public static final int QUADRATIC = 0x1002;
+ public static final int CONIC = 0x1003;
+ public static final int CUBIC = 0x1004;
+ public static final int CLOSE = 0x1005;
+ public static final int DONE = 0x1006;
+ public static final float MOVE_NAN = Utils.asNan(MOVE);
+ public static final float LINE_NAN = Utils.asNan(LINE);
+ public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC);
+ public static final float CONIC_NAN = Utils.asNan(CONIC);
+ public static final float CUBIC_NAN = Utils.asNan(CUBIC);
+ public static final float CLOSE_NAN = Utils.asNan(CLOSE);
+ public static final float DONE_NAN = Utils.asNan(DONE);
+
+ /**
+ *
+ */
+ public static final float ADD = asNan(0x1100);
+ public static final float SUB = asNan(0x1101);
+ public static final float MUL = asNan(0x1102);
+ public static final float DIV = asNan(0x1103);
+ public static final float MOD = asNan(0x1104);
+ public static final float MIN = asNan(0x1105);
+ public static final float MAX = asNan(0x1106);
+ public static final float POW = asNan(0x1107);
+
+
+ /**
+ * Get ID from Nan float
+ * @param v
+ * @return
+ */
+ public static int fromNaN(float v) {
+ int b = Float.floatToRawIntBits(v);
+ return b & 0xFFFFF;
+ }
+
+ /**
+ * Given id return as a Nan float
+ * @param v
+ * @return
+ */
+ public static float asNan(int v) {
+ return Float.intBitsToFloat(v | 0xFF800000);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
new file mode 100644
index 0000000..8dd5405
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+import java.util.Arrays;
+
+/**
+ * Utilities for string manipulation
+ */
+public class StringUtils {
+ /**
+ * Converts a float into a string.
+ * Providing a defined number of characters before and after the
+ * decimal point.
+ *
+ * @param value The value to convert to string
+ * @param beforeDecimalPoint digits before the decimal point
+ * @param afterDecimalPoint digits after the decimal point
+ * @param pre character to pad width 0 = no pad typically ' ' or '0'
+ * @param post character to pad width 0 = no pad typically ' ' or '0'
+ * @return
+ */
+ public static String floatToString(float value,
+ int beforeDecimalPoint,
+ int afterDecimalPoint,
+ char pre, char post) {
+
+ int integerPart = (int) value;
+ float fractionalPart = value % 1;
+
+ // Convert integer part to string and pad with spaces
+ String integerPartString = String.valueOf(integerPart);
+ int iLen = integerPartString.length();
+ if (iLen < beforeDecimalPoint) {
+ int spacesToPad = beforeDecimalPoint - iLen;
+ if (pre != 0) {
+ char[] pad = new char[spacesToPad];
+ Arrays.fill(pad, pre);
+ integerPartString = new String(pad) + integerPartString;
+ }
+
+
+ } else if (iLen > beforeDecimalPoint) {
+ integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
+ }
+ if (afterDecimalPoint == 0) {
+ return integerPartString;
+ }
+ // Convert fractional part to string and pad with zeros
+
+ for (int i = 0; i < afterDecimalPoint; i++) {
+ fractionalPart *= 10;
+ }
+
+ fractionalPart = Math.round(fractionalPart);
+
+ for (int i = 0; i < afterDecimalPoint; i++) {
+ fractionalPart *= .1;
+ }
+
+ String fact = Float.toString(fractionalPart);
+ fact = fact.substring(2, Math.min(fact.length(), afterDecimalPoint + 2));
+ int trim = fact.length();
+ for (int i = fact.length() - 1; i >= 0; i--) {
+ if (fact.charAt(i) != '0') {
+ break;
+ }
+ trim--;
+ }
+ if (trim != fact.length()) {
+ fact = fact.substring(0, trim);
+ }
+ int len = fact.length();
+ if (post != 0 && len < afterDecimalPoint) {
+ char[] c = new char[afterDecimalPoint - len];
+ Arrays.fill(c, post);
+ fact = fact + new String(c);
+ }
+
+ return integerPartString + "." + fact;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
new file mode 100644
index 0000000..c3cd5ae9
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Provide a specific bouncing easing function
+ */
+public class BounceCurve extends Easing {
+ private static final float N1 = 7.5625f;
+ private static final float D1 = 2.75f;
+
+ BounceCurve(int type) {
+ mType = type;
+ }
+
+ @Override
+ public float get(float x) {
+ float t = x;
+ if (t < 0) {
+ return 0f;
+ }
+ if (t < 1 / D1) {
+ return 1 / (1 + 1 / D1) * (N1 * t * t + t);
+ } else if (t < 2 / D1) {
+ t -= 1.5f / D1;
+ return N1 * t * t + 0.75f;
+ } else if (t < 2.5 / D1) {
+ t -= 2.25f / D1;
+ return N1 * t * t + 0.9375f;
+ } else if (t <= 1) {
+ t -= 2.625f / D1;
+ return N1 * t * t + 0.984375f;
+ }
+ return 1f;
+ }
+
+ @Override
+ public float getDiff(float x) {
+ if (x < 0) {
+ return 0f;
+ }
+ if (x < 1 / D1) {
+ return 2 * N1 * x / (1 + 1 / D1) + 1 / (1 + 1 / D1);
+ } else if (x < 2 / D1) {
+ return 2 * N1 * (x - 1.5f / D1);
+ } else if (x < 2.5 / D1) {
+ return 2 * N1 * (x - 2.25f / D1);
+ } else if (x <= 1) {
+ return 2 * N1 * (x - 2.625f / D1);
+ }
+ return 0f;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
new file mode 100644
index 0000000..fd1ee03
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+class CubicEasing extends Easing {
+ float mType = 0;
+ float mX1 = 0f;
+ float mY1 = 0f;
+ float mX2 = 0f;
+ float mY2 = 0f;
+
+ private static final float[] STANDARD = {0.4f, 0.0f, 0.2f, 1f};
+ private static final float[] ACCELERATE = {0.4f, 0.05f, 0.8f, 0.7f};
+ private static final float[] DECELERATE = {0.0f, 0.0f, 0.2f, 0.95f};
+ private static final float[] LINEAR = {1f, 1f, 0f, 0f};
+ private static final float[] ANTICIPATE = {0.36f, 0f, 0.66f, -0.56f};
+ private static final float[] OVERSHOOT = {0.34f, 1.56f, 0.64f, 1f};
+
+ CubicEasing(int type) {
+ mType = type;
+ config(type);
+ }
+
+ CubicEasing(float x1, float y1, float x2, float y2) {
+ setup(x1, y1, x2, y2);
+ }
+
+ public void config(int type) {
+
+ switch (type) {
+ case CUBIC_STANDARD:
+ setup(STANDARD);
+ break;
+ case CUBIC_ACCELERATE:
+ setup(ACCELERATE);
+ break;
+ case CUBIC_DECELERATE:
+ setup(DECELERATE);
+ break;
+ case CUBIC_LINEAR:
+ setup(LINEAR);
+ break;
+ case CUBIC_ANTICIPATE:
+ setup(ANTICIPATE);
+ break;
+ case CUBIC_OVERSHOOT:
+ setup(OVERSHOOT);
+ break;
+ }
+ mType = type;
+ }
+
+ void setup(float[] values) {
+ setup(values[0], values[1], values[2], values[3]);
+ }
+
+ void setup(float x1, float y1, float x2, float y2) {
+ mX1 = x1;
+ mY1 = y1;
+ mX2 = x2;
+ mY2 = y2;
+ }
+
+ private float getX(float t) {
+ float t1 = 1 - t;
+ // no need for because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t)
+ float f1 = 3 * t1 * t1 * t;
+ float f2 = 3 * t1 * t * t;
+ float f3 = t * t * t;
+ return mX1 * f1 + mX2 * f2 + f3;
+ }
+
+ private float getY(float t) {
+ float t1 = 1 - t;
+ // no need for testing because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t)
+ float f1 = 3 * t1 * t1 * t;
+ float f2 = 3 * t1 * t * t;
+ float f3 = t * t * t;
+ return mY1 * f1 + mY2 * f2 + f3;
+ }
+
+ private float getDiffX(float t) {
+ float t1 = 1 - t;
+ return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
+ }
+
+ private float getDiffY(float t) {
+ float t1 = 1 - t;
+ return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
+ }
+
+ /**
+ * binary search for the region and linear interpolate the answer
+ */
+ public float getDiff(float x) {
+ float t = 0.5f;
+ float range = 0.5f;
+ while (range > D_ERROR) {
+ float tx = getX(t);
+ range *= 0.5;
+ if (tx < x) {
+ t += range;
+ } else {
+ t -= range;
+ }
+ }
+ float x1 = getX(t - range);
+ float x2 = getX(t + range);
+ float y1 = getY(t - range);
+ float y2 = getY(t + range);
+ return (y2 - y1) / (x2 - x1);
+ }
+
+ /**
+ * binary search for the region and linear interpolate the answer
+ */
+ public float get(float x) {
+ if (x <= 0.0f) {
+ return 0f;
+ }
+ if (x >= 1.0f) {
+ return 1.0f;
+ }
+ float t = 0.5f;
+ float range = 0.5f;
+ while (range > ERROR) {
+ float tx = getX(t);
+ range *= 0.5f;
+ if (tx < x) {
+ t += range;
+ } else {
+ t -= range;
+ }
+ }
+ float x1 = getX(t - range);
+ float x2 = getX(t + range);
+ float y1 = getY(t - range);
+ float y2 = getY(t + range);
+ return (y2 - y1) * (x - x1) / (x2 - x1) + y1;
+ }
+
+ private static final float ERROR = 0.01f;
+ private static final float D_ERROR = 0.0001f;
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
new file mode 100644
index 0000000..4ed9550
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * The standard interface to Easing functions
+ */
+public abstract class Easing {
+ int mType;
+ /**
+ * get the value at point x
+ */
+ public abstract float get(float x);
+
+ /**
+ * get the slope of the easing function at at x
+ */
+ public abstract float getDiff(float x);
+
+ public int getType() {
+ return mType;
+ }
+
+ public static final int CUBIC_STANDARD = 1;
+ public static final int CUBIC_ACCELERATE = 2;
+ public static final int CUBIC_DECELERATE = 3;
+ public static final int CUBIC_LINEAR = 4;
+ public static final int CUBIC_ANTICIPATE = 5;
+ public static final int CUBIC_OVERSHOOT = 6;
+ public static final int CUBIC_CUSTOM = 11;
+ public static final int SPLINE_CUSTOM = 12;
+ public static final int EASE_OUT_BOUNCE = 13;
+ public static final int EASE_OUT_ELASTIC = 14;
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
new file mode 100644
index 0000000..e269583
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Provide a bouncing Easing function
+ */
+public class ElasticOutCurve extends Easing {
+ private static final float F_PI = (float) Math.PI;
+ private static final float C4 = 2 * F_PI / 3;
+ private static final float TWENTY_PI = 20 * F_PI;
+ private static final float LOG_8 = (float) Math.log(8.0f);
+
+ @Override
+ public float get(float x) {
+ if (x <= 0) {
+ return 0.0f;
+ }
+ if (x >= 1) {
+ return 1.0f;
+ } else
+ return (float) (Math.pow(2.0f, -10 * x)
+ * Math.sin((x * 10 - 0.75f) * C4) + 1);
+ }
+
+ @Override
+ public float getDiff(float x) {
+ if (x < 0 || x > 1) {
+ return 0.0f;
+ } else
+ return (float) ((5 * Math.pow(2.0f, (1 - 10 * x))
+ * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2
+ * F_PI * Math.sin(TWENTY_PI * x / 3))
+ / 3));
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
new file mode 100644
index 0000000..4f484de
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Support Animation of the FloatExpression
+ */
+public class FloatAnimation extends Easing {
+ float[] mSpec;
+ // mSpec[0] = duration
+ // int(mSpec[1]) = num_of_param << 16 | type
+ // mSpec[2..1+num_of_param] params
+ // mSpec[2+num_of_param] starting Value
+ Easing mEasingCurve;
+ private int mType = CUBIC_STANDARD;
+ private float mDuration = 1;
+ private float mWrap = Float.NaN;
+ private float mInitialValue = Float.NaN;
+ private float mTargetValue = Float.NaN;
+ private float mScale = 1;
+ float mOffset = 0;
+
+ @Override
+ public String toString() {
+
+ String str = "type " + mType;
+ if (!Float.isNaN(mInitialValue)) {
+ str += " " + mInitialValue;
+ }
+ if (!Float.isNaN(mTargetValue)) {
+ str += " -> " + mTargetValue;
+ }
+ if (!Float.isNaN(mWrap)) {
+ str += " % " + mWrap;
+ }
+
+ return str;
+ }
+
+ public FloatAnimation() {
+ }
+
+ public FloatAnimation(float... description) {
+ setAnimationDescription(description);
+ }
+
+ public FloatAnimation(int type,
+ float duration,
+ float[] description,
+ float initialValue,
+ float wrap) {
+ setAnimationDescription(packToFloatArray(duration,
+ type, description, initialValue, wrap));
+ }
+
+ /**
+ * packs spec into a float array
+ *
+ * @param duration
+ * @param type
+ * @param spec
+ * @param initialValue
+ * @return
+ */
+ public static float[] packToFloatArray(float duration,
+ int type,
+ float[] spec,
+ float initialValue,
+ float wrap) {
+ int count = 0;
+
+ if (!Float.isNaN(initialValue)) {
+ count++;
+ }
+ if (spec != null) {
+ count++;
+ }
+ if (spec != null || type != CUBIC_STANDARD) {
+ count++;
+ count += (spec == null) ? 0 : spec.length;
+ }
+ if (duration != 1 || count > 0) {
+ count++;
+ }
+ if (!Float.isNaN(initialValue)) {
+ count++;
+ }
+ if (!Float.isNaN(wrap)) {
+ count++;
+ }
+ float[] ret = new float[count];
+ int pos = 0;
+ int specLen = (spec == null) ? 0 : spec.length;
+
+ if (ret.length > 0) {
+ ret[pos++] = duration;
+
+ }
+ if (ret.length > 1) {
+ int wrapBit = (Float.isNaN(wrap)) ? 0 : 1;
+ int initBit = (Float.isNaN(initialValue)) ? 0 : 2;
+ int bits = type | ((wrapBit | initBit) << 8);
+ ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits);
+ }
+
+ if (specLen > 0) {
+ System.arraycopy(spec, 0, ret, pos, spec.length);
+ pos += spec.length;
+ }
+ if (!Float.isNaN(initialValue)) {
+ ret[pos++] = initialValue;
+ }
+ if (!Float.isNaN(wrap)) {
+ ret[pos] = wrap;
+ }
+ return ret;
+ }
+
+ /**
+ * Create an animation based on a float encoding of the animation
+ * @param description
+ */
+ public void setAnimationDescription(float[] description) {
+ mSpec = description;
+ mDuration = (mSpec.length == 0) ? 1 : mSpec[0];
+ int len = 0;
+ if (mSpec.length > 1) {
+ int num_type = Float.floatToRawIntBits(mSpec[1]);
+ mType = num_type & 0xFF;
+ boolean wrap = ((num_type >> 8) & 0x1) > 0;
+ boolean init = ((num_type >> 8) & 0x2) > 0;
+ len = (num_type >> 16) & 0xFFFF;
+ int off = 2 + len;
+ if (init) {
+ mInitialValue = mSpec[off++];
+ }
+ if (wrap) {
+ mWrap = mSpec[off];
+ }
+ }
+ create(mType, description, 2, len);
+ }
+
+ private void create(int type, float[] params, int offset, int len) {
+ switch (type) {
+ case CUBIC_STANDARD:
+ case CUBIC_ACCELERATE:
+ case CUBIC_DECELERATE:
+ case CUBIC_LINEAR:
+ case CUBIC_ANTICIPATE:
+ case CUBIC_OVERSHOOT:
+ mEasingCurve = new CubicEasing(type);
+ break;
+ case CUBIC_CUSTOM:
+ mEasingCurve = new CubicEasing(params[offset + 0],
+ params[offset + 1],
+ params[offset + 2],
+ params[offset + 3]
+ );
+ break;
+ case EASE_OUT_BOUNCE:
+ mEasingCurve = new BounceCurve(type);
+ break;
+ case EASE_OUT_ELASTIC:
+ mEasingCurve = new ElasticOutCurve();
+ break;
+ case SPLINE_CUSTOM:
+ mEasingCurve = new StepCurve(params, offset, len);
+ break;
+ }
+ }
+
+ /**
+ * Get the duration the interpolate is to take
+ * @return duration in seconds
+ */
+ public float getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Set the initial Value
+ * @param value
+ */
+ public void setInitialValue(float value) {
+
+ if (Float.isNaN(mWrap)) {
+ mInitialValue = value;
+ } else {
+ mInitialValue = value % mWrap;
+ }
+ setScaleOffset();
+ }
+
+ /**
+ * Set the target value to interpolate to
+ * @param value
+ */
+ public void setTargetValue(float value) {
+ if (Float.isNaN(mWrap)) {
+ mTargetValue = value;
+ } else {
+ if (Math.abs((value % mWrap) + mWrap - mInitialValue)
+ < Math.abs((value % mWrap) - mInitialValue)) {
+ mTargetValue = (value % mWrap) + mWrap;
+
+ } else {
+ mTargetValue = value % mWrap;
+ }
+ }
+ setScaleOffset();
+ }
+
+ public float getTargetValue() {
+ return mTargetValue;
+ }
+
+ private void setScaleOffset() {
+ if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) {
+ mScale = (mTargetValue - mInitialValue);
+ mOffset = mInitialValue;
+ } else {
+ mScale = 1;
+ mOffset = 0;
+ }
+ }
+
+ /**
+ * get the value at time t in seconds since start
+ */
+ public float get(float t) {
+ return mEasingCurve.get(t / mDuration)
+ * (mTargetValue - mInitialValue) + mInitialValue;
+ }
+
+ /**
+ * get the slope of the easing function at at x
+ */
+ public float getDiff(float t) {
+ return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
+ }
+
+ public float getInitialValue() {
+ return mInitialValue;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
new file mode 100644
index 0000000..693deaf
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Provides and interface to create easing functions
+ */
+public class GeneralEasing extends Easing{
+ float[] mEasingData = new float[0];
+ Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD);
+
+ /**
+ * Set the curve based on the float encoding of it
+ * @param data
+ */
+ public void setCurveSpecification(float[] data) {
+ mEasingData = data;
+ createEngine();
+ }
+
+ public float[] getCurveSpecification() {
+ return mEasingData;
+ }
+
+ void createEngine() {
+ int type = Float.floatToRawIntBits(mEasingData[0]);
+ switch (type) {
+ case CUBIC_STANDARD:
+ case CUBIC_ACCELERATE:
+ case CUBIC_DECELERATE:
+ case CUBIC_LINEAR:
+ case CUBIC_ANTICIPATE:
+ case CUBIC_OVERSHOOT:
+ mEasingCurve = new CubicEasing(type);
+ break;
+ case CUBIC_CUSTOM:
+ mEasingCurve = new CubicEasing(mEasingData[1],
+ mEasingData[2],
+ mEasingData[3],
+ mEasingData[5]
+ );
+ break;
+ case EASE_OUT_BOUNCE:
+ mEasingCurve = new BounceCurve(type);
+ break;
+ }
+ }
+
+ /**
+ * get the value at point x
+ */
+ public float get(float x) {
+ return mEasingCurve.get(x);
+ }
+
+ /**
+ * get the slope of the easing function at at x
+ */
+ public float getDiff(float x) {
+ return mEasingCurve.getDiff(x);
+ }
+
+ public int getType() {
+ return mEasingCurve.getType();
+ }
+
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
new file mode 100644
index 0000000..23930b9
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+import java.util.Arrays;
+
+/**
+ * This performs a spline interpolation in multiple dimensions
+ *
+ *
+ */
+public class MonotonicCurveFit {
+ private static final String TAG = "MonotonicCurveFit";
+ private double[] mT;
+ private double[][] mY;
+ private double[][] mTangent;
+ private boolean mExtrapolate = true;
+ double[] mSlopeTemp;
+
+ /**
+ * create a collection of curves
+ * @param time the point along the curve
+ * @param y the parameter at those points
+ */
+ public MonotonicCurveFit(double[] time, double[][] y) {
+ final int n = time.length;
+ final int dim = y[0].length;
+ mSlopeTemp = new double[dim];
+ double[][] slope = new double[n - 1][dim]; // could optimize this out
+ double[][] tangent = new double[n][dim];
+ for (int j = 0; j < dim; j++) {
+ for (int i = 0; i < n - 1; i++) {
+ double dt = time[i + 1] - time[i];
+ slope[i][j] = (y[i + 1][j] - y[i][j]) / dt;
+ if (i == 0) {
+ tangent[i][j] = slope[i][j];
+ } else {
+ tangent[i][j] = (slope[i - 1][j] + slope[i][j]) * 0.5f;
+ }
+ }
+ tangent[n - 1][j] = slope[n - 2][j];
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ for (int j = 0; j < dim; j++) {
+ if (slope[i][j] == 0.) {
+ tangent[i][j] = 0.;
+ tangent[i + 1][j] = 0.;
+ } else {
+ double a = tangent[i][j] / slope[i][j];
+ double b = tangent[i + 1][j] / slope[i][j];
+ double h = Math.hypot(a, b);
+ if (h > 9.0) {
+ double t = 3. / h;
+ tangent[i][j] = t * a * slope[i][j];
+ tangent[i + 1][j] = t * b * slope[i][j];
+ }
+ }
+ }
+ }
+ mT = time;
+ mY = y;
+ mTangent = tangent;
+ }
+
+ /**
+ * Get the position of all curves at time t
+ * @param t
+ * @param v
+ */
+ public void getPos(double t, double[] v) {
+ final int n = mT.length;
+ final int dim = mY[0].length;
+ if (mExtrapolate) {
+ if (t <= mT[0]) {
+ getSlope(mT[0], mSlopeTemp);
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[0][j] + (t - mT[0]) * mSlopeTemp[j];
+ }
+ return;
+ }
+ if (t >= mT[n - 1]) {
+ getSlope(mT[n - 1], mSlopeTemp);
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j];
+ }
+ return;
+ }
+ } else {
+ if (t <= mT[0]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[0][j];
+ }
+ return;
+ }
+ if (t >= mT[n - 1]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[n - 1][j];
+ }
+ return;
+ }
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ if (t == mT[i]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = mY[i][j];
+ }
+ }
+ if (t < mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ for (int j = 0; j < dim; j++) {
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ v[j] = interpolate(h, x, y1, y2, t1, t2);
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Get the position of all curves at time t
+ * @param t
+ * @param v
+ */
+ public void getPos(double t, float[] v) {
+ final int n = mT.length;
+ final int dim = mY[0].length;
+ if (mExtrapolate) {
+ if (t <= mT[0]) {
+ getSlope(mT[0], mSlopeTemp);
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) (mY[0][j] + (t - mT[0]) * mSlopeTemp[j]);
+ }
+ return;
+ }
+ if (t >= mT[n - 1]) {
+ getSlope(mT[n - 1], mSlopeTemp);
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) (mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]);
+ }
+ return;
+ }
+ } else {
+ if (t <= mT[0]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) mY[0][j];
+ }
+ return;
+ }
+ if (t >= mT[n - 1]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) mY[n - 1][j];
+ }
+ return;
+ }
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ if (t == mT[i]) {
+ for (int j = 0; j < dim; j++) {
+ v[j] = (float) mY[i][j];
+ }
+ }
+ if (t < mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ for (int j = 0; j < dim; j++) {
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ v[j] = (float) interpolate(h, x, y1, y2, t1, t2);
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Get the position of the jth curve at time t
+ * @param t
+ * @param j
+ * @return
+ */
+ public double getPos(double t, int j) {
+ final int n = mT.length;
+ if (mExtrapolate) {
+ if (t <= mT[0]) {
+ return mY[0][j] + (t - mT[0]) * getSlope(mT[0], j);
+ }
+ if (t >= mT[n - 1]) {
+ return mY[n - 1][j] + (t - mT[n - 1]) * getSlope(mT[n - 1], j);
+ }
+ } else {
+ if (t <= mT[0]) {
+ return mY[0][j];
+ }
+ if (t >= mT[n - 1]) {
+ return mY[n - 1][j];
+ }
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ if (t == mT[i]) {
+ return mY[i][j];
+ }
+ if (t < mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ return interpolate(h, x, y1, y2, t1, t2);
+
+ }
+ }
+ return 0; // should never reach here
+ }
+
+ /**
+ * Get the slope of all the curves at position t
+ * @param t
+ * @param v
+ */
+ public void getSlope(double t, double[] v) {
+ final int n = mT.length;
+ int dim = mY[0].length;
+ if (t <= mT[0]) {
+ t = mT[0];
+ } else if (t >= mT[n - 1]) {
+ t = mT[n - 1];
+ }
+
+ for (int i = 0; i < n - 1; i++) {
+ if (t <= mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ for (int j = 0; j < dim; j++) {
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ v[j] = diff(h, x, y1, y2, t1, t2) / h;
+ }
+ break;
+ }
+ }
+ return;
+ }
+
+ /**
+ * Get the slope of the j curve at position t
+ * @param t
+ * @param j
+ * @return
+ */
+ public double getSlope(double t, int j) {
+ final int n = mT.length;
+
+ if (t < mT[0]) {
+ t = mT[0];
+ } else if (t >= mT[n - 1]) {
+ t = mT[n - 1];
+ }
+ for (int i = 0; i < n - 1; i++) {
+ if (t <= mT[i + 1]) {
+ double h = mT[i + 1] - mT[i];
+ double x = (t - mT[i]) / h;
+ double y1 = mY[i][j];
+ double y2 = mY[i + 1][j];
+ double t1 = mTangent[i][j];
+ double t2 = mTangent[i + 1][j];
+ return diff(h, x, y1, y2, t1, t2) / h;
+ }
+ }
+ return 0; // should never reach here
+ }
+
+ public double[] getTimePoints() {
+ return mT;
+ }
+
+ /**
+ * Cubic Hermite spline
+ */
+ private static double interpolate(double h,
+ double x,
+ double y1,
+ double y2,
+ double t1,
+ double t2) {
+ double x2 = x * x;
+ double x3 = x2 * x;
+ return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1
+ + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2
+ + h * t1 * x;
+ }
+
+ /**
+ * Cubic Hermite spline slope differentiated
+ */
+ private static double diff(double h, double x, double y1, double y2, double t1, double t2) {
+ double x2 = x * x;
+ return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2
+ + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1;
+ }
+
+ /**
+ * This builds a monotonic spline to be used as a wave function
+ */
+ public static MonotonicCurveFit buildWave(String configString) {
+ // done this way for efficiency
+ String str = configString;
+ double[] values = new double[str.length() / 2];
+ int start = configString.indexOf('(') + 1;
+ int off1 = configString.indexOf(',', start);
+ int count = 0;
+ while (off1 != -1) {
+ String tmp = configString.substring(start, off1).trim();
+ values[count++] = Double.parseDouble(tmp);
+ off1 = configString.indexOf(',', start = off1 + 1);
+ }
+ off1 = configString.indexOf(')', start);
+ String tmp = configString.substring(start, off1).trim();
+ values[count++] = Double.parseDouble(tmp);
+
+ return buildWave(Arrays.copyOf(values, count));
+ }
+
+ private static MonotonicCurveFit buildWave(double[] values) {
+ int length = values.length * 3 - 2;
+ int len = values.length - 1;
+ double gap = 1.0 / len;
+ double[][] points = new double[length][1];
+ double[] time = new double[length];
+ for (int i = 0; i < values.length; i++) {
+ double v = values[i];
+ points[i + len][0] = v;
+ time[i + len] = i * gap;
+ if (i > 0) {
+ points[i + len * 2][0] = v + 1;
+ time[i + len * 2] = i * gap + 1;
+
+ points[i - 1][0] = v - 1 - gap;
+ time[i - 1] = i * gap + -1 - gap;
+ }
+ }
+
+ return new MonotonicCurveFit(time, points);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
new file mode 100644
index 0000000..6ed6548
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+
+/**
+ * This class translates a series of floating point values into a continuous
+ * curve for use in an easing function including quantize functions
+ * it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)" it should start at 0 and end at one 1
+ */
+public class StepCurve extends Easing {
+ private static final boolean DEBUG = false;
+ MonotonicCurveFit mCurveFit;
+
+ public StepCurve(float[] params, int offset, int len) {
+ mCurveFit = genSpline(params, offset, len);
+ }
+
+ private static MonotonicCurveFit genSpline(float[] values, int off, int arrayLen) {
+ int length = arrayLen * 3 - 2;
+ int len = arrayLen - 1;
+ double gap = 1.0 / len;
+ double[][] points = new double[length][1];
+ double[] time = new double[length];
+ for (int i = 0; i < arrayLen; i++) {
+ double v = values[i + off];
+ points[i + len][0] = v;
+ time[i + len] = i * gap;
+ if (i > 0) {
+ points[i + len * 2][0] = v + 1;
+ time[i + len * 2] = i * gap + 1;
+
+ points[i - 1][0] = v - 1 - gap;
+ time[i - 1] = i * gap + -1 - gap;
+ }
+ }
+
+ MonotonicCurveFit ms = new MonotonicCurveFit(time, points);
+
+ return ms;
+ }
+
+ @Override
+ public float getDiff(float x) {
+ return (float) mCurveFit.getSlope(x, 0);
+ }
+
+
+ @Override
+ public float get(float x) {
+ return (float) mCurveFit.getPos(x, 0);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index bcda27a..d1c4d46 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -79,6 +79,15 @@
}
/**
+ * The delay in milliseconds to next repaint -1 = not needed 0 = asap
+ *
+ * @return delay in milliseconds to next repaint or -1
+ */
+ public int needsRepaint() {
+ return mDocument.needsRepaint();
+ }
+
+ /**
* Returns true if the document can be displayed given this version of the player
*
* @param majorVersion the max major version supported by the player
@@ -89,5 +98,10 @@
return mDocument.canBeDisplayed(majorVersion, minorVersion, capabilities);
}
+ @Override
+ public String toString() {
+ return "Document{\n"
+ + mDocument + '}';
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index d0d6e69..ecb68bb 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -26,6 +26,7 @@
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.graphics.RuntimeShader;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.graphics.Typeface;
@@ -33,6 +34,8 @@
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.ClipPath;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges;
@@ -43,6 +46,7 @@
public class AndroidPaintContext extends PaintContext {
Paint mPaint = new Paint();
Canvas mCanvas;
+ Rect mTmpRect = new Rect(); // use in calculation of bounds
public AndroidPaintContext(RemoteContext context, Canvas canvas) {
super(context);
@@ -177,6 +181,22 @@
}
@Override
+ public void getTextBounds(int textId, int start, int end, boolean monospace, float[] bounds) {
+ String str = getText(textId);
+ if (end == -1) {
+ end = str.length();
+ }
+
+ mPaint.getTextBounds(str, start, end, mTmpRect);
+
+ bounds[0] = mTmpRect.left;
+ bounds[1] = mTmpRect.top;
+ bounds[2] = monospace ? (mPaint.measureText(str, start, end) - mTmpRect.left)
+ : mTmpRect.right;
+ bounds[3] = mTmpRect.bottom;
+ }
+
+ @Override
public void drawTextRun(int textID,
int start,
int end,
@@ -185,7 +205,16 @@
float x,
float y,
boolean rtl) {
- String textToPaint = getText(textID).substring(start, end);
+
+ String textToPaint = getText(textID);
+ if (end == -1) {
+ if (start != 0) {
+ textToPaint = textToPaint.substring(start);
+ }
+ } else {
+ textToPaint = textToPaint.substring(start, end);
+ }
+
mCanvas.drawText(textToPaint, x, y, mPaint);
}
@@ -308,7 +337,7 @@
@Override
public void applyPaint(PaintBundle mPaintData) {
- mPaintData.applyPaintChange(new PaintChanges() {
+ mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
@Override
public void setTextSize(float size) {
mPaint.setTextSize(size);
@@ -361,10 +390,8 @@
}
}
-
}
-
@Override
public void setStrokeWidth(float width) {
mPaint.setStrokeWidth(width);
@@ -386,13 +413,37 @@
}
@Override
- public void setShader(int shader, String shaderString) {
-
+ public void setShader(int shaderId) {
+ // TODO this stuff should check the shader creation
+ if (shaderId == 0) {
+ mPaint.setShader(null);
+ return;
+ }
+ ShaderData data = getShaderData(shaderId);
+ RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
+ String[] names = data.getUniformFloatNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ float[] val = data.getUniformFloats(name);
+ shader.setFloatUniform(name, val);
+ }
+ names = data.getUniformIntegerNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int[] val = data.getUniformInts(name);
+ shader.setIntUniform(name, val);
+ }
+ names = data.getUniformBitmapNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int val = data.getUniformBitmapId(name);
+ }
+ mPaint.setShader(shader);
}
@Override
public void setImageFilterQuality(int quality) {
- System.out.println(">>>>>>>>>>>> ");
+ Utils.log(" quality =" + quality);
}
@Override
@@ -420,7 +471,6 @@
mPaint.setFilterBitmap(filter);
}
-
@Override
public void setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
@@ -437,7 +487,6 @@
case PaintBundle.COLOR_FILTER:
mPaint.setColorFilter(null);
- System.out.println(">>>>>>>>>>>>> CLEAR!!!!");
break;
}
}
@@ -446,12 +495,11 @@
}
}
- Shader.TileMode[] mTilesModes = new Shader.TileMode[]{
+ Shader.TileMode[] mTileModes = new Shader.TileMode[]{
Shader.TileMode.CLAMP,
Shader.TileMode.REPEAT,
Shader.TileMode.MIRROR};
-
@Override
public void setLinearGradient(int[] colors,
float[] stops,
@@ -463,7 +511,7 @@
mPaint.setShader(new LinearGradient(startX,
startY,
endX,
- endY, colors, stops, mTilesModes[tileMode]));
+ endY, colors, stops, mTileModes[tileMode]));
}
@@ -475,7 +523,7 @@
float radius,
int tileMode) {
mPaint.setShader(new RadialGradient(centerX, centerY, radius,
- colors, stops, mTilesModes[tileMode]));
+ colors, stops, mTileModes[tileMode]));
}
@Override
@@ -490,7 +538,6 @@
@Override
public void setColorFilter(int color, int mode) {
PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
- System.out.println("setting color filter to " + pmode.name());
if (pmode != null) {
mPaint.setColorFilter(
new PorterDuffColorFilter(color, pmode));
@@ -500,10 +547,10 @@
}
@Override
- public void mtrixScale(float scaleX,
- float scaleY,
- float centerX,
- float centerY) {
+ public void matrixScale(float scaleX,
+ float scaleY,
+ float centerX,
+ float centerY) {
if (Float.isNaN(centerX)) {
mCanvas.scale(scaleX, scaleY);
} else {
@@ -556,6 +603,11 @@
}
}
+ @Override
+ public void reset() {
+ mPaint.reset();
+ }
+
private Path getPath(int path1Id,
int path2Id,
float tween,
@@ -599,5 +651,9 @@
private String getText(int id) {
return (String) mContext.mRemoteComposeState.getFromId(id);
}
+
+ private ShaderData getShaderData(int id) {
+ return (ShaderData) mContext.mRemoteComposeState.getFromId(id);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index 270e96f..6e4893b 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -20,10 +20,15 @@
import android.graphics.Canvas;
import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
+
+import java.util.HashMap;
/**
* An implementation of Context for Android.
- *
+ * <p>
* This is used to play the RemoteCompose operations on Android.
*/
class AndroidRemoteContext extends RemoteContext {
@@ -33,6 +38,7 @@
mPaintContext = new AndroidPaintContext(this, canvas);
} else {
// need to make sure to update the canvas for the current one
+ mPaintContext.reset();
((AndroidPaintContext) mPaintContext).setCanvas(canvas);
}
mWidth = canvas.getWidth();
@@ -50,13 +56,32 @@
}
}
+ static class VarName {
+ String mName;
+ int mId;
+ int mType;
+
+ VarName(String name, int id, int type) {
+ mName = name;
+ mId = id;
+ mType = type;
+ }
+ }
+
+ HashMap<String, VarName> mVarNameHashMap = new HashMap<>();
+
+ @Override
+ public void loadVariableName(String varName, int varId, int varType) {
+ mVarNameHashMap.put(varName, new VarName(varName, varId, varType));
+ }
+
/**
* Decode a byte array into an image and cache it using the given imageId
*
- * @oaram imageId the id of the image
- * @param width with of image to be loaded
+ * @param width with of image to be loaded
* @param height height of image to be loaded
* @param bitmap a byte array containing the image information
+ * @oaram imageId the id of the image
*/
@Override
public void loadBitmap(int imageId, int width, int height, byte[] bitmap) {
@@ -70,14 +95,66 @@
public void loadText(int id, String text) {
if (!mRemoteComposeState.containsId(id)) {
mRemoteComposeState.cache(id, text);
+ } else {
+ mRemoteComposeState.update(id, text);
}
}
+ @Override
+ public String getText(int id) {
+ return (String) mRemoteComposeState.getFromId(id);
+ }
+
+ @Override
+ public void loadFloat(int id, float value) {
+ mRemoteComposeState.updateFloat(id, value);
+ }
+
+
+ @Override
+ public void loadColor(int id, int color) {
+ mRemoteComposeState.updateColor(id, color);
+ }
+
+ @Override
+ public void loadAnimatedFloat(int id, FloatExpression animatedFloat) {
+ mRemoteComposeState.cache(id, animatedFloat);
+ }
+
+ @Override
+ public void loadShader(int id, ShaderData value) {
+ mRemoteComposeState.cache(id, value);
+ }
+
+ @Override
+ public float getFloat(int id) {
+ return (float) mRemoteComposeState.getFloat(id);
+ }
+
+ @Override
+ public int getColor(int id) {
+ return mRemoteComposeState.getColor(id);
+ }
+
+ @Override
+ public void listensTo(int id, VariableSupport variableSupport) {
+ mRemoteComposeState.listenToVar(id, variableSupport);
+ }
+
+ @Override
+ public int updateOps() {
+ return mRemoteComposeState.getOpsToUpdate(this);
+ }
+
+ @Override
+ public ShaderData getShader(int id) {
+ return (ShaderData) mRemoteComposeState.getFromId(id);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////////
// Click handling
///////////////////////////////////////////////////////////////////////////////////////////////
-
@Override
public void addClickArea(int id,
int contentDescriptionId,
@@ -87,7 +164,7 @@
float bottom,
int metadataId) {
String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId);
- String metadata = (String) mRemoteComposeState.getFromId(metadataId);
+ String metadata = (String) mRemoteComposeState.getFromId(metadataId);
mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
index 672dae3..329178a 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
@@ -20,7 +20,6 @@
import android.graphics.Paint;
import android.view.View;
-
/**
* Implementation for the click handling
*/
@@ -40,7 +39,6 @@
setContentDescription(contentDescription);
}
-
public void setDebug(boolean value) {
if (mDebug != value) {
mDebug = value;
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index a3bb73e..97d23c8 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -85,6 +85,7 @@
mDocument.initializeContext(mARContext);
setContentDescription(mDocument.getDocument().getContentDescription());
requestLayout();
+ invalidate();
}
AndroidRemoteContext mARContext = new AndroidRemoteContext();
@@ -119,8 +120,7 @@
removeAllViews();
}
-
- public interface ClickCallbacks {
+ public interface ClickCallbacks {
void click(int id, String metadata);
}
@@ -213,6 +213,9 @@
setMeasuredDimension(w, h);
}
+ private int mCount;
+ private long mTime = System.nanoTime();
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -224,6 +227,17 @@
mARContext.mWidth = getWidth();
mARContext.mHeight = getHeight();
mDocument.paint(mARContext, mTheme);
+ if (mDebug) {
+ mCount++;
+ if (System.nanoTime() - mTime > 1000000000L) {
+ System.out.println(" count " + mCount + " fps");
+ mCount = 0;
+ mTime = System.nanoTime();
+ }
+ }
+ if (mDocument.needsRepaint() > 0) {
+ invalidate();
+ }
}
}