• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.internal.widget.remotecompose.core.operations;
17 
18 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
19 import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
20 
21 import android.annotation.NonNull;
22 
23 import com.android.internal.widget.remotecompose.core.Operation;
24 import com.android.internal.widget.remotecompose.core.Operations;
25 import com.android.internal.widget.remotecompose.core.RemoteContext;
26 import com.android.internal.widget.remotecompose.core.VariableSupport;
27 import com.android.internal.widget.remotecompose.core.WireBuffer;
28 import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
29 import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
30 import com.android.internal.widget.remotecompose.core.serialize.MapSerializer;
31 import com.android.internal.widget.remotecompose.core.serialize.Serializable;
32 
33 import java.util.List;
34 
35 /**
36  * Operation to Colors Color modes mMode = 0 two colors and a tween mMode = 1 color1 is a colorID.
37  * mMode = 2 color2 is a colorID. mMode = 3 color1 & color2 are ids mMode = 4 H S V mode
38  */
39 public class ColorExpression extends Operation implements VariableSupport, Serializable {
40     private static final int OP_CODE = Operations.COLOR_EXPRESSIONS;
41     private static final String CLASS_NAME = "ColorExpression";
42     public int mId;
43 
44     /**
45      * Mode of the color expression 0 = two colors and a tween 1 = color1 is a colorID. 2 color2 is
46      * a colorID. 3 = color1 & color2 are ids 4 = H S V mode 5 = RGB mode 6 = ARGB mode
47      */
48     int mMode;
49 
50     public int mColor1;
51     public int mColor2;
52     public float mTween = 0.0f;
53 
54     public float mHue = 0; // only in Mode 4
55     public float mSat = 0;
56     public float mValue = 0;
57     public float mOutHue = 0; // only in Mode 4
58     public float mOutSat = 0;
59     public float mOutValue = 0;
60     public int mAlpha = 0xFF; // only used in hsv mode
61 
62     private float mArgbAlpha = 0.0f;
63     private float mArgbRed = 0.0f;
64     private float mArgbGreen = 0.0f;
65     private float mArgbBlue = 0.0f;
66 
67     private float mOutArgbAlpha = 0.0f;
68     private float mOutArgbRed = 0.0f;
69     private float mOutArgbGreen = 0.0f;
70     private float mOutArgbBlue = 0.0f;
71 
72     public float mOutTween = 0.0f;
73     public int mOutColor1;
74     public int mOutColor2;
75 
76     /** COLOR_COLOR_INTERPOLATE */
77     public static final byte COLOR_COLOR_INTERPOLATE = 0;
78 
79     /** COLOR_ID_INTERPOLATE */
80     public static final byte ID_COLOR_INTERPOLATE = 1;
81 
82     /** ID_COLOR_INTERPOLATE */
83     public static final byte COLOR_ID_INTERPOLATE = 2;
84 
85     /** ID_ID_INTERPOLATE */
86     public static final byte ID_ID_INTERPOLATE = 3;
87 
88     /** H S V mode */
89     public static final byte HSV_MODE = 4;
90 
91     /** ARGB mode */
92     public static final byte ARGB_MODE = 5;
93 
94     /** ARGB mode with a being an id */
95     public static final byte IDARGB_MODE = 6;
96 
97     /**
98      * Create a new ColorExpression object
99      *
100      * @param id the id of the color
101      * @param hue the hue of the color
102      * @param sat the saturation of the color
103      * @param value the value of the color
104      */
ColorExpression(int id, float hue, float sat, float value)105     public ColorExpression(int id, float hue, float sat, float value) {
106         mMode = HSV_MODE;
107         mAlpha = 0xFF;
108         mOutHue = mHue = hue;
109         mOutSat = mSat = sat;
110         mOutValue = mValue = value;
111         mColor1 = Float.floatToRawIntBits(hue);
112         mColor2 = Float.floatToRawIntBits(sat);
113         mTween = value;
114     }
115 
116     /**
117      * Create a new ColorExpression object based on HSV
118      *
119      * @param id id of the color
120      * @param mode the mode of the color
121      * @param alpha the alpha of the color
122      * @param hue the hue of the color
123      * @param sat the saturation of the color
124      * @param value the value (brightness) of the color
125      */
ColorExpression(int id, byte mode, int alpha, float hue, float sat, float value)126     public ColorExpression(int id, byte mode, int alpha, float hue, float sat, float value) {
127         if (mode != HSV_MODE) {
128             throw new RuntimeException("Invalid mode " + mode);
129         }
130         mId = id;
131         mMode = HSV_MODE;
132         mAlpha = alpha;
133         mOutHue = mHue = hue;
134         mOutSat = mSat = sat;
135         mOutValue = mValue = value;
136         mColor1 = Float.floatToRawIntBits(hue);
137         mColor2 = Float.floatToRawIntBits(sat);
138         mTween = value;
139     }
140 
141     /**
142      * Create a new ColorExpression object based interpolationg two colors
143      *
144      * @param id the id of the color
145      * @param mode the type of mode (are colors ids or actual values)
146      * @param color1 the first color to use
147      * @param color2 the second color to use
148      * @param tween the value to use to interpolate between the two colors
149      */
ColorExpression(int id, int mode, int color1, int color2, float tween)150     public ColorExpression(int id, int mode, int color1, int color2, float tween) {
151         this.mId = id;
152         this.mMode = mode & 0xFF;
153         this.mAlpha = (mode >> 16) & 0xFF;
154         if (mMode == HSV_MODE) {
155             mOutHue = mHue = Float.intBitsToFloat(color1);
156             mOutSat = mSat = Float.intBitsToFloat(color2);
157             mOutValue = mValue = tween;
158         }
159         this.mColor1 = color1;
160         this.mColor2 = color2;
161         this.mTween = tween;
162         this.mOutTween = tween;
163         this.mOutColor1 = color1;
164         this.mOutColor2 = color2;
165     }
166 
167     /**
168      * Create a new ColorExpression object based on ARGB
169      *
170      * @param id the id of the color
171      * @param mode the mode must be ARGB_MODE
172      * @param alpha the alpha value of the color
173      * @param red the red of component the color
174      * @param green the greej component of the color
175      * @param blue the blue of component the color
176      */
ColorExpression(int id, byte mode, float alpha, float red, float green, float blue)177     public ColorExpression(int id, byte mode, float alpha, float red, float green, float blue) {
178         if (mode != ARGB_MODE) {
179             throw new RuntimeException("Invalid mode " + mode);
180         }
181         mMode = ARGB_MODE;
182         mId = id;
183         mOutArgbAlpha = mArgbAlpha = alpha;
184         mOutArgbRed = mArgbRed = red;
185         mOutArgbGreen = mArgbGreen = green;
186         mOutArgbBlue = mArgbBlue = blue;
187     }
188 
189     @Override
updateVariables(@onNull RemoteContext context)190     public void updateVariables(@NonNull RemoteContext context) {
191         if (mMode == 4) {
192             if (Float.isNaN(mHue)) {
193                 mOutHue = context.getFloat(Utils.idFromNan(mHue));
194             }
195             if (Float.isNaN(mSat)) {
196                 mOutSat = context.getFloat(Utils.idFromNan(mSat));
197             }
198             if (Float.isNaN(mValue)) {
199                 mOutValue = context.getFloat(Utils.idFromNan(mValue));
200             }
201         }
202         if (mMode == ARGB_MODE) {
203             if (Float.isNaN(mArgbAlpha)) {
204                 mOutArgbAlpha = context.getFloat(Utils.idFromNan(mArgbAlpha));
205             }
206             if (Float.isNaN(mArgbRed)) {
207                 mOutArgbRed = context.getFloat(Utils.idFromNan(mArgbRed));
208             }
209             if (Float.isNaN(mArgbGreen)) {
210                 mOutArgbGreen = context.getFloat(Utils.idFromNan(mArgbGreen));
211             }
212             if (Float.isNaN(mArgbBlue)) {
213                 mOutArgbBlue = context.getFloat(Utils.idFromNan(mArgbBlue));
214             }
215         }
216         if (Float.isNaN(mTween)) {
217             mOutTween = context.getFloat(Utils.idFromNan(mTween));
218         }
219         if ((mMode & 1) == 1) {
220             mOutColor1 = context.getColor(mColor1);
221         }
222         if ((mMode & 2) == 2) {
223             mOutColor2 = context.getColor(mColor2);
224         }
225     }
226 
227     @Override
registerListening(@onNull RemoteContext context)228     public void registerListening(@NonNull RemoteContext context) {
229         if (mMode == HSV_MODE) {
230             if (Float.isNaN(mHue)) {
231                 context.listensTo(Utils.idFromNan(mHue), this);
232             }
233             if (Float.isNaN(mSat)) {
234                 context.listensTo(Utils.idFromNan(mSat), this);
235             }
236             if (Float.isNaN(mValue)) {
237                 context.listensTo(Utils.idFromNan(mValue), this);
238             }
239             return;
240         }
241         if (mMode == ARGB_MODE) {
242             if (Float.isNaN(mArgbAlpha)) {
243                 context.listensTo(Utils.idFromNan(mArgbAlpha), this);
244             }
245             if (Float.isNaN(mArgbRed)) {
246                 context.listensTo(Utils.idFromNan(mArgbRed), this);
247             }
248             if (Float.isNaN(mArgbGreen)) {
249                 context.listensTo(Utils.idFromNan(mArgbGreen), this);
250             }
251             if (Float.isNaN(mArgbBlue)) {
252                 context.listensTo(Utils.idFromNan(mArgbBlue), this);
253             }
254             return;
255         }
256         if (Float.isNaN(mTween)) {
257             context.listensTo(Utils.idFromNan(mTween), this);
258         }
259         if ((mMode & 1) == 1) {
260             context.listensTo(mColor1, this);
261         }
262         if ((mMode & 2) == 2) {
263             context.listensTo(mColor2, this);
264         }
265     }
266 
267     @Override
apply(@onNull RemoteContext context)268     public void apply(@NonNull RemoteContext context) {
269         if (mMode == HSV_MODE) {
270             context.loadColor(
271                     mId, (mAlpha << 24) | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
272             return;
273         }
274         if (mMode == ARGB_MODE) {
275             context.loadColor(
276                     mId, Utils.toARGB(mOutArgbAlpha, mOutArgbRed, mOutArgbGreen, mOutArgbBlue));
277             return;
278         }
279         if (mOutTween == 0.0) {
280             if ((mMode & 1) == 1) {
281                 mOutColor1 = context.getColor(mColor1);
282             }
283             context.loadColor(mId, mOutColor1);
284         } else {
285             if ((mMode & 1) == 1) {
286                 mOutColor1 = context.getColor(mColor1);
287             }
288             if ((mMode & 2) == 2) {
289                 mOutColor2 = context.getColor(mColor2);
290             }
291 
292             context.loadColor(mId, Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
293         }
294     }
295 
296     @Override
write(@onNull WireBuffer buffer)297     public void write(@NonNull WireBuffer buffer) {
298         int mode;
299         switch (mMode) {
300             case ARGB_MODE:
301                 apply(buffer, mId, mArgbAlpha, mArgbRed, mArgbGreen, mArgbBlue);
302                 break;
303 
304             case HSV_MODE:
305                 mOutValue = mValue;
306                 mColor1 = Float.floatToRawIntBits(mHue);
307                 mColor2 = Float.floatToRawIntBits(mSat);
308                 mode = mMode | (mAlpha << 16);
309                 apply(buffer, mId, mode, mColor1, mColor2, mTween);
310 
311                 break;
312             case COLOR_ID_INTERPOLATE:
313             case ID_COLOR_INTERPOLATE:
314             case ID_ID_INTERPOLATE:
315             case COLOR_COLOR_INTERPOLATE:
316                 apply(buffer, mId, mMode, mColor1, mColor2, mTween);
317 
318                 break;
319             default:
320                 throw new RuntimeException("Invalid mode ");
321         }
322     }
323 
324     @NonNull
325     @Override
toString()326     public String toString() {
327         if (mMode == HSV_MODE) {
328             return "ColorExpression["
329                     + mId
330                     + "] = hsv ("
331                     + Utils.floatToString(mHue)
332                     + ", "
333                     + Utils.floatToString(mSat)
334                     + ", "
335                     + Utils.floatToString(mValue)
336                     + ")";
337         }
338         Utils.log(" ColorExpression toString" + mId + " " + mMode);
339         if (mMode == ARGB_MODE) {
340             return "ColorExpression["
341                     + mId
342                     + "] = rgb ("
343                     + Utils.floatToString(mArgbAlpha)
344                     + ", "
345                     + Utils.floatToString(mArgbRed)
346                     + ", "
347                     + Utils.floatToString(mArgbGreen)
348                     + ", "
349                     + Utils.floatToString(mArgbRed)
350                     + ")";
351         }
352         String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
353         String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
354         return "ColorExpression["
355                 + mId
356                 + "] = tween("
357                 + c1
358                 + ", "
359                 + c2
360                 + ", "
361                 + Utils.floatToString(mTween)
362                 + ")";
363     }
364 
365     /**
366      * The name of the class
367      *
368      * @return the name
369      */
370     @NonNull
name()371     public static String name() {
372         return CLASS_NAME;
373     }
374 
375     /**
376      * The OP_CODE for this command
377      *
378      * @return the opcode
379      */
id()380     public static int id() {
381         return OP_CODE;
382     }
383 
384     /**
385      * Call to write a ColorExpression object on the buffer
386      *
387      * @param buffer
388      * @param id of the ColorExpression object
389      * @param mode if colors are id or actual values
390      * @param color1
391      * @param color2
392      * @param tween
393      */
apply( @onNull WireBuffer buffer, int id, int mode, int color1, int color2, float tween)394     public static void apply(
395             @NonNull WireBuffer buffer, int id, int mode, int color1, int color2, float tween) {
396         apply(buffer, id, mode, color1, color2, Float.floatToRawIntBits(tween));
397     }
398 
399     /**
400      * Call to write a ColorExpression object on the buffer
401      *
402      * @param buffer
403      * @param id of the ColorExpression object
404      * @param alpha
405      * @param red
406      * @param green
407      * @param blue
408      */
apply( @onNull WireBuffer buffer, int id, float alpha, float red, float green, float blue)409     public static void apply(
410             @NonNull WireBuffer buffer, int id, float alpha, float red, float green, float blue) {
411         int param1 = (Float.isNaN(alpha)) ? IDARGB_MODE : ARGB_MODE;
412         param1 |=
413                 (Float.isNaN(alpha)) ? Utils.idFromNan(alpha) << 16 : ((int) (alpha * 1024)) << 16;
414         int param2 = Float.floatToRawIntBits(red);
415         int param3 = Float.floatToRawIntBits(green);
416         int param4 = Float.floatToRawIntBits(blue);
417         apply(buffer, id, param1, param2, param3, param4);
418     }
419 
apply( @onNull WireBuffer buffer, int id, int param1, int param2, int param3, int param4)420     private static void apply(
421             @NonNull WireBuffer buffer, int id, int param1, int param2, int param3, int param4) {
422         buffer.start(OP_CODE);
423         buffer.writeInt(id);
424         buffer.writeInt(param1);
425         buffer.writeInt(param2);
426         buffer.writeInt(param3);
427         buffer.writeInt(param4);
428     }
429 
430     /**
431      * Read this operation and add it to the list of operations
432      *
433      * @param buffer the buffer to read
434      * @param operations the list of operations that will be added to
435      */
read(@onNull WireBuffer buffer, @NonNull List<Operation> operations)436     public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
437         int id = buffer.readInt();
438         int param1 = buffer.readInt();
439         int param2 = buffer.readInt();
440         int param3 = buffer.readInt();
441         int param4 = buffer.readInt();
442         int mode = param1 & 0xFF;
443         float alpha;
444         float red;
445         float green;
446         float blue;
447         switch (mode) {
448             case IDARGB_MODE:
449                 alpha = Utils.asNan(param1 >> 16);
450                 red = Float.intBitsToFloat(param2);
451                 green = Float.intBitsToFloat(param3);
452                 blue = Float.intBitsToFloat(param4);
453                 operations.add(new ColorExpression(id, (byte) ARGB_MODE, alpha, red, green, blue));
454                 break;
455             case ARGB_MODE:
456                 alpha = (param1 >> 16) / 1024.0f;
457                 red = Float.intBitsToFloat(param2);
458                 green = Float.intBitsToFloat(param3);
459                 blue = Float.intBitsToFloat(param4);
460                 operations.add(new ColorExpression(id, (byte) ARGB_MODE, alpha, red, green, blue));
461                 break;
462             case HSV_MODE:
463                 alpha = (param1 >> 16) / 1024.0f;
464                 float hue = Float.intBitsToFloat(param2);
465                 float sat = Float.intBitsToFloat(param3);
466                 float value = Float.intBitsToFloat(param4);
467                 operations.add(new ColorExpression(id, HSV_MODE, (param1 >> 16), hue, sat, value));
468                 break;
469             case COLOR_ID_INTERPOLATE:
470             case ID_COLOR_INTERPOLATE:
471             case ID_ID_INTERPOLATE:
472             case COLOR_COLOR_INTERPOLATE:
473                 operations.add(
474                         new ColorExpression(
475                                 id, mode, param2, param3, Float.intBitsToFloat(param4)));
476                 break;
477             default:
478                 throw new RuntimeException("Invalid mode " + mode);
479         }
480     }
481 
482     /**
483      * Populate the documentation with a description of this operation
484      *
485      * @param doc to append the description to.
486      */
documentation(@onNull DocumentationBuilder doc)487     public static void documentation(@NonNull DocumentationBuilder doc) {
488         doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
489                 .description("A Color defined by an expression")
490                 .field(DocumentedOperation.INT, "id", "Id of the color")
491                 .field(INT, "mode", "The use of the next 3 fields")
492                 .possibleValues("COLOR_COLOR_INTERPOLATE", 0)
493                 .possibleValues("COLOR_ID_INTERPOLATE", 1)
494                 .possibleValues("ID_COLOR_INTERPOLATE", 2)
495                 .possibleValues("ID_ID_INTERPOLATE", 3)
496                 .possibleValues("HSV", 4)
497                 .field(INT, "color1", "32 bit ARGB color")
498                 .field(INT, "color2", "32 bit ARGB color")
499                 .field(FLOAT, "tween", "32 bit ARGB color");
500     }
501 
502     @NonNull
503     @Override
deepToString(@onNull String indent)504     public String deepToString(@NonNull String indent) {
505         return indent + toString();
506     }
507 
508     @Override
serialize(MapSerializer serializer)509     public void serialize(MapSerializer serializer) {
510         serializer.addType(CLASS_NAME).add("id", mId);
511         switch (mMode) {
512             case COLOR_COLOR_INTERPOLATE:
513             case ID_COLOR_INTERPOLATE:
514             case COLOR_ID_INTERPOLATE:
515             case ID_ID_INTERPOLATE:
516                 serializer.add("mode", "TWEEN");
517                 serializer.add("startColor", mColor1, mOutColor1);
518                 serializer.add("endColor", mColor2, mOutColor2);
519                 serializer.add("startColor", mTween, mOutTween);
520                 break;
521             case HSV_MODE:
522                 serializer.add("mode", "HSV");
523                 serializer.add("hue", mHue, mOutHue);
524                 serializer.add("sat", mSat, mOutSat);
525                 serializer.add("val", mValue, mOutValue);
526                 break;
527             case ARGB_MODE:
528             case IDARGB_MODE:
529                 serializer.add("mode", "ARGB");
530                 serializer.add("a", mArgbAlpha, mOutArgbAlpha);
531                 serializer.add("r", mArgbRed, mOutArgbRed);
532                 serializer.add("g", mArgbGreen, mOutArgbGreen);
533                 serializer.add("b", mArgbBlue, mOutArgbBlue);
534                 break;
535             default:
536                 serializer.add("mode", "NONE");
537         }
538     }
539 }
540