Merge "add suport for integer operations" into main
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 fc8668e..4b8dbf6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -37,6 +37,7 @@
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.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
@@ -54,6 +55,8 @@
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;
+import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
+import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
/**
* List of operations supported in a RemoteCompose document
@@ -109,6 +112,9 @@
public static final int TEXT_MERGE = 136;
public static final int NAMED_VARIABLE = 137;
public static final int COLOR_CONSTANT = 138;
+ public static final int DATA_INT = 140;
+ public static final int DATA_BOOLEAN = 143;
+ public static final int INTEGER_EXPRESSION = 144;
/////////////////////////////////////////======================
public static IntMap<CompanionOperation> map = new IntMap<>();
@@ -153,6 +159,9 @@
map.put(TEXT_MERGE, TextMerge.COMPANION);
map.put(NAMED_VARIABLE, NamedVariable.COMPANION);
map.put(COLOR_CONSTANT, ColorConstant.COMPANION);
+ map.put(DATA_INT, IntegerConstant.COMPANION);
+ map.put(INTEGER_EXPRESSION, IntegerExpression.COMPANION);
+ map.put(DATA_BOOLEAN, BooleanConstant.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 ecd0efc..6d8a442 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -59,6 +59,16 @@
public abstract void drawRect(float left, float top, float right, float bottom);
+ /**
+ * this caches the paint to a paint stack
+ */
+ public abstract void savePaint();
+
+ /**
+ * This restores the paint form the paint stack
+ */
+ public abstract void restorePaint();
+
public abstract void drawRoundRect(float left,
float top,
float right,
@@ -119,6 +129,10 @@
float start,
float stop);
+ /**
+ * This applies changes to the current paint
+ * @param mPaintData the list of changes
+ */
public abstract void applyPaint(PaintBundle mPaintData);
/**
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 d462c7d..f5f155e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -37,6 +37,7 @@
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.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
import com.android.internal.widget.remotecompose.core.operations.MatrixSave;
@@ -55,6 +56,7 @@
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 com.android.internal.widget.remotecompose.core.types.IntegerConstant;
import java.io.File;
import java.io.FileInputStream;
@@ -876,6 +878,27 @@
return Utils.asNan(id);
}
+
+ /**
+ * Add a Integer return an id number pointing to that float.
+ * @param value
+ * @return
+ */
+ public int addInteger(int value) {
+ int id = mRemoteComposeState.cacheInteger(value);
+ IntegerConstant.COMPANION.apply(mBuffer, id, value);
+ return id;
+ }
+
+ /**
+ * Add a IntegerId as float ID.
+ * @param id id to be converted
+ * @return
+ */
+ public float asFloatId(int id) {
+ 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
@@ -901,6 +924,18 @@
}
/**
+ * Add and integer expression
+ * @param mask defines which elements are operators or variables
+ * @param value array of values to calculate maximum 32
+ * @return
+ */
+ public int addIntegerExpression(int mask, int[] value) {
+ int id = mRemoteComposeState.cache(value);
+ IntegerExpression.COMPANION.apply(mBuffer, id, mask, value);
+ return id;
+ }
+
+ /**
* Add a simple color
* @param color
* @return id that represents that color
@@ -1038,5 +1073,6 @@
NamedVariable.COMPANION.apply(mBuffer, id,
NamedVariable.COLOR_TYPE, name);
}
+
}
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 bfe67c8..6b06a54 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -21,6 +21,8 @@
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.IntFloatMap;
+import com.android.internal.widget.remotecompose.core.operations.utilities.IntIntMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
import java.util.ArrayList;
@@ -37,16 +39,12 @@
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_COLORS]; // efficient cache
+ private final IntFloatMap mFloatMap = new IntFloatMap(); // efficient cache
+ private final IntIntMap mIntegerMap = new IntIntMap(); // efficient cache
+ private final IntIntMap mColorMap = new IntIntMap(); // efficient cache
private final boolean[] mColorOverride = new boolean[MAX_COLORS];
private int mNextId = START_ID;
- {
- for (int i = 0; i < mFloatMap.length; i++) {
- mFloatMap[i] = Float.NaN;
- }
- }
/**
* Get Object based on id. The system will cache things like bitmaps
@@ -113,7 +111,18 @@
*/
public int cacheFloat(float item) {
int id = nextId();
- mFloatMap[id] = item;
+ mFloatMap.put(id, item);
+ mIntegerMap.put(id, (int) item);
+ return id;
+ }
+
+ /**
+ * Insert an item in the cache
+ */
+ public int cacheInteger(int item) {
+ int id = nextId();
+ mIntegerMap.put(id, item);
+ mFloatMap.put(id, item);
return id;
}
@@ -121,21 +130,43 @@
* Insert an item in the cache
*/
public void cacheFloat(int id, float item) {
- mFloatMap[id] = item;
+ mFloatMap.put(id, item);
}
/**
- * Insert an item in the cache
+ * Insert an float item in the cache
*/
public void updateFloat(int id, float item) {
- mFloatMap[id] = item;
+ mFloatMap.put(id, item);
+ mIntegerMap.put(id, (int) item);
}
/**
- * get float
+ * Insert an integer item in the cache
+ */
+ public void updateInteger(int id, int item) {
+ mFloatMap.put(id, item);
+ mIntegerMap.put(id, item);
+ }
+
+ /**
+ * get a float from the float cache
+ *
+ * @param id of the float value
+ * @return the float value
*/
public float getFloat(int id) {
- return mFloatMap[id];
+ return mFloatMap.get(id);
+ }
+
+ /**
+ * get an integer from the cache
+ *
+ * @param id of the integer value
+ * @return the integer
+ */
+ public int getInteger(int id) {
+ return mIntegerMap.get(id);
}
/**
@@ -145,11 +176,12 @@
* @return
*/
public int getColor(int id) {
- return mColorMap[id];
+ return mColorMap.get(id);
}
/**
* Modify the color at id.
+ *
* @param id
* @param color
*/
@@ -157,7 +189,7 @@
if (mColorOverride[id]) {
return;
}
- mColorMap[id] = color;
+ mColorMap.put(id, color);
}
/**
@@ -169,7 +201,7 @@
*/
public void overrideColor(int id, int color) {
mColorOverride[id] = true;
- mColorMap[id] = color;
+ mColorMap.put(id, color);
}
/**
@@ -205,6 +237,7 @@
/**
* Get the next available id
+ *
* @return
*/
public int nextId() {
@@ -213,6 +246,7 @@
/**
* Set the next id
+ *
* @param id
*/
public void setNextId(int id) {
@@ -234,6 +268,7 @@
/**
* Commands that listen to variables add themselves.
+ *
* @param id
* @param variableSupport
*/
@@ -243,6 +278,7 @@
/**
* List of Commands that need to be updated
+ *
* @param context
* @return
*/
@@ -264,6 +300,7 @@
/**
* Set the width of the overall document on screen.
+ *
* @param width
*/
public void setWindowWidth(float width) {
@@ -272,6 +309,7 @@
/**
* Set the width of the overall document on screen.
+ *
* @param height
*/
public void setWindowHeight(float 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 32027d8..41eeb5b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -39,6 +39,7 @@
public float mWidth = 0f;
public float mHeight = 0f;
+ private float mAnimationTime;
/**
* Load a path under an id.
@@ -65,11 +66,20 @@
public abstract void loadColor(int id, int color);
/**
+ * Set the animation time allowing the creator to control animation rates
+ * @param time
+ */
+ public void setAnimationTime(float time) {
+ mAnimationTime = time;
+ }
+
+ /**
* 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;
+ mAnimationTime = (System.nanoTime() - mStart) * 1E-9f; // Eliminate
+ return mAnimationTime;
}
/**
@@ -213,6 +223,13 @@
public abstract void loadFloat(int id, float value);
/**
+ * Load a float
+ * @param id
+ * @param value
+ */
+ public abstract void loadInteger(int id, int value);
+
+ /**
* Load an animated float associated with an id
* Todo: Remove?
* @param id
@@ -235,6 +252,13 @@
public abstract float getFloat(int id);
/**
+ * Get a float given an id
+ * @param id
+ * @return
+ */
+ public abstract int getInteger(int id);
+
+ /**
* Get the color given and ID
* @param id
* @return
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
index 56b2f1f..5a4a9f3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -63,23 +63,23 @@
@Override
public void updateVariables(RemoteContext context) {
- mV1 = (Float.isNaN(mValue1))
+ mV1 = (Utils.isVariable(mValue1))
? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
- mV2 = (Float.isNaN(mValue2))
+ mV2 = (Utils.isVariable(mValue2))
? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
- mV3 = (Float.isNaN(mValue3))
+ mV3 = (Utils.isVariable(mValue3))
? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
}
@Override
public void registerListening(RemoteContext context) {
- if (Float.isNaN(mValue1)) {
+ if (Utils.isVariable(mValue1)) {
context.listensTo(Utils.idFromNan(mValue1), this);
}
- if (Float.isNaN(mValue2)) {
+ if (Utils.isVariable(mValue2)) {
context.listensTo(Utils.idFromNan(mValue2), this);
}
- if (Float.isNaN(mValue3)) {
+ if (Utils.isVariable(mValue3)) {
context.listensTo(Utils.idFromNan(mValue3), this);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextRun.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextRun.java
deleted file mode 100644
index a099252..0000000
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextRun.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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 DrawTextRun 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 DrawTextRun(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 "";
- }
-
- 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();
- DrawTextRun op = new DrawTextRun(text, start, end, contextStart, contextEnd, x, y, rtl);
-
- operations.add(op);
- }
-
- @Override
- public String name() {
- return "";
- }
-
- @Override
- public int id() {
- return 0;
- }
-
- 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/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
new file mode 100644
index 0000000..d52df5d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
@@ -0,0 +1,177 @@
+/*
+ * 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.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.IntegerExpressionEvaluator;
+
+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 IntegerExpression implements Operation, VariableSupport {
+ public int mId;
+ private int mMask;
+ private int mPreMask;
+ public int[] mSrcValue;
+ public int[] mPreCalcValue;
+ private float mLastChange = Float.NaN;
+ public static final Companion COMPANION = new Companion();
+ public static final int MAX_STRING_SIZE = 4000;
+ IntegerExpressionEvaluator mExp = new IntegerExpressionEvaluator();
+
+ public IntegerExpression(int id, int mask, int[] value) {
+ this.mId = id;
+ this.mMask = mask;
+ this.mSrcValue = value;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) {
+ mPreCalcValue = new int[mSrcValue.length];
+ }
+ mPreMask = mMask;
+ for (int i = 0; i < mSrcValue.length; i++) {
+ if (isId(mMask, i, mSrcValue[i])) {
+ mPreMask &= ~(0x1 << i);
+ mPreCalcValue[i] = context.getInteger(mSrcValue[i]);
+ } else {
+ mPreCalcValue[i] = mSrcValue[i];
+ }
+ }
+ }
+
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ for (int i = 0; i < mSrcValue.length; i++) {
+ if (isId(mMask, i, mSrcValue[i])) {
+ context.listensTo(mSrcValue[i], this);
+ }
+ }
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ updateVariables(context);
+ float t = context.getAnimationTime();
+ if (Float.isNaN(mLastChange)) {
+ mLastChange = t;
+ }
+ int v = mExp.eval(mPreMask, Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
+ context.loadInteger(mId, v);
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mId, mMask, mSrcValue);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ for (int i = 0; i < mPreCalcValue.length; i++) {
+ if (i != 0) {
+ s.append(" ");
+ }
+ if (IntegerExpressionEvaluator.isOperation(mMask, i)) {
+ if (isId(mMask, i, mSrcValue[i])) {
+ s.append("[" + mSrcValue[i] + "]");
+ } else {
+ s.append(IntegerExpressionEvaluator.toMathName(mPreCalcValue[i]));
+ }
+ } else {
+ s.append(mSrcValue[i]);
+ }
+ }
+ return "IntegerExpression[" + mId + "] = (" + s + ")";
+ }
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "FloatExpression";
+ }
+
+ @Override
+ public int id() {
+ return Operations.INTEGER_EXPRESSION;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer
+ * @param id
+ * @param mask
+ * @param value
+ */
+ public void apply(WireBuffer buffer, int id, int mask, int[] value) {
+ buffer.start(Operations.INTEGER_EXPRESSION);
+ buffer.writeInt(id);
+ buffer.writeInt(mask);
+ buffer.writeInt(value.length);
+ for (int i = 0; i < value.length; i++) {
+ buffer.writeInt(value[i]);
+ }
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int mask = buffer.readInt();
+ int len = buffer.readInt();
+
+ int[] values = new int[len];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = buffer.readInt();
+ }
+
+ operations.add(new IntegerExpression(id, mask, values));
+ }
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+
+ /**
+ * given the "i" position in the mask is this an ID
+ * @param mask 32 bit mask used for defining numbers vs other
+ * @param i the bit in question
+ * @param value the value
+ * @return true if this is an ID
+ */
+ public static boolean isId(int mask, int i, int value) {
+ return ((1 << i) & mask) != 0 && value < IntegerExpressionEvaluator.OFFSET;
+ }
+}
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 fcb3bfa..e9b0c3b 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
@@ -40,7 +40,7 @@
* @param n
* @return
*/
- static String trimString(String str, int n) {
+ public static String trimString(String str, int n) {
if (str.length() > n) {
str = str.substring(0, n - 3) + "...";
}
@@ -55,6 +55,9 @@
*/
public static String floatToString(float idvalue, float value) {
if (Float.isNaN(idvalue)) {
+ if (idFromNan(value) == 0) {
+ return "NaN";
+ }
return "[" + idFromNan(idvalue) + "]" + floatToString(value);
}
return floatToString(value);
@@ -67,6 +70,9 @@
*/
public static String floatToString(float value) {
if (Float.isNaN(value)) {
+ if (idFromNan(value) == 0) {
+ return "NaN";
+ }
return "[" + idFromNan(value) + "]";
}
return Float.toString(value);
@@ -107,6 +113,7 @@
public static boolean isVariable(float v) {
if (Float.isNaN(v)) {
int id = idFromNan(v);
+ if (id == 0) return false;
return id > 40 || id < 10;
}
return false;
@@ -222,6 +229,4 @@
}
return 0;
}
-
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
new file mode 100644
index 0000000..ada3757
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
@@ -0,0 +1,260 @@
+/*
+ * 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.paint;
+
+
+/**
+ * Provides a Builder pattern for a PaintBundle
+ */
+class Painter {
+ PaintBundle mPaint;
+
+ /**
+ * Write the paint to the buffer
+ */
+ public PaintBundle commit() {
+ return mPaint;
+ }
+
+ public Painter setAntiAlias(boolean aa) {
+ mPaint.setAntiAlias(aa);
+ return this;
+ }
+
+ public Painter setColor(int color) {
+ mPaint.setColor(color);
+ return this;
+ }
+
+ public Painter setColorId(int colorId) {
+ mPaint.setColorId(colorId);
+ return this;
+ }
+
+ /**
+ * Set the paint's Join.
+ *
+ * @param join set the paint's Join, used whenever the paint's style is
+ * Stroke or StrokeAndFill.
+ */
+ public Painter setStrokeJoin(int join) {
+ mPaint.setStrokeJoin(join);
+ return this;
+ }
+
+ /**
+ * Set the width for stroking. Pass 0 to stroke in hairline mode.
+ * Hairlines always draws a single
+ * pixel independent of the canvas's matrix.
+ *
+ * @param width set the paint's stroke width, used whenever the paint's
+ * style is Stroke or StrokeAndFill.
+ */
+ public Painter setStrokeWidth(float width) {
+ mPaint.setStrokeWidth(width);
+ return this;
+ }
+
+ /**
+ * Set the paint's style, used for controlling how primitives' geometries
+ * are interpreted (except for drawBitmap, which always assumes Fill).
+ *
+ * @param style The new style to set in the paint
+ */
+ public Painter setStyle(int style) {
+ mPaint.setStyle(style);
+ return this;
+ }
+
+ /**
+ * Set the paint's Cap.
+ *
+ * @param cap set the paint's line cap style, used whenever the paint's
+ * style is Stroke or StrokeAndFill.
+ */
+ public Painter setStrokeCap(int cap) {
+ mPaint.setStrokeCap(cap);
+ return this;
+ }
+
+ /**
+ * Set the paint's stroke miter value. This is used to control the behavior
+ * of miter joins when the joins angle is sharp. This value must be >= 0.
+ *
+ * @param miter set the miter limit on the paint, used whenever the paint's
+ * style is Stroke or StrokeAndFill.
+ */
+ public Painter setStrokeMiter(float miter) {
+ mPaint.setStrokeMiter(miter);
+ return this;
+ }
+
+ /**
+ * Helper to setColor(), that only assigns the color's alpha value,
+ * leaving its r,g,b values unchanged. Results are undefined if the alpha
+ * value is outside of the range [0..1.0]
+ *
+ * @param alpha set the alpha component [0..1.0] of the paint's color.
+ */
+ public Painter setAlpha(float alpha) {
+ mPaint.setAlpha((alpha > 2) ? alpha / 255f : alpha);
+ return this;
+ }
+
+ /**
+ * Create a color filter that uses the specified color and Porter-Duff mode.
+ *
+ * @param color The ARGB source color used with the specified Porter-Duff
+ * mode
+ * @param mode The porter-duff mode that is applied
+ */
+ public Painter setPorterDuffColorFilter(int color, int mode) {
+ mPaint.setColorFilter(color, mode);
+ return this;
+ }
+
+ /**
+ * sets a shader that draws a linear gradient along a line.
+ *
+ * @param startX The x-coordinate for the start of the gradient line
+ * @param startY The y-coordinate for the start of the gradient line
+ * @param endX The x-coordinate for the end of the gradient line
+ * @param endY The y-coordinate for the end of the gradient line
+ * @param colors The sRGB colors to be distributed along the gradient
+ * line
+ * @param positions May be null. The relative positions [0..1] of each
+ * corresponding color in the colors array. If this is null,
+ * the colors are distributed evenly along the gradient
+ * line.
+ * @param tileMode The Shader tiling mode
+ */
+ public Painter setLinearGradient(
+ float startX,
+ float startY,
+ float endX,
+ float endY,
+ int[] colors,
+ float[] positions,
+ int tileMode
+ ) {
+ mPaint.setLinearGradient(colors, positions, startX,
+ startY, endX, endY, tileMode);
+ return this;
+ }
+
+ /**
+ * Sets a shader that draws a radial gradient given the center and radius.
+ *
+ * @param centerX The x-coordinate of the center of the radius
+ * @param centerY The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the circle for this
+ * gradient.
+ * @param colors The sRGB colors to be distributed between the center
+ * and edge of the circle
+ * @param positions May be <code>null</code>. Valid values are between
+ * <code>0.0f</code> and
+ * <code>1.0f</code>. The relative position of each
+ * corresponding color in the colors array. If
+ * <code>null</code>, colors are distributed evenly
+ * between the center and edge of the circle.
+ * @param tileMode The Shader tiling mode
+ */
+ public Painter setRadialGradient(
+ float centerX,
+ float centerY,
+ float radius,
+ int[] colors,
+ float[] positions,
+ int tileMode
+ ) {
+ mPaint.setRadialGradient(colors, positions, centerX,
+ centerY, radius, tileMode);
+ return this;
+ }
+
+ /**
+ * Set a shader that draws a sweep gradient around a center point.
+ *
+ * @param centerX The x-coordinate of the center
+ * @param centerY The y-coordinate of the center
+ * @param colors The sRGB colors to be distributed between around the
+ * center. There must be at least 2 colors in the array.
+ * @param positions May be NULL. The relative position of each corresponding
+ * color in the colors array, beginning with 0 and ending
+ * with 1.0. If the values are not monotonic, the drawing
+ * may produce unexpected results. If positions is NULL,
+ * then the colors are automatically spaced evenly.
+ */
+ public Painter setSweepGradient(
+ float centerX,
+ float centerY,
+ int[] colors,
+ float[] positions
+ ) {
+ mPaint.setSweepGradient(colors, positions, centerX, centerY);
+ return this;
+ }
+
+ /**
+ * Set the paint's text size. This value must be > 0
+ *
+ * @param size set the paint's text size in pixel units.
+ */
+ public Painter setTextSize(float size) {
+ mPaint.setTextSize(size);
+ return this;
+ }
+
+ /**
+ * sets a typeface object that best matches the specified existing
+ * typeface and the specified weight and italic style
+ *
+ * <p>Below are numerical values and corresponding common weight names.</p>
+ * <table> <thead>
+ * <tr><th>Value</th><th>Common weight name</th></tr> </thead> <tbody>
+ * <tr><td>100</td><td>Thin</td></tr>
+ * <tr><td>200</td><td>Extra Light</td></tr>
+ * <tr><td>300</td><td>Light</td></tr>
+ * <tr><td>400</td><td>Normal</td></tr>
+ * <tr><td>500</td><td>Medium</td></tr>
+ * <tr><td>600</td><td>Semi Bold</td></tr>
+ * <tr><td>700</td><td>Bold</td></tr>
+ * <tr><td>800</td><td>Extra Bold</td></tr>
+ * <tr><td>900</td><td>Black</td></tr> </tbody> </table>
+ *
+ * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
+ * @param weight The desired weight to be drawn.
+ * @param italic {@code true} if italic style is desired to be drawn.
+ * Otherwise, {@code false}
+ */
+ public Painter setTypeface(int fontType, int weight, boolean italic) {
+ mPaint.setTextStyle(fontType, weight, italic);
+ return this;
+ }
+
+
+ public Painter setFilterBitmap(boolean filter) {
+ mPaint.setFilterBitmap(filter);
+ return this;
+ }
+
+
+ public Painter setShader(int id) {
+ mPaint.setShader(id);
+ return this;
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
new file mode 100644
index 0000000..23c3ec5
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
@@ -0,0 +1,140 @@
+/*
+ * 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 java.util.Arrays;
+
+public class IntFloatMap {
+
+ private static final int DEFAULT_CAPACITY = 16;
+ private static final float LOAD_FACTOR = 0.75f;
+ private static final int NOT_PRESENT = Integer.MIN_VALUE;
+ private int[] mKeys;
+ private float[] mValues;
+ int mSize;
+
+ public IntFloatMap() {
+ mKeys = new int[DEFAULT_CAPACITY];
+ Arrays.fill(mKeys, NOT_PRESENT);
+ mValues = new float[DEFAULT_CAPACITY];
+ }
+
+ /**
+ * clear the map
+ */
+ public void clear() {
+ Arrays.fill(mKeys, NOT_PRESENT);
+ Arrays.fill(mValues, Float.NaN); // not strictly necessary but defensive
+ mSize = 0;
+ }
+
+ /**
+ * is the key contained in map
+ *
+ * @param key the key to check
+ * @return true if the map contains the key
+ */
+ public boolean contains(int key) {
+ return findKey(key) != -1;
+ }
+
+ /**
+ * Put a item in the map
+ *
+ * @param key item'values key
+ * @param value item's value
+ * @return old value if exist
+ */
+ public float put(int key, float value) {
+ if (key == NOT_PRESENT) {
+ throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
+ }
+ if (mSize > mKeys.length * LOAD_FACTOR) {
+ resize();
+ }
+ return insert(key, value);
+ }
+
+ /**
+ * get an element given the key
+ *
+ * @param key the key to fetch
+ * @return the value
+ */
+ public float get(int key) {
+ int index = findKey(key);
+ if (index == -1) {
+ return 0;
+ } else
+ return mValues[index];
+ }
+
+ /**
+ * how many elements in the map
+ *
+ * @return number of elements
+ */
+ public int size() {
+ return mSize;
+ }
+
+ private float insert(int key, float value) {
+ int index = hash(key) % mKeys.length;
+ while (mKeys[index] != NOT_PRESENT && mKeys[index] != key) {
+ index = (index + 1) % mKeys.length;
+ }
+ float oldValue = 0;
+ if (mKeys[index] == NOT_PRESENT) {
+ mSize++;
+ } else {
+ oldValue = mValues[index];
+ }
+ mKeys[index] = key;
+ mValues[index] = value;
+ return oldValue;
+ }
+
+ private int findKey(int key) {
+ int index = hash(key) % mKeys.length;
+ while (mKeys[index] != NOT_PRESENT) {
+ if (mKeys[index] == key) {
+ return index;
+ }
+ index = (index + 1) % mKeys.length;
+ }
+ return -1;
+ }
+
+ private int hash(int key) {
+ return key;
+ }
+
+ private void resize() {
+ int[] oldKeys = mKeys;
+ float[] oldValues = mValues;
+ mKeys = new int[(oldKeys.length * 2)];
+ for (int i = 0; i < mKeys.length; i++) {
+ mKeys[i] = NOT_PRESENT;
+ }
+ mValues = new float[oldKeys.length * 2];
+ mSize = 0;
+ for (int i = 0; i < oldKeys.length; i++) {
+ if (oldKeys[i] != NOT_PRESENT) {
+ put(oldKeys[i], oldValues[i]);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
new file mode 100644
index 0000000..221014c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
@@ -0,0 +1,139 @@
+/*
+ * 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 java.util.Arrays;
+
+public class IntIntMap {
+ private static final int DEFAULT_CAPACITY = 16;
+ private static final float LOAD_FACTOR = 0.75f;
+ private static final int NOT_PRESENT = Integer.MIN_VALUE;
+ private int[] mKeys;
+ private int[] mValues;
+ int mSize;
+
+ public IntIntMap() {
+ mKeys = new int[DEFAULT_CAPACITY];
+ Arrays.fill(mKeys, NOT_PRESENT);
+ mValues = new int[DEFAULT_CAPACITY];
+ }
+
+ /**
+ * clear the map
+ */
+ public void clear() {
+ Arrays.fill(mKeys, NOT_PRESENT);
+ Arrays.fill(mValues, 0);
+ mSize = 0;
+ }
+
+ /**
+ * is the key contained in map
+ *
+ * @param key the key to check
+ * @return true if the map contains the key
+ */
+ public boolean contains(int key) {
+ return findKey(key) != -1;
+ }
+
+ /**
+ * Put a item in the map
+ *
+ * @param key item'values key
+ * @param value item's value
+ * @return old value if exist
+ */
+ public int put(int key, int value) {
+ if (key == NOT_PRESENT) {
+ throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
+ }
+ if (mSize > mKeys.length * LOAD_FACTOR) {
+ resize();
+ }
+ return insert(key, value);
+ }
+
+ /**
+ * get an element given the key
+ *
+ * @param key the key to fetch
+ * @return the value
+ */
+ public int get(int key) {
+ int index = findKey(key);
+ if (index == -1) {
+ return 0;
+ } else
+ return mValues[index];
+ }
+
+ /**
+ * how many elements in the map
+ *
+ * @return number of elements
+ */
+ public int size() {
+ return mSize;
+ }
+
+ private int insert(int key, int value) {
+ int index = hash(key) % mKeys.length;
+ while (mKeys[index] != NOT_PRESENT && mKeys[index] != key) {
+ index = (index + 1) % mKeys.length;
+ }
+ int oldValue = 0;
+ if (mKeys[index] == NOT_PRESENT) {
+ mSize++;
+ } else {
+ oldValue = mValues[index];
+ }
+ mKeys[index] = key;
+ mValues[index] = value;
+ return oldValue;
+ }
+
+ private int findKey(int key) {
+ int index = hash(key) % mKeys.length;
+ while (mKeys[index] != NOT_PRESENT) {
+ if (mKeys[index] == key) {
+ return index;
+ }
+ index = (index + 1) % mKeys.length;
+ }
+ return -1;
+ }
+
+ private int hash(int key) {
+ return key;
+ }
+
+ private void resize() {
+ int[] oldKeys = mKeys;
+ int[] oldValues = mValues;
+ mKeys = new int[(oldKeys.length * 2)];
+ for (int i = 0; i < mKeys.length; i++) {
+ mKeys[i] = NOT_PRESENT;
+ }
+ mValues = new int[oldKeys.length * 2];
+ mSize = 0;
+ for (int i = 0; i < oldKeys.length; i++) {
+ if (oldKeys[i] != NOT_PRESENT) {
+ put(oldKeys[i], oldValues[i]);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
new file mode 100644
index 0000000..4c1389c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
@@ -0,0 +1,429 @@
+/*
+ * 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;
+
+/**
+ * High performance Integer expression evaluator
+ */
+public class IntegerExpressionEvaluator {
+ static IntMap<String> sNames = new IntMap<>();
+ public static final int OFFSET = 0x10000;
+ // add, sub, mul,div,mod,min,max, shl, shr, ushr, OR, AND , XOR, COPY_SIGN
+ public static final int I_ADD = OFFSET + 1;
+ public static final int I_SUB = OFFSET + 2;
+ public static final int I_MUL = OFFSET + 3;
+ public static final int I_DIV = OFFSET + 4;
+ public static final int I_MOD = OFFSET + 5;
+ public static final int I_SHL = OFFSET + 6;
+ public static final int I_SHR = OFFSET + 7;
+ public static final int I_USHR = OFFSET + 8;
+ public static final int I_OR = OFFSET + 9;
+ public static final int I_AND = OFFSET + 10;
+ public static final int I_XOR = OFFSET + 11;
+ public static final int I_COPY_SIGN = OFFSET + 12;
+ public static final int I_MIN = OFFSET + 13;
+ public static final int I_MAX = OFFSET + 14;
+
+ public static final int I_NEG = OFFSET + 15;
+ public static final int I_ABS = OFFSET + 16;
+ public static final int I_INCR = OFFSET + 17;
+ public static final int I_DECR = OFFSET + 18;
+ public static final int I_NOT = OFFSET + 19;
+ public static final int I_SIGN = OFFSET + 20;
+
+ public static final int I_CLAMP = OFFSET + 21;
+ public static final int I_IFELSE = OFFSET + 22;
+ public static final int I_MAD = OFFSET + 23;
+
+ public static final float LAST_OP = 24;
+
+ public static final int I_VAR1 = OFFSET + 24;
+ public static final int I_VAR2 = OFFSET + 24;
+
+
+ int[] mStack;
+ int[] mLocalStack = new int[128];
+ int[] mVar;
+
+
+ interface Op {
+ int eval(int sp);
+ }
+
+ /**
+ * Evaluate a float expression
+ *
+ * @param exp
+ * @param var
+ * @return
+ */
+ public int eval(int mask, int[] exp, int... var) {
+ mStack = exp;
+ mVar = var;
+ int sp = -1;
+ for (int i = 0; i < mStack.length; i++) {
+ int v = mStack[i];
+ if (((1 << i) & mask) != 0) {
+ sp = mOps[v - OFFSET].eval(sp);
+ } else {
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
+
+ /**
+ * Evaluate a int expression
+ *
+ * @param exp
+ * @param len
+ * @param var
+ * @return
+ */
+ public int eval(int mask, int[] exp, int len, int... var) {
+ System.arraycopy(exp, 0, mLocalStack, 0, len);
+ mStack = mLocalStack;
+ mVar = var;
+ int sp = -1;
+ for (int i = 0; i < len; i++) {
+ int v = mStack[i];
+ if (((1 << i) & mask) != 0) {
+ sp = mOps[v - OFFSET].eval(sp);
+ } else {
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
+
+ /**
+ * Evaluate a int expression
+ *
+ * @param exp
+ * @param var
+ * @return
+ */
+ public int evalDB(int mask, int[] exp, int... var) {
+ mStack = exp;
+ mVar = var;
+ int sp = -1;
+ for (int i = 0; i < exp.length; i++) {
+ int v = mStack[i];
+ if (((1 << i) & mask) != 0) {
+ System.out.print(" " + sNames.get((v - OFFSET)));
+ sp = mOps[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) -> { // SHL shift left
+ mStack[sp - 1] = mStack[sp - 1] << mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // SHR shift right
+ mStack[sp - 1] = mStack[sp - 1] >> mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // USHR unsigned shift right
+ mStack[sp - 1] = mStack[sp - 1] >>> mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // OR operator
+ mStack[sp - 1] = mStack[sp - 1] | mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // AND operator
+ mStack[sp - 1] = mStack[sp - 1] & mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // XOR xor operator
+ mStack[sp - 1] = mStack[sp - 1] ^ mStack[sp];
+ return sp - 1;
+ },
+ (sp) -> { // COPY_SIGN copy the sing of (using bit magic)
+ mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31))
+ - (mStack[sp] >> 31);
+ return sp - 1;
+ },
+ (sp) -> { // MIN
+ mStack[sp - 1] = Math.min(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // MAX
+ mStack[sp - 1] = Math.max(mStack[sp - 1], mStack[sp]);
+ return sp - 1;
+ },
+ (sp) -> { // NEG
+ mStack[sp] = -mStack[sp];
+ return sp;
+ },
+ (sp) -> { // ABS
+ mStack[sp] = Math.abs(mStack[sp]);
+ return sp;
+ },
+ (sp) -> { // INCR increment
+ mStack[sp] = mStack[sp] + 1;
+ return sp;
+ },
+ (sp) -> { // DECR decrement
+ mStack[sp] = mStack[sp] - 1;
+ return sp;
+ },
+ (sp) -> { // NOT Bit invert
+ mStack[sp] = ~mStack[sp];
+ return sp;
+ },
+ (sp) -> { // SIGN x<0 = -1,x==0 = 0 , x>0 = 1
+ mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
+ return sp;
+ },
+
+ (sp) -> { // CLAMP(min,max, val)
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
+ mStack[sp - 1]);
+ return sp - 2;
+ },
+ (sp) -> { // Ternary conditional
+ mStack[sp - 2] = (mStack[sp] > 0)
+ ? mStack[sp - 1] : mStack[sp - 2];
+ return sp - 2;
+ },
+ (sp) -> { // MAD
+ mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+ return sp - 2;
+ },
+
+ (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++, "<<");
+ sNames.put(k++, ">>");
+ sNames.put(k++, ">>>");
+ sNames.put(k++, "|");
+ sNames.put(k++, "&");
+ sNames.put(k++, "^");
+ sNames.put(k++, "copySign");
+ sNames.put(k++, "min");
+ sNames.put(k++, "max");
+ sNames.put(k++, "neg");
+ sNames.put(k++, "abs");
+ sNames.put(k++, "incr");
+ sNames.put(k++, "decr");
+ sNames.put(k++, "not");
+ sNames.put(k++, "sign");
+ sNames.put(k++, "clamp");
+ sNames.put(k++, "ifElse");
+ sNames.put(k++, "mad");
+ sNames.put(k++, "ceil");
+ sNames.put(k++, "a[0]");
+ sNames.put(k++, "a[1]");
+ sNames.put(k++, "a[2]");
+ }
+
+ /**
+ * given a int command return its math name (e.g sin, cos etc.)
+ *
+ * @param f
+ * @return
+ */
+ public static String toMathName(int f) {
+ int id = f - OFFSET;
+ return sNames.get(id);
+ }
+
+ /**
+ * Convert an expression encoded as an array of ints int ot a string
+ *
+ * @param exp
+ * @param labels
+ * @return
+ */
+ public static String toString(int mask, int[] exp, String[] labels) {
+ StringBuilder s = new StringBuilder();
+ for (int i = 0; i < exp.length; i++) {
+ int v = exp[i];
+
+ if (((1 << i) & mask) != 0) {
+ if (v < OFFSET) {
+ s.append(toMathName(v));
+ } else {
+ s.append("[");
+ s.append(v);
+ s.append("]");
+ }
+ } else {
+ if (labels[i] != null) {
+ s.append(labels[i]);
+ }
+ s.append(v);
+ }
+ s.append(" ");
+ }
+ return s.toString();
+ }
+
+ /**
+ * Convert an expression encoded as an array of ints int ot a string
+ *
+ * @param mask bit mask of operators vs commands
+ * @param exp
+ * @return
+ */
+ public static String toString(int mask, int[] exp) {
+ StringBuilder s = new StringBuilder();
+ s.append(Integer.toBinaryString(mask));
+ s.append(" : ");
+ for (int i = 0; i < exp.length; i++) {
+ int v = exp[i];
+
+ if (((1 << i) & mask) != 0) {
+ if (v > OFFSET) {
+ s.append(" ");
+ s.append(toMathName(v));
+ s.append(" ");
+
+ } else {
+ s.append("[");
+ s.append(v);
+ s.append("]");
+ }
+ }
+ s.append(" " + v);
+ }
+ return s.toString();
+ }
+
+ /**
+ * This creates an infix string expression
+ * @param mask The bits that are operators
+ * @param exp the array of expressions
+ * @return infix string
+ */
+ public static String toStringInfix(int mask, int[] exp) {
+ return toString(mask, exp, exp.length - 1);
+ }
+
+ static String toString(int mask, int[] exp, int sp) {
+ String[] str = new String[exp.length];
+ if (((1 << sp) & mask) != 0) {
+ int id = exp[sp] - OFFSET;
+ switch (NO_OF_OPS[id]) {
+ case -1:
+ return "nop";
+ case 1:
+ return sNames.get(id) + "(" + toString(mask, exp, sp - 1) + ") ";
+ case 2:
+ if (infix(id)) {
+ return "(" + toString(mask, exp, sp - 2)
+ + " " + sNames.get(id) + " "
+ + toString(mask, exp, sp - 1) + ") ";
+ } else {
+ return sNames.get(id) + "("
+ + toString(mask, exp, sp - 2) + ", "
+ + toString(mask, exp, sp - 1) + ")";
+ }
+ case 3:
+ if (infix(id)) {
+ return "((" + toString(mask, exp, sp + 3) + ") ? "
+ + toString(mask, exp, sp - 2) + ":"
+ + toString(mask, exp, sp - 1) + ")";
+ } else {
+ return sNames.get(id)
+ + "(" + toString(mask, exp, sp - 3)
+ + ", " + toString(mask, exp, sp - 2)
+ + ", " + toString(mask, exp, sp - 1) + ")";
+ }
+ }
+ }
+ return Integer.toString(exp[sp]);
+ }
+
+ static final int[] NO_OF_OPS = {
+ -1, // no op
+ 2, 2, 2, 2, 2, // + - * / %
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, //<<, >> , >>> , | , &, ^, min max
+ 1, 1, 1, 1, 1, 1, // neg, abs, ++, -- , not , sign
+
+ 3, 3, 3, // clamp, ifElse, mad,
+ 0, 0, 0 // mad, ?:,
+ // a[0],a[1],a[2]
+ };
+
+ /**
+ * to be used by parser to determine if command is infix
+ *
+ * @param n the operator (minus the offset)
+ * @return true if the operator is infix
+ */
+ static boolean infix(int n) {
+ return ((n < 12));
+ }
+
+ /**
+ * is it an id or operation
+ * @param mask the bits that mark elements as an operation
+ * @param i the bit to check
+ * @return true if the bit is 1
+ */
+ public static boolean isOperation(int mask, int i) {
+ return ((1 << i) & mask) != 0;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
new file mode 100644
index 0000000..1051192
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.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.types;
+
+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;
+
+/**
+ * Used to represent a boolean
+ */
+public class BooleanConstant implements Operation {
+ boolean mValue = false;
+ private int mId;
+
+ public BooleanConstant(int id, boolean value) {
+ mId = id;
+ mValue = value;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mId, mValue);
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return toString();
+ }
+
+ @Override
+ public String toString() {
+ return "BooleanConstant[" + mId + "] = " + mValue + "";
+ }
+
+ public static final Companion COMPANION = new Companion();
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "OrigamiBoolean";
+ }
+
+ @Override
+ public int id() {
+ return Operations.DATA_BOOLEAN;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer
+ * @param id
+ * @param value
+ */
+ public void apply(WireBuffer buffer, int id, boolean value) {
+ buffer.start(Operations.DATA_BOOLEAN);
+ buffer.writeInt(id);
+ buffer.writeBoolean(value);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+
+ boolean value = buffer.readBoolean();
+ operations.add(new BooleanConstant(id, value));
+ }
+ }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
new file mode 100644
index 0000000..ceb3236
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.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.types;
+
+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;
+
+/**
+ * Represents a single integer typically used for states
+ * or named for input into the system
+ */
+public class IntegerConstant implements Operation {
+ private int mValue = 0;
+ private int mId;
+
+ IntegerConstant(int id, int value) {
+ mId = id;
+ mValue = value;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ COMPANION.apply(buffer, mId, mValue);
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadInteger(mId, mValue);
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return toString();
+ }
+
+ @Override
+ public String toString() {
+ return "IntegerConstant[" + mId + "] = " + mValue + "";
+ }
+
+ public static final Companion COMPANION = new Companion();
+
+ public static class Companion implements CompanionOperation {
+ private Companion() {
+ }
+
+ @Override
+ public String name() {
+ return "IntegerConstant";
+ }
+
+ @Override
+ public int id() {
+ return Operations.DATA_INT;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer
+ * @param textId
+ * @param value
+ */
+ public void apply(WireBuffer buffer, int textId, int value) {
+ buffer.start(Operations.DATA_INT);
+ buffer.writeInt(textId);
+ buffer.writeInt(value);
+ }
+
+ @Override
+ public void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+
+ int value = buffer.readInt();
+ operations.add(new IntegerConstant(id, value));
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 73e94fa..b2406bf 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -410,4 +410,3 @@
}
}
}
-
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 ecb68bb..39a770a 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
@@ -39,12 +39,16 @@
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* An implementation of PaintContext for the Android Canvas.
* This is used to play the RemoteCompose operations on Android.
*/
public class AndroidPaintContext extends PaintContext {
Paint mPaint = new Paint();
+ List<Paint> mPaintList = new ArrayList<>();
Canvas mCanvas;
Rect mTmpRect = new Rect(); // use in calculation of bounds
@@ -162,6 +166,16 @@
}
@Override
+ public void savePaint() {
+ mPaintList.add(new Paint(mPaint));
+ }
+
+ @Override
+ public void restorePaint() {
+ mPaint = mPaintList.remove(mPaintList.size() - 1);
+ }
+
+ @Override
public void drawRoundRect(float left,
float top,
float right,
@@ -335,6 +349,11 @@
return null;
}
+ /**
+ * This applies paint changes to the current paint
+ *
+ * @param mPaintData the list change to the paint
+ */
@Override
public void applyPaint(PaintBundle mPaintData) {
mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
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 dd43bd5..5a87c70 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
@@ -120,6 +120,11 @@
mRemoteComposeState.updateFloat(id, value);
}
+ @Override
+ public void loadInteger(int id, int value) {
+ mRemoteComposeState.updateInteger(id, value);
+ }
+
@Override
public void loadColor(int id, int color) {
@@ -142,6 +147,11 @@
}
@Override
+ public int getInteger(int id) {
+ return mRemoteComposeState.getInteger(id);
+ }
+
+ @Override
public int getColor(int id) {
return mRemoteComposeState.getColor(id);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
index 2d766f8..7a85427 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
@@ -58,7 +58,7 @@
break;
case PathData.CONIC: {
i += 3;
- if (Build.VERSION.SDK_INT >= 34) {
+ if (Build.VERSION.SDK_INT >= 34) { // REMOVE IN PLATFORM
path.conicTo(
floatPath[i + 0], floatPath[i + 1],
floatPath[i + 2], floatPath[i + 3],