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; 17 18 import static com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression.MUL; 19 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 23 import com.android.internal.widget.remotecompose.core.operations.BitmapData; 24 import com.android.internal.widget.remotecompose.core.operations.BitmapFontData; 25 import com.android.internal.widget.remotecompose.core.operations.ClickArea; 26 import com.android.internal.widget.remotecompose.core.operations.ClipPath; 27 import com.android.internal.widget.remotecompose.core.operations.ClipRect; 28 import com.android.internal.widget.remotecompose.core.operations.ColorAttribute; 29 import com.android.internal.widget.remotecompose.core.operations.ColorConstant; 30 import com.android.internal.widget.remotecompose.core.operations.ColorExpression; 31 import com.android.internal.widget.remotecompose.core.operations.ComponentValue; 32 import com.android.internal.widget.remotecompose.core.operations.ConditionalOperations; 33 import com.android.internal.widget.remotecompose.core.operations.DataListFloat; 34 import com.android.internal.widget.remotecompose.core.operations.DataListIds; 35 import com.android.internal.widget.remotecompose.core.operations.DataMapIds; 36 import com.android.internal.widget.remotecompose.core.operations.DataMapLookup; 37 import com.android.internal.widget.remotecompose.core.operations.DebugMessage; 38 import com.android.internal.widget.remotecompose.core.operations.DrawArc; 39 import com.android.internal.widget.remotecompose.core.operations.DrawBitmap; 40 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapFontText; 41 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt; 42 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled; 43 import com.android.internal.widget.remotecompose.core.operations.DrawCircle; 44 import com.android.internal.widget.remotecompose.core.operations.DrawContent; 45 import com.android.internal.widget.remotecompose.core.operations.DrawLine; 46 import com.android.internal.widget.remotecompose.core.operations.DrawOval; 47 import com.android.internal.widget.remotecompose.core.operations.DrawPath; 48 import com.android.internal.widget.remotecompose.core.operations.DrawRect; 49 import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect; 50 import com.android.internal.widget.remotecompose.core.operations.DrawSector; 51 import com.android.internal.widget.remotecompose.core.operations.DrawText; 52 import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored; 53 import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath; 54 import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath; 55 import com.android.internal.widget.remotecompose.core.operations.FloatConstant; 56 import com.android.internal.widget.remotecompose.core.operations.FloatExpression; 57 import com.android.internal.widget.remotecompose.core.operations.FloatFunctionCall; 58 import com.android.internal.widget.remotecompose.core.operations.FloatFunctionDefine; 59 import com.android.internal.widget.remotecompose.core.operations.HapticFeedback; 60 import com.android.internal.widget.remotecompose.core.operations.Header; 61 import com.android.internal.widget.remotecompose.core.operations.ImageAttribute; 62 import com.android.internal.widget.remotecompose.core.operations.IntegerExpression; 63 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; 64 import com.android.internal.widget.remotecompose.core.operations.MatrixRotate; 65 import com.android.internal.widget.remotecompose.core.operations.MatrixSave; 66 import com.android.internal.widget.remotecompose.core.operations.MatrixScale; 67 import com.android.internal.widget.remotecompose.core.operations.MatrixSkew; 68 import com.android.internal.widget.remotecompose.core.operations.MatrixTranslate; 69 import com.android.internal.widget.remotecompose.core.operations.NamedVariable; 70 import com.android.internal.widget.remotecompose.core.operations.PaintData; 71 import com.android.internal.widget.remotecompose.core.operations.ParticlesCreate; 72 import com.android.internal.widget.remotecompose.core.operations.ParticlesLoop; 73 import com.android.internal.widget.remotecompose.core.operations.PathAppend; 74 import com.android.internal.widget.remotecompose.core.operations.PathCombine; 75 import com.android.internal.widget.remotecompose.core.operations.PathCreate; 76 import com.android.internal.widget.remotecompose.core.operations.PathData; 77 import com.android.internal.widget.remotecompose.core.operations.PathTween; 78 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; 79 import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; 80 import com.android.internal.widget.remotecompose.core.operations.TextAttribute; 81 import com.android.internal.widget.remotecompose.core.operations.TextData; 82 import com.android.internal.widget.remotecompose.core.operations.TextFromFloat; 83 import com.android.internal.widget.remotecompose.core.operations.TextLength; 84 import com.android.internal.widget.remotecompose.core.operations.TextLookup; 85 import com.android.internal.widget.remotecompose.core.operations.TextLookupInt; 86 import com.android.internal.widget.remotecompose.core.operations.TextMeasure; 87 import com.android.internal.widget.remotecompose.core.operations.TextMerge; 88 import com.android.internal.widget.remotecompose.core.operations.Theme; 89 import com.android.internal.widget.remotecompose.core.operations.TimeAttribute; 90 import com.android.internal.widget.remotecompose.core.operations.TouchExpression; 91 import com.android.internal.widget.remotecompose.core.operations.Utils; 92 import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent; 93 import com.android.internal.widget.remotecompose.core.operations.layout.CanvasOperations; 94 import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart; 95 import com.android.internal.widget.remotecompose.core.operations.layout.ContainerEnd; 96 import com.android.internal.widget.remotecompose.core.operations.layout.ImpulseOperation; 97 import com.android.internal.widget.remotecompose.core.operations.layout.ImpulseProcess; 98 import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent; 99 import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation; 100 import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent; 101 import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout; 102 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout; 103 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleColumnLayout; 104 import com.android.internal.widget.remotecompose.core.operations.layout.managers.CollapsibleRowLayout; 105 import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout; 106 import com.android.internal.widget.remotecompose.core.operations.layout.managers.FitBoxLayout; 107 import com.android.internal.widget.remotecompose.core.operations.layout.managers.ImageLayout; 108 import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout; 109 import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout; 110 import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout; 111 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation; 112 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation; 113 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ClipRectModifierOperation; 114 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.GraphicsLayerModifierOperation; 115 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.MarqueeModifierOperation; 116 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.OffsetModifierOperation; 117 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation; 118 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RippleModifierOperation; 119 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation; 120 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RunActionOperation; 121 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ScrollModifierOperation; 122 import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ZIndexModifierOperation; 123 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; 124 import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap; 125 import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; 126 import com.android.internal.widget.remotecompose.core.types.BooleanConstant; 127 import com.android.internal.widget.remotecompose.core.types.IntegerConstant; 128 import com.android.internal.widget.remotecompose.core.types.LongConstant; 129 130 import java.io.File; 131 import java.io.FileInputStream; 132 import java.io.FileOutputStream; 133 import java.io.IOException; 134 import java.io.InputStream; 135 import java.util.ArrayList; 136 import java.util.Arrays; 137 import java.util.HashMap; 138 139 /** Provides an abstract buffer to encode/decode RemoteCompose operations */ 140 public class RemoteComposeBuffer { 141 public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD; 142 public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE; 143 public static final int EASING_CUBIC_DECELERATE = FloatAnimation.CUBIC_DECELERATE; 144 public static final int EASING_CUBIC_LINEAR = FloatAnimation.CUBIC_LINEAR; 145 public static final int EASING_CUBIC_ANTICIPATE = FloatAnimation.CUBIC_ANTICIPATE; 146 public static final int EASING_CUBIC_OVERSHOOT = FloatAnimation.CUBIC_OVERSHOOT; 147 public static final int EASING_CUBIC_CUSTOM = FloatAnimation.CUBIC_CUSTOM; 148 public static final int EASING_SPLINE_CUSTOM = FloatAnimation.SPLINE_CUSTOM; 149 public static final int EASING_EASE_OUT_BOUNCE = FloatAnimation.EASE_OUT_BOUNCE; 150 public static final int EASING_EASE_OUT_ELASTIC = FloatAnimation.EASE_OUT_ELASTIC; 151 private @NonNull WireBuffer mBuffer = new WireBuffer(); 152 @Nullable private Platform mPlatform = null; 153 @NonNull private final RemoteComposeState mRemoteComposeState; 154 private static final boolean DEBUG = false; 155 156 private int mLastComponentId = 0; 157 private int mGeneratedComponentId = -1; 158 159 /** 160 * Provides an abstract buffer to encode/decode RemoteCompose operations 161 * 162 * @param remoteComposeState the state used while encoding on the buffer 163 */ RemoteComposeBuffer(@onNull RemoteComposeState remoteComposeState)164 public RemoteComposeBuffer(@NonNull RemoteComposeState remoteComposeState) { 165 this.mRemoteComposeState = remoteComposeState; 166 } 167 168 /** 169 * Reset the internal buffers 170 * 171 * @param expectedSize provided hint for the main buffer size 172 */ reset(int expectedSize)173 public void reset(int expectedSize) { 174 mBuffer.reset(expectedSize); 175 mRemoteComposeState.reset(); 176 mLastComponentId = 0; 177 mGeneratedComponentId = -1; 178 } 179 getLastComponentId()180 public int getLastComponentId() { 181 return mLastComponentId; 182 } 183 184 @Nullable getPlatform()185 public Platform getPlatform() { 186 return mPlatform; 187 } 188 setPlatform(@onNull Platform platform)189 public void setPlatform(@NonNull Platform platform) { 190 this.mPlatform = platform; 191 } 192 getBuffer()193 public @NonNull WireBuffer getBuffer() { 194 return mBuffer; 195 } 196 setBuffer(@onNull WireBuffer buffer)197 public void setBuffer(@NonNull WireBuffer buffer) { 198 this.mBuffer = buffer; 199 } 200 201 /////////////////////////////////////////////////////////////////////////////////////////////// 202 // Supported operations on the buffer 203 /////////////////////////////////////////////////////////////////////////////////////////////// 204 205 /** Insert a header */ addHeader(short[] tags, Object[] values)206 public void addHeader(short[] tags, Object[] values) { 207 Header.apply(mBuffer, tags, values); 208 } 209 210 /** 211 * Insert a header 212 * 213 * @param width the width of the document in pixels 214 * @param height the height of the document in pixels 215 * @param contentDescription content description of the document 216 * @param capabilities bitmask indicating needed capabilities (unused for now) 217 */ header( int width, int height, @Nullable String contentDescription, float density, long capabilities)218 public void header( 219 int width, 220 int height, 221 @Nullable String contentDescription, 222 float density, 223 long capabilities) { 224 Header.apply(mBuffer, width, height, density, capabilities); 225 int contentDescriptionId = 0; 226 if (contentDescription != null) { 227 contentDescriptionId = addText(contentDescription); 228 RootContentDescription.apply(mBuffer, contentDescriptionId); 229 } 230 } 231 232 /** 233 * Insert a header 234 * 235 * @param width the width of the document in pixels 236 * @param height the height of the document in pixels 237 * @param contentDescription content description of the document 238 * @param capabilities bitmask indicating needed capabilities (unused for now) 239 */ addHeader( int width, int height, @Nullable String contentDescription, float density, long capabilities)240 public void addHeader( 241 int width, 242 int height, 243 @Nullable String contentDescription, 244 float density, 245 long capabilities) { 246 Header.apply(mBuffer, width, height, density, capabilities); 247 int contentDescriptionId = 0; 248 if (contentDescription != null) { 249 contentDescriptionId = addText(contentDescription); 250 RootContentDescription.apply(mBuffer, contentDescriptionId); 251 } 252 } 253 254 /** 255 * Insert a header 256 * 257 * @param width the width of the document in pixels 258 * @param height the height of the document in pixels 259 * @param contentDescription content description of the document 260 */ header(int width, int height, @Nullable String contentDescription)261 public void header(int width, int height, @Nullable String contentDescription) { 262 header(width, height, contentDescription, 1f, 0); 263 } 264 265 /** 266 * Insert a bitmap 267 * 268 * @param image an opaque image that we'll add to the buffer 269 * @param imageWidth the width of the image 270 * @param imageHeight the height of the image 271 * @param srcLeft left coordinate of the source area 272 * @param srcTop top coordinate of the source area 273 * @param srcRight right coordinate of the source area 274 * @param srcBottom bottom coordinate of the source area 275 * @param dstLeft left coordinate of the destination area 276 * @param dstTop top coordinate of the destination area 277 * @param dstRight right coordinate of the destination area 278 * @param dstBottom bottom coordinate of the destination area 279 */ drawBitmap( @onNull Object image, int imageWidth, int imageHeight, int srcLeft, int srcTop, int srcRight, int srcBottom, int dstLeft, int dstTop, int dstRight, int dstBottom, @Nullable String contentDescription)280 public void drawBitmap( 281 @NonNull Object image, 282 int imageWidth, 283 int imageHeight, 284 int srcLeft, 285 int srcTop, 286 int srcRight, 287 int srcBottom, 288 int dstLeft, 289 int dstTop, 290 int dstRight, 291 int dstBottom, 292 @Nullable String contentDescription) { 293 int imageId = storeBitmap(image); 294 int contentDescriptionId = 0; 295 if (contentDescription != null) { 296 contentDescriptionId = addText(contentDescription); 297 } 298 DrawBitmapInt.apply( 299 mBuffer, 300 imageId, 301 srcLeft, 302 srcTop, 303 srcRight, 304 srcBottom, 305 dstLeft, 306 dstTop, 307 dstRight, 308 dstBottom, 309 contentDescriptionId); 310 } 311 312 /** 313 * look up map and return the id of the object looked up 314 * 315 * @param mapId the map to access 316 * @param strId the string to lookup 317 * @return id containing the result of the lookup 318 */ mapLookup(int mapId, int strId)319 public int mapLookup(int mapId, int strId) { 320 int hash = mapId + strId * 33; 321 int id = mRemoteComposeState.dataGetId(hash); 322 if (id == -1) { 323 id = mRemoteComposeState.cacheData(hash); 324 DataMapLookup.apply(mBuffer, id, mapId, strId); 325 } 326 return id; 327 } 328 329 /** 330 * Adds a text string data to the stream and returns its id Will be used to insert string with 331 * bitmaps etc. 332 * 333 * @param text the string to inject in the buffer 334 */ addText(@onNull String text)335 public int addText(@NonNull String text) { 336 int id = mRemoteComposeState.dataGetId(text); 337 if (id == -1) { 338 id = mRemoteComposeState.cacheData(text); 339 TextData.apply(mBuffer, id, text); 340 } 341 return id; 342 } 343 344 /** 345 * Add a click area to the document 346 * 347 * @param id the id of the click area, reported in the click listener callback 348 * @param contentDescription the content description of that click area (accessibility) 349 * @param left left coordinate of the area bounds 350 * @param top top coordinate of the area bounds 351 * @param right right coordinate of the area bounds 352 * @param bottom bottom coordinate of the area bounds 353 * @param metadata associated metadata, user-provided 354 */ addClickArea( int id, @Nullable String contentDescription, float left, float top, float right, float bottom, @Nullable String metadata)355 public void addClickArea( 356 int id, 357 @Nullable String contentDescription, 358 float left, 359 float top, 360 float right, 361 float bottom, 362 @Nullable String metadata) { 363 int contentDescriptionId = 0; 364 if (contentDescription != null) { 365 contentDescriptionId = addText(contentDescription); 366 } 367 int metadataId = 0; 368 if (metadata != null) { 369 metadataId = addText(metadata); 370 } 371 ClickArea.apply(mBuffer, id, contentDescriptionId, left, top, right, bottom, metadataId); 372 } 373 374 /** 375 * Sets the way the player handles the content 376 * 377 * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) 378 * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END) 379 * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE) 380 * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are: 381 * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical 382 * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT - 383 * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT - 384 * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size 385 */ setRootContentBehavior(int scroll, int alignment, int sizing, int mode)386 public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) { 387 RootContentBehavior.apply(mBuffer, scroll, alignment, sizing, mode); 388 } 389 390 /** 391 * add Drawing the specified arc, which will be scaled to fit inside the specified oval. <br> 392 * If the start angle is negative or >= 360, the start angle is treated as start angle modulo 393 * 360. <br> 394 * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs 395 * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is 396 * negative, the sweep angle is treated as sweep angle modulo 360 <br> 397 * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0 398 * degrees (3 o'clock on a watch.) <br> 399 * 400 * @param left left coordinate of oval used to define the shape and size of the arc 401 * @param top top coordinate of oval used to define the shape and size of the arc 402 * @param right right coordinate of oval used to define the shape and size of the arc 403 * @param bottom bottom coordinate of oval used to define the shape and size of the arc 404 * @param startAngle Starting angle (in degrees) where the arc begins 405 * @param sweepAngle Sweep angle (in degrees) measured clockwise 406 */ addDrawArc( float left, float top, float right, float bottom, float startAngle, float sweepAngle)407 public void addDrawArc( 408 float left, float top, float right, float bottom, float startAngle, float sweepAngle) { 409 DrawArc.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle); 410 } 411 412 /** 413 * add Drawing the specified sector, which will be scaled to fit inside the specified oval. <br> 414 * If the start angle is negative or >= 360, the start angle is treated as start angle modulo 415 * 360. <br> 416 * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs 417 * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is 418 * negative, the sweep angle is treated as sweep angle modulo 360 <br> 419 * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0 420 * degrees (3 o'clock on a watch.) <br> 421 * 422 * @param left left coordinate of oval used to define the shape and size of the arc 423 * @param top top coordinate of oval used to define the shape and size of the arc 424 * @param right right coordinate of oval used to define the shape and size of the arc 425 * @param bottom bottom coordinate of oval used to define the shape and size of the arc 426 * @param startAngle Starting angle (in degrees) where the arc begins 427 * @param sweepAngle Sweep angle (in degrees) measured clockwise 428 */ addDrawSector( float left, float top, float right, float bottom, float startAngle, float sweepAngle)429 public void addDrawSector( 430 float left, float top, float right, float bottom, float startAngle, float sweepAngle) { 431 DrawSector.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle); 432 } 433 434 /** 435 * @param image The bitmap to be drawn 436 * @param left left coordinate of rectangle that the bitmap will be to fit into 437 * @param top top coordinate of rectangle that the bitmap will be to fit into 438 * @param right right coordinate of rectangle that the bitmap will be to fit into 439 * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into 440 * @param contentDescription content description of the image 441 */ addDrawBitmap( @onNull Object image, float left, float top, float right, float bottom, @Nullable String contentDescription)442 public void addDrawBitmap( 443 @NonNull Object image, 444 float left, 445 float top, 446 float right, 447 float bottom, 448 @Nullable String contentDescription) { 449 int imageId = storeBitmap(image); 450 addDrawBitmap(imageId, left, top, right, bottom, contentDescription); 451 } 452 453 /** 454 * @param imageId The Id bitmap to be drawn 455 * @param left left coordinate of rectangle that the bitmap will be to fit into 456 * @param top top coordinate of rectangle that the bitmap will be to fit into 457 * @param right right coordinate of rectangle that the bitmap will be to fit into 458 * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into 459 * @param contentDescription content description of the image 460 */ addDrawBitmap( int imageId, float left, float top, float right, float bottom, @Nullable String contentDescription)461 public void addDrawBitmap( 462 int imageId, 463 float left, 464 float top, 465 float right, 466 float bottom, 467 @Nullable String contentDescription) { 468 int contentDescriptionId = 0; 469 if (contentDescription != null) { 470 contentDescriptionId = addText(contentDescription); 471 } 472 DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId); 473 } 474 475 /** 476 * @param imageId The Id bitmap to be drawn 477 * @param left left coordinate of rectangle that the bitmap will be to fit into 478 * @param top top coordinate of rectangle that the bitmap will be to fit into 479 * @param contentDescription content description of the image 480 */ addDrawBitmap( int imageId, float left, float top, @Nullable String contentDescription)481 public void addDrawBitmap( 482 int imageId, float left, float top, @Nullable String contentDescription) { 483 int imageWidth = mPlatform.getImageWidth(imageId); 484 int imageHeight = mPlatform.getImageHeight(imageId); 485 int contentDescriptionId = 0; 486 if (contentDescription != null) { 487 contentDescriptionId = addText(contentDescription); 488 } 489 DrawBitmap.apply( 490 mBuffer, imageId, left, top, imageWidth, imageHeight, contentDescriptionId); 491 } 492 493 /** 494 * @param image The bitmap to be drawn 495 * @param srcLeft left coordinate in the source bitmap will be to extracted 496 * @param srcTop top coordinate in the source bitmap will be to extracted 497 * @param srcRight right coordinate in the source bitmap will be to extracted 498 * @param srcBottom bottom coordinate in the source bitmap will be to extracted 499 * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into 500 * @param dstTop top coordinate of rectangle that the bitmap will be to fit into 501 * @param dstRight right coordinate of rectangle that the bitmap will be to fit into 502 * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into 503 * @param scaleType The type of scaling to allow the image to fit. 504 * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7) 505 * @param contentDescription associate a string with image for accessibility 506 */ drawScaledBitmap( @onNull Object image, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, int scaleType, float scaleFactor, @Nullable String contentDescription)507 public void drawScaledBitmap( 508 @NonNull Object image, 509 float srcLeft, 510 float srcTop, 511 float srcRight, 512 float srcBottom, 513 float dstLeft, 514 float dstTop, 515 float dstRight, 516 float dstBottom, 517 int scaleType, 518 float scaleFactor, 519 @Nullable String contentDescription) { 520 int imageId = storeBitmap(image); 521 int contentDescriptionId = 0; 522 if (contentDescription != null) { 523 contentDescriptionId = addText(contentDescription); 524 } 525 DrawBitmapScaled.apply( 526 mBuffer, 527 imageId, 528 srcLeft, 529 srcTop, 530 srcRight, 531 srcBottom, 532 dstLeft, 533 dstTop, 534 dstRight, 535 dstBottom, 536 scaleType, 537 scaleFactor, 538 contentDescriptionId); 539 } 540 541 /** 542 * Transmit bitmap so the you can use the id form. This is useful if 543 * 544 * @param image drawScaledBitmap 545 * @return id of the image useful with 546 */ addBitmap(@onNull Object image)547 public int addBitmap(@NonNull Object image) { 548 return storeBitmap(image); 549 } 550 551 /** 552 * Transmit bitmap so the you can use the id form. This is useful if 553 * 554 * @param image drawScaledBitmap 555 * @return id of the image useful with 556 */ addBitmap(@onNull Object image, @NonNull String name)557 public int addBitmap(@NonNull Object image, @NonNull String name) { 558 return storeBitmap(image); 559 } 560 561 /** 562 * Records a bitmap font and returns an ID. 563 * 564 * @param glyphs The glyphs that define the bitmap font 565 * @return id of the BitmapFont 566 */ addBitmapFont(BitmapFontData.Glyph[] glyphs)567 public int addBitmapFont(BitmapFontData.Glyph[] glyphs) { 568 int id = mRemoteComposeState.nextId(); 569 BitmapFontData.apply(mBuffer, id, glyphs); 570 return id; 571 } 572 573 /** 574 * This defines the name of the bitmap given the id. 575 * 576 * @param id of the Bitmap 577 * @param name Name of the color 578 */ setBitmapName(int id, @NonNull String name)579 public void setBitmapName(int id, @NonNull String name) { 580 NamedVariable.apply(mBuffer, id, NamedVariable.IMAGE_TYPE, name); 581 } 582 583 /** 584 * @param imageId The id of the bitmap to be drawn 585 * @param srcLeft left coordinate in the source bitmap will be to extracted 586 * @param srcTop top coordinate in the source bitmap will be to extracted 587 * @param srcRight right coordinate in the source bitmap will be to extracted 588 * @param srcBottom bottom coordinate in the source bitmap will be to extracted 589 * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into 590 * @param dstTop top coordinate of rectangle that the bitmap will be to fit into 591 * @param dstRight right coordinate of rectangle that the bitmap will be to fit into 592 * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into 593 * @param scaleType The type of scaling to allow the image to fit. 594 * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7) 595 * @param contentDescription associate a string with image for accessibility 596 */ drawScaledBitmap( int imageId, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, int scaleType, float scaleFactor, @Nullable String contentDescription)597 public void drawScaledBitmap( 598 int imageId, 599 float srcLeft, 600 float srcTop, 601 float srcRight, 602 float srcBottom, 603 float dstLeft, 604 float dstTop, 605 float dstRight, 606 float dstBottom, 607 int scaleType, 608 float scaleFactor, 609 @Nullable String contentDescription) { 610 int contentDescriptionId = 0; 611 if (contentDescription != null) { 612 contentDescriptionId = addText(contentDescription); 613 } 614 DrawBitmapScaled.apply( 615 mBuffer, 616 imageId, 617 srcLeft, 618 srcTop, 619 srcRight, 620 srcBottom, 621 dstLeft, 622 dstTop, 623 dstRight, 624 dstBottom, 625 scaleType, 626 scaleFactor, 627 contentDescriptionId); 628 } 629 630 /** 631 * Draw the specified circle using the specified paint. If radius is <= 0, then nothing will be 632 * drawn. 633 * 634 * @param centerX The x-coordinate of the center of the circle to be drawn 635 * @param centerY The y-coordinate of the center of the circle to be drawn 636 * @param radius The radius of the circle to be drawn 637 */ addDrawCircle(float centerX, float centerY, float radius)638 public void addDrawCircle(float centerX, float centerY, float radius) { 639 DrawCircle.apply(mBuffer, centerX, centerY, radius); 640 } 641 642 /** 643 * Draw a line segment with the specified start and stop x,y coordinates, using the specified 644 * paint. 645 * 646 * @param x1 The x-coordinate of the start point of the line 647 * @param y1 The y-coordinate of the start point of the line 648 * @param x2 The x-coordinate of the end point of the line 649 * @param y2 The y-coordinate of the end point of the line 650 */ addDrawLine(float x1, float y1, float x2, float y2)651 public void addDrawLine(float x1, float y1, float x2, float y2) { 652 DrawLine.apply(mBuffer, x1, y1, x2, y2); 653 } 654 655 /** 656 * Draw the specified oval using the specified paint. 657 * 658 * @param left left coordinate of oval 659 * @param top top coordinate of oval 660 * @param right right coordinate of oval 661 * @param bottom bottom coordinate of oval 662 */ addDrawOval(float left, float top, float right, float bottom)663 public void addDrawOval(float left, float top, float right, float bottom) { 664 DrawOval.apply(mBuffer, left, top, right, bottom); 665 } 666 667 /** 668 * Draw the specified path 669 * 670 * <p>Note: path objects are not immutable modifying them and calling this will not change the 671 * drawing 672 * 673 * @param path The path to be drawn 674 */ addDrawPath(@onNull Object path)675 public void addDrawPath(@NonNull Object path) { 676 int id = mRemoteComposeState.dataGetId(path); 677 if (id == -1) { // never been seen before 678 id = addPathData(path); 679 } 680 addDrawPath(id); 681 } 682 683 /** 684 * interpolate the two paths to produce a 3rd 685 * 686 * @param pid1 the first path 687 * @param pid2 the second path 688 * @param tween path is the path1+(pat2-path1)*tween 689 * @return id of the tweened path 690 */ pathTween(int pid1, int pid2, float tween)691 public int pathTween(int pid1, int pid2, float tween) { 692 int out = mRemoteComposeState.nextId(); 693 PathTween.apply(mBuffer, out, pid1, pid2, tween); 694 return out; 695 } 696 697 /** 698 * Create a path with an initial moveTo 699 * 700 * @param x x coordinate of the moveto 701 * @param y y coordinate of the moveto 702 * @return id of the created path 703 */ pathCreate(float x, float y)704 public int pathCreate(float x, float y) { 705 int out = mRemoteComposeState.nextId(); 706 PathCreate.apply(mBuffer, out, x, y); 707 return out; 708 } 709 710 /** 711 * Append a path to an existing path 712 * 713 * @param id id of the path to append to 714 * @param path the path to append 715 */ pathAppend(int id, float... path)716 public void pathAppend(int id, float... path) { 717 PathAppend.apply(mBuffer, id, path); 718 } 719 720 /** 721 * Draw the specified path 722 * 723 * @param pathId 724 */ addDrawPath(int pathId)725 public void addDrawPath(int pathId) { 726 DrawPath.apply(mBuffer, pathId); 727 } 728 729 /** 730 * Draw the specified Rect 731 * 732 * @param left left coordinate of rectangle to be drawn 733 * @param top top coordinate of rectangle to be drawn 734 * @param right right coordinate of rectangle to be drawn 735 * @param bottom bottom coordinate of rectangle to be drawn 736 */ addDrawRect(float left, float top, float right, float bottom)737 public void addDrawRect(float left, float top, float right, float bottom) { 738 DrawRect.apply(mBuffer, left, top, right, bottom); 739 } 740 741 /** 742 * Draw the specified round-rect 743 * 744 * @param left left coordinate of rectangle to be drawn 745 * @param top left coordinate of rectangle to be drawn 746 * @param right left coordinate of rectangle to be drawn 747 * @param bottom left coordinate of rectangle to be drawn 748 * @param radiusX The x-radius of the oval used to round the corners 749 * @param radiusY The y-radius of the oval used to round the corners 750 */ addDrawRoundRect( float left, float top, float right, float bottom, float radiusX, float radiusY)751 public void addDrawRoundRect( 752 float left, float top, float right, float bottom, float radiusX, float radiusY) { 753 DrawRoundRect.apply(mBuffer, left, top, right, bottom, radiusX, radiusY); 754 } 755 756 /** 757 * Draw the text, with origin at (x,y) along the specified path. 758 * 759 * @param text The text to be drawn 760 * @param path The path the text should follow for its baseline 761 * @param hOffset The distance along the path to add to the text's starting position 762 * @param vOffset The distance above(-) or below(+) the path to position the text 763 */ addDrawTextOnPath( @onNull String text, @NonNull Object path, float hOffset, float vOffset)764 public void addDrawTextOnPath( 765 @NonNull String text, @NonNull Object path, float hOffset, float vOffset) { 766 int pathId = mRemoteComposeState.dataGetId(path); 767 if (pathId == -1) { // never been seen before 768 pathId = addPathData(path); 769 } 770 int textId = addText(text); 771 DrawTextOnPath.apply(mBuffer, textId, pathId, hOffset, vOffset); 772 } 773 774 /** 775 * Draw the text, with origin at (x,y) along the specified path. 776 * 777 * @param textId The text to be drawn 778 * @param path The path the text should follow for its baseline 779 * @param hOffset The distance along the path to add to the text's starting position 780 * @param vOffset The distance above(-) or below(+) the path to position the text 781 */ addDrawTextOnPath(int textId, Object path, float hOffset, float vOffset)782 public void addDrawTextOnPath(int textId, Object path, float hOffset, float vOffset) { 783 int pathId = mRemoteComposeState.dataGetId(path); 784 if (pathId == -1) { // never been seen before 785 pathId = addPathData(path); 786 } 787 DrawTextOnPath.apply(mBuffer, textId, pathId, hOffset, vOffset); 788 } 789 790 /** 791 * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in 792 * the paint. 793 * 794 * @param text The text to be drawn 795 * @param start The index of the first character in text to draw 796 * @param end (end - 1) is the index of the last character in text to draw 797 * @param contextStart the context start 798 * @param contextEnd the context end 799 * @param x The x-coordinate of the origin of the text being drawn 800 * @param y The y-coordinate of the baseline of the text being drawn 801 * @param rtl Draw RTTL 802 */ addDrawTextRun( @onNull String text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean rtl)803 public void addDrawTextRun( 804 @NonNull String text, 805 int start, 806 int end, 807 int contextStart, 808 int contextEnd, 809 float x, 810 float y, 811 boolean rtl) { 812 int textId = addText(text); 813 DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl); 814 } 815 816 /** 817 * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in 818 * the paint. 819 * 820 * @param textId The text to be drawn 821 * @param start The index of the first character in text to draw 822 * @param end (end - 1) is the index of the last character in text to draw 823 * @param contextStart the context start 824 * @param contextEnd the context end 825 * @param x The x-coordinate of the origin of the text being drawn 826 * @param y The y-coordinate of the baseline of the text being drawn 827 * @param rtl Draw RTTL 828 */ addDrawTextRun( int textId, int start, int end, int contextStart, int contextEnd, float x, float y, boolean rtl)829 public void addDrawTextRun( 830 int textId, 831 int start, 832 int end, 833 int contextStart, 834 int contextEnd, 835 float x, 836 float y, 837 boolean rtl) { 838 DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl); 839 } 840 841 /** 842 * Draw the text with a bitmap font, with origin at (x,y). The origin is interpreted based on 843 * the Align setting in the paint. 844 * 845 * @param textId The text to be drawn 846 * @param bitmapFontId The id of the bitmap font to draw with 847 * @param start The index of the first character in text to draw 848 * @param end (end - 1) is the index of the last character in text to draw 849 * @param x The x-coordinate of the origin of the text being drawn 850 * @param y The y-coordinate of the baseline of the text being drawn 851 */ addDrawBitmapFontTextRun( int textId, int bitmapFontId, int start, int end, float x, float y)852 public void addDrawBitmapFontTextRun( 853 int textId, int bitmapFontId, int start, int end, float x, float y) { 854 DrawBitmapFontText.apply(mBuffer, textId, bitmapFontId, start, end, x, y); 855 } 856 857 /** 858 * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br> 859 * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a 860 * way that a panning factor of (0.0, 0.0) would center the text at (x, y) 861 * 862 * <ul> 863 * <li>Panning of -1.0, -1.0 - the text above & right of x,y. 864 * <li>Panning of 1.0, 1.0 - the text is below and to the left 865 * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y 866 * </ul> 867 * 868 * <p>Setting panY to NaN results in y being the baseline of the text. 869 * 870 * @param text text to draw 871 * @param x Coordinate of the Anchor 872 * @param y Coordinate of the Anchor 873 * @param panX justifies text -1.0=right, 0.0=center, 1.0=left 874 * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline 875 * @param flags 1 = RTL 876 */ drawTextAnchored( @onNull String text, float x, float y, float panX, float panY, int flags)877 public void drawTextAnchored( 878 @NonNull String text, float x, float y, float panX, float panY, int flags) { 879 int textId = addText(text); 880 DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags); 881 } 882 883 /** 884 * Add a text and id so that it can be used 885 * 886 * @param text 887 * @return 888 */ createTextId(@onNull String text)889 public int createTextId(@NonNull String text) { 890 return addText(text); 891 } 892 893 /** 894 * Merge two text (from id's) output one id 895 * 896 * @param id1 left id 897 * @param id2 right id 898 * @return new id that merges the two text 899 */ textMerge(int id1, int id2)900 public int textMerge(int id1, int id2) { 901 int textId = nextId(); 902 TextMerge.apply(mBuffer, textId, id1, id2); 903 return textId; 904 } 905 906 public static final int PAD_AFTER_SPACE = TextFromFloat.PAD_AFTER_SPACE; 907 public static final int PAD_AFTER_NONE = TextFromFloat.PAD_AFTER_NONE; 908 public static final int PAD_AFTER_ZERO = TextFromFloat.PAD_AFTER_ZERO; 909 public static final int PAD_PRE_SPACE = TextFromFloat.PAD_PRE_SPACE; 910 public static final int PAD_PRE_NONE = TextFromFloat.PAD_PRE_NONE; 911 public static final int PAD_PRE_ZERO = TextFromFloat.PAD_PRE_ZERO; 912 913 /** 914 * Create a TextFromFloat command which creates text from a Float. 915 * 916 * @param value The value to convert 917 * @param digitsBefore the digits before the decimal point 918 * @param digitsAfter the digits after the decimal point 919 * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags 920 * @return id of the string that can be passed to drawTextAnchored 921 */ createTextFromFloat(float value, short digitsBefore, short digitsAfter, int flags)922 public int createTextFromFloat(float value, short digitsBefore, short digitsAfter, int flags) { 923 String placeHolder = 924 Utils.floatToString(value) 925 + "(" 926 + digitsBefore 927 + "," 928 + digitsAfter 929 + "," 930 + flags 931 + ")"; 932 int id = mRemoteComposeState.dataGetId(placeHolder); 933 if (id == -1) { 934 id = mRemoteComposeState.cacheData(placeHolder); 935 // TextData.apply(mBuffer, id, text); 936 } 937 TextFromFloat.apply(mBuffer, id, value, digitsBefore, digitsAfter, flags); 938 return id; 939 } 940 941 /** 942 * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br> 943 * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a 944 * way that a panning factor of (0.0, 0.0) would center the text at (x, y) 945 * 946 * <ul> 947 * <li>Panning of -1.0, -1.0 - the text above & right of x,y. 948 * <li>Panning of 1.0, 1.0 - the text is below and to the left 949 * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y 950 * </ul> 951 * 952 * <p>Setting panY to NaN results in y being the baseline of the text. 953 * 954 * @param textId text to draw 955 * @param x Coordinate of the Anchor 956 * @param y Coordinate of the Anchor 957 * @param panX justifies text -1.0=right, 0.0=center, 1.0=left 958 * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline 959 * @param flags 1 = RTL 960 */ drawTextAnchored(int textId, float x, float y, float panX, float panY, int flags)961 public void drawTextAnchored(int textId, float x, float y, float panX, float panY, int flags) { 962 963 DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags); 964 } 965 966 /** 967 * draw an interpolation between two paths that have the same pattern 968 * 969 * <p>Warning paths objects are not immutable and this is not taken into consideration 970 * 971 * @param path1 The path1 to be drawn between 972 * @param path2 The path2 to be drawn between 973 * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2 974 * @param start The start of the subrange of paths to draw 0 = start form start 0.5 is half way 975 * @param stop The end of the subrange of paths to draw 1 = end at the end 0.5 is end half way 976 */ addDrawTweenPath( @onNull Object path1, @NonNull Object path2, float tween, float start, float stop)977 public void addDrawTweenPath( 978 @NonNull Object path1, @NonNull Object path2, float tween, float start, float stop) { 979 int path1Id = mRemoteComposeState.dataGetId(path1); 980 if (path1Id == -1) { // never been seen before 981 path1Id = addPathData(path1); 982 } 983 int path2Id = mRemoteComposeState.dataGetId(path2); 984 if (path2Id == -1) { // never been seen before 985 path2Id = addPathData(path2); 986 } 987 addDrawTweenPath(path1Id, path2Id, tween, start, stop); 988 } 989 990 /** 991 * draw an interpolation between two paths that have the same pattern 992 * 993 * @param path1Id The path1 to be drawn between 994 * @param path2Id The path2 to be drawn between 995 * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2 996 * @param start The start of the subrange of paths to draw 0 = start form start .5 is 1/2 way 997 * @param stop The end of the subrange of paths to draw 1 = end at the end .5 is end 1/2 way 998 */ addDrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop)999 public void addDrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop) { 1000 DrawTweenPath.apply(mBuffer, path1Id, path2Id, tween, start, stop); 1001 } 1002 1003 /** 1004 * Add a path object 1005 * 1006 * @param path 1007 * @return the id of the path on the wire 1008 */ addPathData(@onNull Object path)1009 public int addPathData(@NonNull Object path) { 1010 float[] pathData = mPlatform.pathToFloatArray(path); 1011 int id = mRemoteComposeState.cacheData(path); 1012 PathData.apply(mBuffer, id, pathData); 1013 return id; 1014 } 1015 1016 /** 1017 * Adds a paint Bundle to the doc 1018 * 1019 * @param paint 1020 */ addPaint(@onNull PaintBundle paint)1021 public void addPaint(@NonNull PaintBundle paint) { 1022 PaintData.apply(mBuffer, paint); 1023 } 1024 1025 /////////////////////////////////////////////////////////////////////////////////////////////// 1026 1027 /** 1028 * inflate the buffer into a list of operations 1029 * 1030 * @param operations the operations list to add to 1031 */ inflateFromBuffer(@onNull ArrayList<Operation> operations)1032 public void inflateFromBuffer(@NonNull ArrayList<Operation> operations) { 1033 mBuffer.setIndex(0); 1034 while (mBuffer.available()) { 1035 int opId = mBuffer.readByte(); 1036 if (DEBUG) { 1037 Utils.log(">> " + opId); 1038 } 1039 CompanionOperation operation = Operations.map.get(opId); 1040 if (operation == null) { 1041 throw new RuntimeException("Unknown operation encountered " + opId); 1042 } 1043 operation.read(mBuffer, operations); 1044 } 1045 } 1046 1047 /** 1048 * Read the next operation from the buffer 1049 * 1050 * @param buffer The buff to read 1051 * @param operations the operations list to add to 1052 */ readNextOperation( @onNull WireBuffer buffer, @NonNull ArrayList<Operation> operations)1053 public static void readNextOperation( 1054 @NonNull WireBuffer buffer, @NonNull ArrayList<Operation> operations) { 1055 int opId = buffer.readByte(); 1056 if (DEBUG) { 1057 Utils.log(">> " + opId); 1058 } 1059 CompanionOperation operation = Operations.map.get(opId); 1060 if (operation == null) { 1061 throw new RuntimeException("Unknown operation encountered " + opId); 1062 } 1063 operation.read(buffer, operations); 1064 } 1065 1066 /** 1067 * copy the current buffer to a new one 1068 * 1069 * @return A new RemoteComposeBuffer 1070 */ 1071 @NonNull copy()1072 RemoteComposeBuffer copy() { 1073 ArrayList<Operation> operations = new ArrayList<>(); 1074 inflateFromBuffer(operations); 1075 RemoteComposeBuffer buffer = new RemoteComposeBuffer(mRemoteComposeState); 1076 return copyFromOperations(operations, buffer); 1077 } 1078 1079 /** 1080 * add a set theme 1081 * 1082 * @param theme The theme to set 1083 */ setTheme(int theme)1084 public void setTheme(int theme) { 1085 Theme.apply(mBuffer, theme); 1086 } 1087 1088 @NonNull version()1089 static String version() { 1090 return "v1.0"; 1091 } 1092 1093 /** 1094 * Initialize a buffer from a file 1095 * 1096 * @param path the file path 1097 * @param remoteComposeState the associated state 1098 * @return the RemoteComposeBuffer 1099 * @throws IOException 1100 */ 1101 @NonNull fromFile( @onNull String path, @NonNull RemoteComposeState remoteComposeState)1102 public static RemoteComposeBuffer fromFile( 1103 @NonNull String path, @NonNull RemoteComposeState remoteComposeState) 1104 throws IOException { 1105 RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState); 1106 read(new File(path), buffer); 1107 return buffer; 1108 } 1109 1110 /** 1111 * Create a RemoteComposeBuffer from a file 1112 * 1113 * @param file A file 1114 * @param remoteComposeState The RemoteComposeState 1115 * @return A RemoteComposeBuffer 1116 * @throws IOException if the file cannot be read 1117 */ 1118 @NonNull fromFile( @onNull File file, @NonNull RemoteComposeState remoteComposeState)1119 public RemoteComposeBuffer fromFile( 1120 @NonNull File file, @NonNull RemoteComposeState remoteComposeState) throws IOException { 1121 RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState); 1122 read(file, buffer); 1123 return buffer; 1124 } 1125 1126 /** 1127 * Create a RemoteComposeBuffer from an InputStream 1128 * 1129 * @param inputStream An InputStream 1130 * @param remoteComposeState The RemoteComposeState 1131 * @return A RemoteComposeBuffer 1132 */ 1133 @NonNull fromInputStream( @onNull InputStream inputStream, @NonNull RemoteComposeState remoteComposeState)1134 public static RemoteComposeBuffer fromInputStream( 1135 @NonNull InputStream inputStream, @NonNull RemoteComposeState remoteComposeState) { 1136 RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState); 1137 read(inputStream, buffer); 1138 return buffer; 1139 } 1140 1141 /** 1142 * Create a RemoteComposeBuffer from an array of operations 1143 * 1144 * @param operations An array of operations 1145 * @param buffer A RemoteComposeBuffer 1146 * @return A RemoteComposeBuffer 1147 */ 1148 @NonNull copyFromOperations( @onNull ArrayList<Operation> operations, @NonNull RemoteComposeBuffer buffer)1149 RemoteComposeBuffer copyFromOperations( 1150 @NonNull ArrayList<Operation> operations, @NonNull RemoteComposeBuffer buffer) { 1151 1152 for (Operation operation : operations) { 1153 operation.write(buffer.mBuffer); 1154 } 1155 return buffer; 1156 } 1157 1158 /** 1159 * Write the given RemoteComposeBuffer to the given file 1160 * 1161 * @param buffer a RemoteComposeBuffer 1162 * @param file a target file 1163 */ write(@onNull RemoteComposeBuffer buffer, @NonNull File file)1164 public void write(@NonNull RemoteComposeBuffer buffer, @NonNull File file) { 1165 try { 1166 FileOutputStream fd = new FileOutputStream(file); 1167 fd.write(buffer.mBuffer.getBuffer(), 0, buffer.mBuffer.getSize()); 1168 fd.flush(); 1169 fd.close(); 1170 } catch (Exception ex) { 1171 throw new RuntimeException(ex); 1172 } 1173 } 1174 1175 /** 1176 * Read the content of the file into the buffer 1177 * 1178 * @param file a target file 1179 * @param buffer a RemoteComposeBuffer 1180 * @throws IOException 1181 */ read(@onNull File file, @NonNull RemoteComposeBuffer buffer)1182 static void read(@NonNull File file, @NonNull RemoteComposeBuffer buffer) throws IOException { 1183 FileInputStream fd = new FileInputStream(file); 1184 read(fd, buffer); 1185 } 1186 1187 /** 1188 * Initialize a buffer from an input stream 1189 * 1190 * @param fd the input stream 1191 * @param buffer a RemoteComposeBuffer 1192 */ read(@onNull InputStream fd, @NonNull RemoteComposeBuffer buffer)1193 public static void read(@NonNull InputStream fd, @NonNull RemoteComposeBuffer buffer) { 1194 try { 1195 byte[] bytes = readAllBytes(fd); 1196 buffer.reset(bytes.length); 1197 System.arraycopy(bytes, 0, buffer.mBuffer.mBuffer, 0, bytes.length); 1198 buffer.mBuffer.mSize = bytes.length; 1199 } catch (Exception e) { 1200 throw new RuntimeException(e); 1201 } 1202 } 1203 1204 /** 1205 * Load a byte buffer from the input stream 1206 * 1207 * @param is the input stream 1208 * @return a byte buffer containing the input stream content 1209 * @throws IOException 1210 */ readAllBytes(@onNull InputStream is)1211 private static byte[] readAllBytes(@NonNull InputStream is) throws IOException { 1212 byte[] buff = new byte[32 * 1024]; // moderate size buff to start 1213 int red = 0; 1214 while (true) { 1215 int ret = is.read(buff, red, buff.length - red); 1216 if (ret == -1) { 1217 is.close(); 1218 return Arrays.copyOf(buff, red); 1219 } 1220 red += ret; 1221 if (red == buff.length) { 1222 buff = Arrays.copyOf(buff, buff.length * 2); 1223 } 1224 } 1225 } 1226 1227 /** 1228 * add a Pre-concat the current matrix with the specified skew. 1229 * 1230 * @param skewX The amount to skew in X 1231 * @param skewY The amount to skew in Y 1232 */ addMatrixSkew(float skewX, float skewY)1233 public void addMatrixSkew(float skewX, float skewY) { 1234 MatrixSkew.apply(mBuffer, skewX, skewY); 1235 } 1236 1237 /** 1238 * This call balances a previous call to save(), and is used to remove all modifications to the 1239 * matrix/clip state since the last save call. Do not call restore() more times than save() was 1240 * called. 1241 */ addMatrixRestore()1242 public void addMatrixRestore() { 1243 MatrixRestore.apply(mBuffer); 1244 } 1245 1246 /** 1247 * Add a saves the current matrix and clip onto a private stack. 1248 * 1249 * <p>Subsequent calls to translate,scale,rotate,skew,concat or clipRect, clipPath will all 1250 * operate as usual, but when the balancing call to restore() is made, those calls will be 1251 * forgotten, and the settings that existed before the save() will be reinstated. 1252 */ addMatrixSave()1253 public void addMatrixSave() { 1254 MatrixSave.apply(mBuffer); 1255 } 1256 1257 /** 1258 * add a pre-concat the current matrix with the specified rotation. 1259 * 1260 * @param angle The amount to rotate, in degrees 1261 * @param centerX The x-coord for the pivot point (unchanged by the rotation) 1262 * @param centerY The y-coord for the pivot point (unchanged by the rotation) 1263 */ addMatrixRotate(float angle, float centerX, float centerY)1264 public void addMatrixRotate(float angle, float centerX, float centerY) { 1265 MatrixRotate.apply(mBuffer, angle, centerX, centerY); 1266 } 1267 1268 /** 1269 * add a Pre-concat to the current matrix with the specified translation 1270 * 1271 * @param dx The distance to translate in X 1272 * @param dy The distance to translate in Y 1273 */ addMatrixTranslate(float dx, float dy)1274 public void addMatrixTranslate(float dx, float dy) { 1275 MatrixTranslate.apply(mBuffer, dx, dy); 1276 } 1277 1278 /** 1279 * Add a pre-concat of the current matrix with the specified scale. 1280 * 1281 * @param scaleX The amount to scale in X 1282 * @param scaleY The amount to scale in Y 1283 */ addMatrixScale(float scaleX, float scaleY)1284 public void addMatrixScale(float scaleX, float scaleY) { 1285 MatrixScale.apply(mBuffer, scaleX, scaleY, Float.NaN, Float.NaN); 1286 } 1287 1288 /** 1289 * Add a pre-concat of the current matrix with the specified scale. 1290 * 1291 * @param scaleX The amount to scale in X 1292 * @param scaleY The amount to scale in Y 1293 * @param centerX The x-coord for the pivot point (unchanged by the scale) 1294 * @param centerY The y-coord for the pivot point (unchanged by the scale) 1295 */ addMatrixScale(float scaleX, float scaleY, float centerX, float centerY)1296 public void addMatrixScale(float scaleX, float scaleY, float centerX, float centerY) { 1297 MatrixScale.apply(mBuffer, scaleX, scaleY, centerX, centerY); 1298 } 1299 1300 /** 1301 * sets the clip based on clip id 1302 * 1303 * @param pathId 0 clears the clip 1304 */ addClipPath(int pathId)1305 public void addClipPath(int pathId) { 1306 ClipPath.apply(mBuffer, pathId); 1307 } 1308 1309 /** 1310 * Sets the clip based on clip rec 1311 * 1312 * @param left left coordinate of the clip rectangle 1313 * @param top top coordinate of the clip rectangle 1314 * @param right right coordinate of the clip rectangle 1315 * @param bottom bottom coordinate of the clip rectangle 1316 */ addClipRect(float left, float top, float right, float bottom)1317 public void addClipRect(float left, float top, float right, float bottom) { 1318 ClipRect.apply(mBuffer, left, top, right, bottom); 1319 } 1320 1321 /** 1322 * Add a float return a NaN number pointing to that float 1323 * 1324 * @param value the value of the float 1325 * @return the nan id of float 1326 */ addFloat(float value)1327 public float addFloat(float value) { 1328 int id = mRemoteComposeState.cacheFloat(value); 1329 FloatConstant.apply(mBuffer, id, value); 1330 return Utils.asNan(id); 1331 } 1332 1333 /** 1334 * Reserve a float and returns a NaN number pointing to that float 1335 * 1336 * @return the nan id of float 1337 */ reserveFloatVariable()1338 public float reserveFloatVariable() { 1339 int id = mRemoteComposeState.nextId(); 1340 return Utils.asNan(id); 1341 } 1342 1343 /** 1344 * Add a Integer return an id number pointing to that float. 1345 * 1346 * @param value adds an integer and assigns it an id 1347 * @return the id of the integer to be used 1348 */ addInteger(int value)1349 public int addInteger(int value) { 1350 int id = mRemoteComposeState.cacheInteger(value); 1351 IntegerConstant.apply(mBuffer, id, value); 1352 return id; 1353 } 1354 1355 /** 1356 * Add a long constant return a id. They can be used as parameters to Custom Attributes. 1357 * 1358 * @param value the value of the long 1359 * @return the id of the command representing long 1360 */ addLong(long value)1361 public int addLong(long value) { 1362 int id = mRemoteComposeState.nextId(); 1363 LongConstant.apply(mBuffer, id, value); 1364 return id; 1365 } 1366 1367 /** 1368 * Add a boolean constant return a id. They can be used as parameters to Custom Attributes. 1369 * 1370 * @param value the value of the boolean 1371 * @return the id 1372 */ addBoolean(boolean value)1373 public int addBoolean(boolean value) { 1374 int id = mRemoteComposeState.nextId(); 1375 BooleanConstant.apply(mBuffer, id, value); 1376 return id; 1377 } 1378 1379 /** 1380 * Add a IntegerId as float ID. 1381 * 1382 * @param id id to be converted 1383 * @return the id wrapped in a NaN 1384 */ asFloatId(int id)1385 public float asFloatId(int id) { 1386 return Utils.asNan(id); 1387 } 1388 1389 /** 1390 * Add a float that is a computation based on variables 1391 * 1392 * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7 1393 * @return NaN id of the result of the calculation 1394 */ addAnimatedFloat(@onNull float... value)1395 public float addAnimatedFloat(@NonNull float... value) { 1396 int id = mRemoteComposeState.cacheData(value); 1397 FloatExpression.apply(mBuffer, id, value, null); 1398 return Utils.asNan(id); 1399 } 1400 1401 /** 1402 * Add a touch handle system 1403 * 1404 * @param id the float NaN id used for the returned position 1405 * @param value the default value 1406 * @param min the minimum value 1407 * @param max the maximum value 1408 * @param velocityId the id for the velocity TODO support in v2 1409 * @param exp The Float Expression 1410 * @param touchMode the touch up handling behaviour 1411 * @param touchSpec the touch up handling parameters 1412 * @param easingSpec the easing parameter TODO support in v2 1413 */ addTouchExpression( float id, float value, float min, float max, float velocityId, int touchEffects, float[] exp, int touchMode, float[] touchSpec, float[] easingSpec)1414 public void addTouchExpression( 1415 float id, 1416 float value, 1417 float min, 1418 float max, 1419 float velocityId, 1420 int touchEffects, 1421 float[] exp, 1422 int touchMode, 1423 float[] touchSpec, 1424 float[] easingSpec) { 1425 TouchExpression.apply( 1426 mBuffer, 1427 Utils.idFromNan(id), 1428 value, 1429 min, 1430 max, 1431 velocityId, 1432 touchEffects, 1433 exp, 1434 touchMode, 1435 touchSpec, 1436 easingSpec); 1437 } 1438 1439 /** 1440 * Add a touch handle system 1441 * 1442 * @param value the default value 1443 * @param min the minimum value 1444 * @param max the maximum value 1445 * @param velocityId the id for the velocity TODO support in v2 1446 * @param exp The Float Expression 1447 * @param touchMode the touch up handling behaviour 1448 * @param touchSpec the touch up handling parameters 1449 * @param easingSpec the easing parameter TODO support in v2 1450 * @return id of the variable to be used controlled by touch handling 1451 */ addTouchExpression( float value, float min, float max, float velocityId, int touchEffects, float[] exp, int touchMode, float[] touchSpec, float[] easingSpec)1452 public float addTouchExpression( 1453 float value, 1454 float min, 1455 float max, 1456 float velocityId, 1457 int touchEffects, 1458 float[] exp, 1459 int touchMode, 1460 float[] touchSpec, 1461 float[] easingSpec) { 1462 float id = Utils.asNan(mRemoteComposeState.nextId()); 1463 addTouchExpression( 1464 id, 1465 value, 1466 min, 1467 max, 1468 velocityId, 1469 touchEffects, 1470 exp, 1471 touchMode, 1472 touchSpec, 1473 easingSpec); 1474 return id; 1475 } 1476 1477 /** 1478 * Add a float that is a computation based on variables. see packAnimation 1479 * 1480 * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7 1481 * @param animation Array of floats that represents animation 1482 * @return NaN id of the result of the calculation 1483 */ addAnimatedFloat(@onNull float[] value, @Nullable float[] animation)1484 public float addAnimatedFloat(@NonNull float[] value, @Nullable float[] animation) { 1485 int id = mRemoteComposeState.cacheData(value); 1486 FloatExpression.apply(mBuffer, id, value, animation); 1487 return Utils.asNan(id); 1488 } 1489 1490 /** 1491 * measure the text and return a measure as a float 1492 * 1493 * @param textId id of the text 1494 * @param mode the mode 0 is the width 1495 * @return 1496 */ textMeasure(int textId, int mode)1497 public float textMeasure(int textId, int mode) { 1498 int id = mRemoteComposeState.cacheData(textId + mode * 31); 1499 TextMeasure.apply(mBuffer, id, textId, mode); 1500 return Utils.asNan(id); 1501 } 1502 1503 /** 1504 * measure the text and return the length of the text as float 1505 * 1506 * @param textId id of the text 1507 * @return id of a float that is the length 1508 */ textLength(int textId)1509 public float textLength(int textId) { 1510 // The cache id is computed buy merging the two values together 1511 // to create a relatively unique value 1512 int id = mRemoteComposeState.cacheData(textId + (TextLength.id() << 16)); 1513 TextLength.apply(mBuffer, id, textId); 1514 return Utils.asNan(id); 1515 } 1516 1517 /** 1518 * add a float array 1519 * 1520 * @param values 1521 * @return the id of the array, encoded as a float NaN 1522 */ addFloatArray(@onNull float[] values)1523 public float addFloatArray(@NonNull float[] values) { 1524 int id = mRemoteComposeState.cacheData(values, NanMap.TYPE_ARRAY); 1525 DataListFloat.apply(mBuffer, id, values); 1526 return Utils.asNan(id); 1527 } 1528 1529 /** 1530 * This creates a list of individual floats 1531 * 1532 * @param values array of floats to be individually stored 1533 * @return id of the list 1534 */ addFloatList(@onNull float[] values)1535 public float addFloatList(@NonNull float[] values) { 1536 int[] listId = new int[values.length]; 1537 for (int i = 0; i < listId.length; i++) { 1538 listId[i] = mRemoteComposeState.cacheFloat(values[i]); 1539 FloatConstant.apply(mBuffer, listId[i], values[i]); 1540 } 1541 return addList(listId); 1542 } 1543 1544 /** 1545 * This creates a list of individual floats 1546 * 1547 * @param listId array id to be stored 1548 * @return id of the list 1549 */ addList(@onNull int[] listId)1550 public float addList(@NonNull int[] listId) { 1551 int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY); 1552 DataListIds.apply(mBuffer, id, listId); 1553 return Utils.asNan(id); 1554 } 1555 1556 /** 1557 * add a float map 1558 * 1559 * @param keys 1560 * @param values 1561 * @return the id of the map, encoded as a float NaN 1562 */ addFloatMap(@onNull String[] keys, @NonNull float[] values)1563 public float addFloatMap(@NonNull String[] keys, @NonNull float[] values) { 1564 int[] listId = new int[values.length]; 1565 byte[] type = new byte[values.length]; 1566 for (int i = 0; i < listId.length; i++) { 1567 listId[i] = mRemoteComposeState.cacheFloat(values[i]); 1568 FloatConstant.apply(mBuffer, listId[i], values[i]); 1569 type[i] = DataMapIds.TYPE_FLOAT; 1570 } 1571 return addMap(keys, type, listId); 1572 } 1573 1574 /** 1575 * add an int map 1576 * 1577 * @param keys 1578 * @param listId 1579 * @return the id of the map, encoded as a float NaN 1580 */ addMap(@onNull String[] keys, @Nullable byte[] types, @NonNull int[] listId)1581 public int addMap(@NonNull String[] keys, @Nullable byte[] types, @NonNull int[] listId) { 1582 int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY); 1583 DataMapIds.apply(mBuffer, id, keys, types, listId); 1584 return id; 1585 } 1586 1587 /** 1588 * This provides access to text in RemoteList 1589 * 1590 * <p>TODO: do we want both a float and an int index version of this method? bbade@ TODO 1591 * for @hoford - add a unit test for this method 1592 * 1593 * @param dataSet 1594 * @param index index as a float variable 1595 * @return 1596 */ textLookup(float dataSet, float index)1597 public int textLookup(float dataSet, float index) { 1598 long hash = 1599 (((long) Float.floatToRawIntBits(dataSet)) << 32) 1600 + Float.floatToRawIntBits( 1601 index); // TODO: is this the correct ()s? -- bbade@ 1602 int id = mRemoteComposeState.cacheData(hash); 1603 TextLookup.apply(mBuffer, id, Utils.idFromNan(dataSet), index); 1604 return id; 1605 } 1606 1607 /** 1608 * This provides access to text in RemoteList 1609 * 1610 * <p>TODO for hoford - add a unit test for this method 1611 * 1612 * @param dataSet 1613 * @param index index as an int variable 1614 * @return 1615 */ textLookup(float dataSet, int index)1616 public int textLookup(float dataSet, int index) { 1617 long hash = 1618 (((long) Float.floatToRawIntBits(dataSet)) << 32) 1619 + Float.floatToRawIntBits(index); // TODO: is this the correct ()s? 1620 int id = mRemoteComposeState.cacheData(hash); 1621 TextLookupInt.apply(mBuffer, id, Utils.idFromNan(dataSet), index); 1622 return id; 1623 } 1624 1625 /** 1626 * Add and integer expression 1627 * 1628 * @param mask defines which elements are operators or variables 1629 * @param value array of values to calculate maximum 32 1630 * @return the id as an integer 1631 */ addIntegerExpression(int mask, @NonNull int[] value)1632 public int addIntegerExpression(int mask, @NonNull int[] value) { 1633 int id = mRemoteComposeState.cacheData(value); 1634 IntegerExpression.apply(mBuffer, id, mask, value); 1635 return id; 1636 } 1637 1638 /** 1639 * Add a simple color 1640 * 1641 * @param color the RGB color value 1642 * @return id that represents that color 1643 */ addColor(int color)1644 public int addColor(int color) { 1645 ColorConstant c = new ColorConstant(0, color); 1646 short id = (short) mRemoteComposeState.cacheData(c); 1647 c.mColorId = id; 1648 c.write(mBuffer); 1649 return id; 1650 } 1651 1652 /** 1653 * Add a color that represents the tween between two colors 1654 * 1655 * @param color1 the ARGB value of the first color 1656 * @param color2 the ARGB value of the second color 1657 * @param tween the interpolation bet 1658 * @return id of the color (color ids are short) 1659 */ addColorExpression(int color1, int color2, float tween)1660 public short addColorExpression(int color1, int color2, float tween) { 1661 ColorExpression c = new ColorExpression(0, 0, color1, color2, tween); 1662 short id = (short) mRemoteComposeState.cacheData(c); 1663 c.mId = id; 1664 c.write(mBuffer); 1665 return id; 1666 } 1667 1668 /** 1669 * Add a color that represents the tween between two colors where color1 is the id of a color 1670 * 1671 * @param color1 id of color 1672 * @param color2 rgb color value 1673 * @param tween the tween between color1 and color2 (1 = color2) 1674 * @return id of the color (color ids are short) 1675 */ addColorExpression(short color1, int color2, float tween)1676 public short addColorExpression(short color1, int color2, float tween) { 1677 ColorExpression c = new ColorExpression(0, 1, color1, color2, tween); 1678 short id = (short) mRemoteComposeState.cacheData(c); 1679 c.mId = id; 1680 c.write(mBuffer); 1681 return id; 1682 } 1683 1684 /** 1685 * Add a color that represents the tween between two colors where color2 is the id of a color 1686 * 1687 * @param color1 the ARGB value of the first color 1688 * @param color2 id of the second color 1689 * @param tween the tween between color1 and color2 (1 = color2) 1690 * @return id of the color (color ids are short) 1691 */ addColorExpression(int color1, short color2, float tween)1692 public short addColorExpression(int color1, short color2, float tween) { 1693 ColorExpression c = new ColorExpression(0, 2, color1, color2, tween); 1694 short id = (short) mRemoteComposeState.cacheData(c); 1695 c.mId = id; 1696 c.write(mBuffer); 1697 return id; 1698 } 1699 1700 /** 1701 * Add a color that represents the tween between two colors where color1 & color2 are the ids of 1702 * colors 1703 * 1704 * @param color1 id of the first color 1705 * @param color2 id of the second color 1706 * @param tween the tween between color1 and color2 (1 = color2) 1707 * @return id of the color (color ids are short) 1708 */ addColorExpression(short color1, short color2, float tween)1709 public short addColorExpression(short color1, short color2, float tween) { 1710 ColorExpression c = new ColorExpression(0, 3, color1, color2, tween); 1711 short id = (short) mRemoteComposeState.cacheData(c); 1712 c.mId = id; 1713 c.write(mBuffer); 1714 return id; 1715 } 1716 1717 /** 1718 * Color calculated by Hue saturation and value. (as floats they can be variables used to create 1719 * color transitions) 1720 * 1721 * @param hue the Hue 1722 * @param sat the saturation 1723 * @param value the value 1724 * @return id of the color (color ids are short) 1725 */ addColorExpression(float hue, float sat, float value)1726 public short addColorExpression(float hue, float sat, float value) { 1727 ColorExpression c = new ColorExpression(0, hue, sat, value); 1728 short id = (short) mRemoteComposeState.cacheData(c); 1729 c.mId = id; 1730 c.write(mBuffer); 1731 return id; 1732 } 1733 1734 /** 1735 * Color calculated by Alpha, Hue saturation and value. (as floats they can be variables used to 1736 * create color transitions) 1737 * 1738 * @param alpha the Alpha 1739 * @param hue the hue 1740 * @param sat the saturation 1741 * @param value the value 1742 * @return id of the color (color ids are short) 1743 */ addColorExpression(int alpha, float hue, float sat, float value)1744 public short addColorExpression(int alpha, float hue, float sat, float value) { 1745 ColorExpression c = 1746 new ColorExpression(0, ColorExpression.HSV_MODE, alpha, hue, sat, value); 1747 short id = (short) mRemoteComposeState.cacheData(c); 1748 c.mId = id; 1749 c.write(mBuffer); 1750 return id; 1751 } 1752 1753 /** 1754 * Color calculated by Alpha, Red, Green and Blue. (as floats they can be variables used to 1755 * create color transitions) 1756 * 1757 * @param alpha the alpha value of the color 1758 * @param red the red component of the color 1759 * @param green the green component of the color 1760 * @param blue the blue component of the color 1761 * @return id of the color (color ids are short) 1762 */ addColorExpression(float alpha, float red, float green, float blue)1763 public short addColorExpression(float alpha, float red, float green, float blue) { 1764 ColorExpression c = 1765 new ColorExpression(0, ColorExpression.ARGB_MODE, alpha, red, green, blue); 1766 short id = (short) mRemoteComposeState.cacheData(c); 1767 c.mId = id; 1768 c.write(mBuffer); 1769 return id; 1770 } 1771 1772 /** 1773 * create and animation based on description and return as an array of floats. see 1774 * addAnimatedFloat 1775 * 1776 * @param duration the duration of the animation in seconds 1777 * @param type the type of animation 1778 * @param spec the parameters of the animation if any 1779 * @param initialValue the initial value if it animates to a start 1780 * @param wrap the wraps value so (e.g 360 so angles 355 would animate to 5) 1781 * @return 1782 */ packAnimation( float duration, int type, @Nullable float[] spec, float initialValue, float wrap)1783 public static @NonNull float[] packAnimation( 1784 float duration, int type, @Nullable float[] spec, float initialValue, float wrap) { 1785 1786 return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap); 1787 } 1788 1789 /** 1790 * This defines the name of a type of given object 1791 * 1792 * @param id of the float 1793 * @param name name of the float 1794 * @param type the type of variable NamedVariable.COLOR_TYPE, STRING_TYPE, etc 1795 */ setNamedVariable(int id, @NonNull String name, int type)1796 public void setNamedVariable(int id, @NonNull String name, int type) { 1797 NamedVariable.apply(mBuffer, id, type, name); 1798 } 1799 1800 /** 1801 * Returns a usable component id -- either the one passed in parameter if not -1 or a generated 1802 * one. 1803 * 1804 * @param id the current component id (if -1, we'll generate a new one) 1805 * @return a usable component id 1806 */ getComponentId(int id)1807 private int getComponentId(int id) { 1808 int resolvedId = 0; 1809 if (id != -1) { 1810 resolvedId = id; 1811 } else { 1812 mGeneratedComponentId--; 1813 resolvedId = mGeneratedComponentId; 1814 } 1815 return resolvedId; 1816 } 1817 1818 /** 1819 * Add a component start tag 1820 * 1821 * @param type type of component 1822 * @param id component id 1823 */ addComponentStart(int type, int id)1824 public void addComponentStart(int type, int id) { 1825 mLastComponentId = getComponentId(id); 1826 ComponentStart.apply(mBuffer, type, mLastComponentId, 0f, 0f); 1827 } 1828 1829 /** 1830 * Add a component start tag 1831 * 1832 * @param type type of component 1833 */ addComponentStart(int type)1834 public void addComponentStart(int type) { 1835 addComponentStart(type, -1); 1836 } 1837 1838 /** Add a component end tag */ addContainerEnd()1839 public void addContainerEnd() { 1840 ContainerEnd.apply(mBuffer); 1841 } 1842 1843 /** 1844 * Add a scroll modifier 1845 * 1846 * @param direction HORIZONTAL(0) or VERTICAL(1) 1847 * @param positionId the position id as a NaN 1848 * @param notches 1849 */ addModifierScroll(int direction, float positionId, int notches)1850 public void addModifierScroll(int direction, float positionId, int notches) { 1851 // TODO: add support for non-notch behaviors etc. 1852 float max = this.reserveFloatVariable(); 1853 float notchMax = this.reserveFloatVariable(); 1854 float touchExpressionDirection = 1855 direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y; 1856 1857 ScrollModifierOperation.apply(mBuffer, direction, positionId, max, notchMax); 1858 1859 this.addTouchExpression( 1860 positionId, 1861 0f, 1862 0f, 1863 max, 1864 0f, 1865 3, 1866 new float[] { 1867 touchExpressionDirection, -1, MUL, 1868 }, 1869 TouchExpression.STOP_NOTCHES_EVEN, 1870 new float[] {notches, notchMax}, 1871 null); 1872 1873 ContainerEnd.apply(mBuffer); 1874 } 1875 1876 /** 1877 * Add a scroll modifier 1878 * 1879 * @param direction HORIZONTAL(0) or VERTICAL(1) 1880 * @param positionId the position id as a NaN 1881 */ addModifierScroll(int direction, float positionId)1882 public void addModifierScroll(int direction, float positionId) { 1883 float max = this.reserveFloatVariable(); 1884 float notchMax = this.reserveFloatVariable(); 1885 float touchExpressionDirection = 1886 direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y; 1887 1888 ScrollModifierOperation.apply(mBuffer, direction, positionId, max, notchMax); 1889 this.addTouchExpression( 1890 positionId, 1891 0f, 1892 0f, 1893 max, 1894 0f, 1895 3, 1896 new float[] { 1897 touchExpressionDirection, -1, MUL, 1898 }, 1899 TouchExpression.STOP_GENTLY, 1900 null, 1901 null); 1902 ContainerEnd.apply(mBuffer); 1903 } 1904 1905 /** 1906 * Add a scroll modifier 1907 * 1908 * @param direction HORIZONTAL(0) or VERTICAL(1) 1909 */ addModifierScroll(int direction)1910 public void addModifierScroll(int direction) { 1911 float max = this.reserveFloatVariable(); 1912 ScrollModifierOperation.apply(mBuffer, direction, 0f, max, 0f); 1913 ContainerEnd.apply(mBuffer); 1914 } 1915 1916 /** 1917 * Add a background modifier of provided color 1918 * 1919 * @param color the color of the background 1920 * @param shape the background shape -- SHAPE_RECTANGLE, SHAPE_CIRCLE 1921 */ addModifierBackground(int color, int shape)1922 public void addModifierBackground(int color, int shape) { 1923 float r = (color >> 16 & 0xff) / 255.0f; 1924 float g = (color >> 8 & 0xff) / 255.0f; 1925 float b = (color & 0xff) / 255.0f; 1926 float a = (color >> 24 & 0xff) / 255.0f; 1927 BackgroundModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f, r, g, b, a, shape); 1928 } 1929 1930 /** 1931 * Add a border modifier 1932 * 1933 * @param borderWidth the border width 1934 * @param borderRoundedCorner the rounded corner radius if the shape is ROUNDED_RECT 1935 * @param color the color of the border 1936 * @param shape the shape of the border 1937 */ addModifierBorder( float borderWidth, float borderRoundedCorner, int color, int shape)1938 public void addModifierBorder( 1939 float borderWidth, float borderRoundedCorner, int color, int shape) { 1940 float r = (color >> 16 & 0xff) / 255.0f; 1941 float g = (color >> 8 & 0xff) / 255.0f; 1942 float b = (color & 0xff) / 255.0f; 1943 float a = (color >> 24 & 0xff) / 255.0f; 1944 BorderModifierOperation.apply( 1945 mBuffer, 0f, 0f, 0f, 0f, borderWidth, borderRoundedCorner, r, g, b, a, shape); 1946 } 1947 1948 /** 1949 * Add a padding modifier 1950 * 1951 * @param left left padding 1952 * @param top top padding 1953 * @param right right padding 1954 * @param bottom bottom padding 1955 */ addModifierPadding(float left, float top, float right, float bottom)1956 public void addModifierPadding(float left, float top, float right, float bottom) { 1957 PaddingModifierOperation.apply(mBuffer, left, top, right, bottom); 1958 } 1959 1960 /** 1961 * Add an offset modifier 1962 * 1963 * @param x x offset 1964 * @param y y offset 1965 */ addModifierOffset(float x, float y)1966 public void addModifierOffset(float x, float y) { 1967 OffsetModifierOperation.apply(mBuffer, x, y); 1968 } 1969 1970 /** 1971 * Add a zIndex modifier 1972 * 1973 * @param value z-Index value 1974 */ addModifierZIndex(float value)1975 public void addModifierZIndex(float value) { 1976 ZIndexModifierOperation.apply(mBuffer, value); 1977 } 1978 1979 /** Add a ripple effect on touch down as a modifier */ addModifierRipple()1980 public void addModifierRipple() { 1981 RippleModifierOperation.apply(mBuffer); 1982 } 1983 1984 /** 1985 * Add a marquee modifier 1986 * 1987 * @param iterations number of iterations 1988 * @param animationMode animation mode 1989 * @param repeatDelayMillis repeat delay 1990 * @param initialDelayMillis initial delay 1991 * @param spacing spacing between items 1992 * @param velocity velocity of the marquee 1993 */ addModifierMarquee( int iterations, int animationMode, float repeatDelayMillis, float initialDelayMillis, float spacing, float velocity)1994 public void addModifierMarquee( 1995 int iterations, 1996 int animationMode, 1997 float repeatDelayMillis, 1998 float initialDelayMillis, 1999 float spacing, 2000 float velocity) { 2001 MarqueeModifierOperation.apply( 2002 mBuffer, 2003 iterations, 2004 animationMode, 2005 repeatDelayMillis, 2006 initialDelayMillis, 2007 spacing, 2008 velocity); 2009 } 2010 2011 /** 2012 * Add a graphics layer 2013 * 2014 * @param attributes 2015 */ addModifierGraphicsLayer(HashMap<Integer, Object> attributes)2016 public void addModifierGraphicsLayer(HashMap<Integer, Object> attributes) { 2017 GraphicsLayerModifierOperation.apply(mBuffer, attributes); 2018 } 2019 2020 /** 2021 * Sets the clip based on rounded clip rect 2022 * 2023 * @param topStart 2024 * @param topEnd 2025 * @param bottomStart 2026 * @param bottomEnd 2027 */ addRoundClipRectModifier( float topStart, float topEnd, float bottomStart, float bottomEnd)2028 public void addRoundClipRectModifier( 2029 float topStart, float topEnd, float bottomStart, float bottomEnd) { 2030 RoundedClipRectModifierOperation.apply(mBuffer, topStart, topEnd, bottomStart, bottomEnd); 2031 } 2032 2033 /** Add a clip rect modifier */ addClipRectModifier()2034 public void addClipRectModifier() { 2035 ClipRectModifierOperation.apply(mBuffer); 2036 } 2037 2038 /** 2039 * add start of loop 2040 * 2041 * @param indexId id of the variable 2042 * @param from start value 2043 * @param step step value 2044 * @param until stop value 2045 */ addLoopStart(int indexId, float from, float step, float until)2046 public void addLoopStart(int indexId, float from, float step, float until) { 2047 LoopOperation.apply(mBuffer, indexId, from, step, until); 2048 } 2049 2050 /** Add a loop end */ addLoopEnd()2051 public void addLoopEnd() { 2052 ContainerEnd.apply(mBuffer); 2053 } 2054 2055 /** 2056 * add a state layout 2057 * 2058 * @param componentId id of the state 2059 * @param animationId animation id 2060 * @param horizontal horizontal alignment 2061 * @param vertical vertical alignment 2062 * @param indexId index of the state 2063 */ addStateLayout( int componentId, int animationId, int horizontal, int vertical, int indexId)2064 public void addStateLayout( 2065 int componentId, int animationId, int horizontal, int vertical, int indexId) { 2066 mLastComponentId = getComponentId(componentId); 2067 StateLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, indexId); 2068 } 2069 2070 /** 2071 * Add a box start tag 2072 * 2073 * @param componentId component id 2074 * @param animationId animation id 2075 * @param horizontal horizontal alignment 2076 * @param vertical vertical alignment 2077 */ addBoxStart(int componentId, int animationId, int horizontal, int vertical)2078 public void addBoxStart(int componentId, int animationId, int horizontal, int vertical) { 2079 mLastComponentId = getComponentId(componentId); 2080 BoxLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical); 2081 } 2082 2083 /** 2084 * Add a fitbox start tag 2085 * 2086 * @param componentId component id 2087 * @param animationId animation id 2088 * @param horizontal horizontal alignment 2089 * @param vertical vertical alignment 2090 */ addFitBoxStart(int componentId, int animationId, int horizontal, int vertical)2091 public void addFitBoxStart(int componentId, int animationId, int horizontal, int vertical) { 2092 mLastComponentId = getComponentId(componentId); 2093 FitBoxLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical); 2094 } 2095 2096 /** 2097 * Add an imagelayout command 2098 * 2099 * @param componentId component id 2100 * @param animationId animation id 2101 * @param bitmapId bitmap id 2102 */ addImage( int componentId, int animationId, int bitmapId, int scaleType, float alpha)2103 public void addImage( 2104 int componentId, int animationId, int bitmapId, int scaleType, float alpha) { 2105 mLastComponentId = getComponentId(componentId); 2106 ImageLayout.apply(mBuffer, componentId, animationId, bitmapId, scaleType, alpha); 2107 } 2108 2109 /** 2110 * Add a row start tag 2111 * 2112 * @param componentId component id 2113 * @param animationId animation id 2114 * @param horizontal horizontal alignment 2115 * @param vertical vertical alignment 2116 * @param spacedBy spacing between items 2117 */ addRowStart( int componentId, int animationId, int horizontal, int vertical, float spacedBy)2118 public void addRowStart( 2119 int componentId, int animationId, int horizontal, int vertical, float spacedBy) { 2120 mLastComponentId = getComponentId(componentId); 2121 RowLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy); 2122 } 2123 2124 /** 2125 * Add a row start tag 2126 * 2127 * @param componentId component id 2128 * @param animationId animation id 2129 * @param horizontal horizontal alignment 2130 * @param vertical vertical alignment 2131 * @param spacedBy spacing between items 2132 */ addCollapsibleRowStart( int componentId, int animationId, int horizontal, int vertical, float spacedBy)2133 public void addCollapsibleRowStart( 2134 int componentId, int animationId, int horizontal, int vertical, float spacedBy) { 2135 mLastComponentId = getComponentId(componentId); 2136 CollapsibleRowLayout.apply( 2137 mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy); 2138 } 2139 2140 /** 2141 * Add a column start tag 2142 * 2143 * @param componentId component id 2144 * @param animationId animation id 2145 * @param horizontal horizontal alignment 2146 * @param vertical vertical alignment 2147 * @param spacedBy spacing between items 2148 */ addColumnStart( int componentId, int animationId, int horizontal, int vertical, float spacedBy)2149 public void addColumnStart( 2150 int componentId, int animationId, int horizontal, int vertical, float spacedBy) { 2151 mLastComponentId = getComponentId(componentId); 2152 ColumnLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy); 2153 } 2154 2155 /** 2156 * Add a column start tag 2157 * 2158 * @param componentId component id 2159 * @param animationId animation id 2160 * @param horizontal horizontal alignment 2161 * @param vertical vertical alignment 2162 * @param spacedBy spacing between items 2163 */ addCollapsibleColumnStart( int componentId, int animationId, int horizontal, int vertical, float spacedBy)2164 public void addCollapsibleColumnStart( 2165 int componentId, int animationId, int horizontal, int vertical, float spacedBy) { 2166 mLastComponentId = getComponentId(componentId); 2167 CollapsibleColumnLayout.apply( 2168 mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy); 2169 } 2170 2171 /** 2172 * Add a canvas start tag 2173 * 2174 * @param componentId component id 2175 * @param animationId animation id 2176 */ addCanvasStart(int componentId, int animationId)2177 public void addCanvasStart(int componentId, int animationId) { 2178 mLastComponentId = getComponentId(componentId); 2179 CanvasLayout.apply(mBuffer, mLastComponentId, animationId); 2180 } 2181 2182 /** 2183 * Add a canvas content start tag 2184 * 2185 * @param componentId component id 2186 */ addCanvasContentStart(int componentId)2187 public void addCanvasContentStart(int componentId) { 2188 mLastComponentId = getComponentId(componentId); 2189 CanvasContent.apply(mBuffer, mLastComponentId); 2190 } 2191 2192 /** Add a root start tag */ addRootStart()2193 public void addRootStart() { 2194 mLastComponentId = getComponentId(-1); 2195 RootLayoutComponent.apply(mBuffer, mLastComponentId); 2196 } 2197 2198 /** Add a content start tag */ addContentStart()2199 public void addContentStart() { 2200 mLastComponentId = getComponentId(-1); 2201 LayoutComponentContent.apply(mBuffer, mLastComponentId); 2202 } 2203 2204 /** Add a canvas operations start tag */ addCanvasOperationsStart()2205 public void addCanvasOperationsStart() { 2206 CanvasOperations.apply(mBuffer); 2207 } 2208 2209 /** Add container hosting actions */ addRunActionsStart()2210 public void addRunActionsStart() { 2211 RunActionOperation.apply(mBuffer); 2212 } 2213 2214 /** 2215 * Add a component width value 2216 * 2217 * @param id id of the value 2218 */ addComponentWidthValue(int id)2219 public void addComponentWidthValue(int id) { 2220 ComponentValue.apply(mBuffer, ComponentValue.WIDTH, mLastComponentId, id); 2221 } 2222 2223 /** 2224 * Add a component height value 2225 * 2226 * @param id id of the value 2227 */ addComponentHeightValue(int id)2228 public void addComponentHeightValue(int id) { 2229 ComponentValue.apply(mBuffer, ComponentValue.HEIGHT, mLastComponentId, id); 2230 } 2231 2232 /** 2233 * Add a text component start tag 2234 * 2235 * @param componentId component id 2236 * @param animationId animation id 2237 * @param textId id of the text 2238 * @param color color of the text 2239 * @param fontSize font size 2240 * @param fontStyle font style (0 : Normal, 1 : Italic) 2241 * @param fontWeight font weight (1 to 1000, normal is 400) 2242 * @param fontFamily font family or null 2243 * @param overflow 2244 * @param maxLines 2245 */ addTextComponentStart( int componentId, int animationId, int textId, int color, float fontSize, int fontStyle, float fontWeight, @Nullable String fontFamily, int textAlign, int overflow, int maxLines)2246 public void addTextComponentStart( 2247 int componentId, 2248 int animationId, 2249 int textId, 2250 int color, 2251 float fontSize, 2252 int fontStyle, 2253 float fontWeight, 2254 @Nullable String fontFamily, 2255 int textAlign, 2256 int overflow, 2257 int maxLines) { 2258 mLastComponentId = getComponentId(componentId); 2259 int fontFamilyId = -1; 2260 if (fontFamily != null) { 2261 fontFamilyId = addText(fontFamily); 2262 } 2263 TextLayout.apply( 2264 mBuffer, 2265 mLastComponentId, 2266 animationId, 2267 textId, 2268 color, 2269 fontSize, 2270 fontStyle, 2271 fontWeight, 2272 fontFamilyId, 2273 textAlign, 2274 overflow, 2275 maxLines); 2276 } 2277 2278 /** 2279 * Returns the next available id for the given type 2280 * 2281 * @param type the type of the value 2282 * @return a unique id 2283 */ createID(int type)2284 public int createID(int type) { 2285 return mRemoteComposeState.nextId(type); 2286 } 2287 2288 /** 2289 * Returns the next available id 2290 * 2291 * @return a unique id 2292 */ nextId()2293 public int nextId() { 2294 return mRemoteComposeState.nextId(); 2295 } 2296 2297 /** 2298 * add an impulse. (must be followed by impulse end) 2299 * 2300 * @param duration duration of the impulse 2301 * @param start the start time 2302 */ addImpulse(float duration, float start)2303 public void addImpulse(float duration, float start) { 2304 ImpulseOperation.apply(mBuffer, duration, start); 2305 } 2306 2307 /** add an impulse process */ addImpulseProcess()2308 public void addImpulseProcess() { 2309 ImpulseProcess.apply(mBuffer); 2310 } 2311 2312 /** Add an impulse end */ addImpulseEnd()2313 public void addImpulseEnd() { 2314 ContainerEnd.apply(mBuffer); 2315 } 2316 2317 /** 2318 * Start a particle engine container & initial setup 2319 * 2320 * @param id the particle engine id 2321 * @param varIds list of variable ids 2322 * @param initialExpressions the expressions used to initialize the variables 2323 * @param particleCount the number of particles to draw 2324 */ addParticles( int id, int[] varIds, float[][] initialExpressions, int particleCount)2325 public void addParticles( 2326 int id, int[] varIds, float[][] initialExpressions, int particleCount) { 2327 ParticlesCreate.apply(mBuffer, id, varIds, initialExpressions, particleCount); 2328 } 2329 2330 /** 2331 * Setup the particle engine loop 2332 * 2333 * @param id the particle engine id 2334 * @param restart value on restart 2335 * @param expressions the expressions used to update the variables during the particles run 2336 */ addParticlesLoop(int id, float[] restart, float[][] expressions)2337 public void addParticlesLoop(int id, float[] restart, float[][] expressions) { 2338 ParticlesLoop.apply(mBuffer, id, restart, expressions); 2339 } 2340 2341 /** Closes the particle engine container */ addParticleLoopEnd()2342 public void addParticleLoopEnd() { 2343 ContainerEnd.apply(mBuffer); 2344 } 2345 2346 /** 2347 * @param fid The id of the function 2348 * @param args The arguments of the function 2349 */ defineFloatFunction(int fid, int[] args)2350 public void defineFloatFunction(int fid, int[] args) { 2351 FloatFunctionDefine.apply(mBuffer, fid, args); 2352 } 2353 2354 /** end the definition of the function */ addEndFloatFunctionDef()2355 public void addEndFloatFunctionDef() { 2356 ContainerEnd.apply(mBuffer); 2357 } 2358 2359 /** 2360 * add a function call 2361 * 2362 * @param id the id of the function to call 2363 * @param args the arguments of the function 2364 */ callFloatFunction(int id, float[] args)2365 public void callFloatFunction(int id, float[] args) { 2366 FloatFunctionCall.apply(mBuffer, id, args); 2367 } 2368 2369 /** 2370 * @param bitmapId the id of the bitmap 2371 * @param attribute the attribute to get 2372 * @return the nan id of the attribute 2373 */ bitmapAttribute(int bitmapId, short attribute)2374 public float bitmapAttribute(int bitmapId, short attribute) { 2375 int id = mRemoteComposeState.nextId(); 2376 ImageAttribute.apply(mBuffer, id, bitmapId, attribute, null); 2377 return Utils.asNan(id); 2378 } 2379 2380 /** 2381 * @param textId the id of the bitmap 2382 * @param attribute the attribute to get 2383 * @return the nan id of the attribute 2384 */ textAttribute(int textId, short attribute)2385 public float textAttribute(int textId, short attribute) { 2386 int id = mRemoteComposeState.nextId(); 2387 TextAttribute.apply(mBuffer, id, textId, attribute); 2388 return Utils.asNan(id); 2389 } 2390 2391 /** 2392 * @param timeId the id of the long 2393 * @param attribute the attribute to get 2394 * @param args the arguments of the function 2395 * @return the nan id of the attribute 2396 */ timeAttribute(int timeId, short attribute, int... args)2397 public float timeAttribute(int timeId, short attribute, int... args) { 2398 int id = mRemoteComposeState.nextId(); 2399 TimeAttribute.apply(mBuffer, id, timeId, attribute, args); 2400 return Utils.asNan(id); 2401 } 2402 2403 /** In the context of a component draw modifier, draw the content of the component */ drawComponentContent()2404 public void drawComponentContent() { 2405 DrawContent.apply(mBuffer); 2406 } 2407 2408 /** 2409 * Ensures the bitmap is stored. 2410 * 2411 * @param image the bitbap to store 2412 * @return the id of the bitmap 2413 */ storeBitmap(Object image)2414 private int storeBitmap(Object image) { 2415 int imageId = mRemoteComposeState.dataGetId(image); 2416 if (imageId == -1) { 2417 imageId = mRemoteComposeState.cacheData(image); 2418 byte[] data = mPlatform.imageToByteArray(image); // todo: potential npe 2419 short imageWidth = (short) mPlatform.getImageWidth(image); 2420 short imageHeight = (short) mPlatform.getImageHeight(image); 2421 if (mPlatform.isAlpha8Image(image)) { 2422 BitmapData.apply( 2423 mBuffer, 2424 imageId, 2425 BitmapData.TYPE_PNG_ALPHA_8, 2426 imageWidth, 2427 BitmapData.ENCODING_INLINE, 2428 imageHeight, 2429 data); // todo: potential npe 2430 } else { 2431 BitmapData.apply( 2432 mBuffer, imageId, imageWidth, imageHeight, data); // todo: potential npe 2433 } 2434 } 2435 2436 return imageId; 2437 } 2438 2439 /** 2440 * Combine two paths 2441 * 2442 * @param id output id 2443 * @param path1 first path 2444 * @param path2 second path 2445 * @param op operation to perform OP_DIFFERENCE, OP_INTERSECT, OP_REVERSE_DIFFERENCE, OP_UNION, 2446 * OP_XOR 2447 */ pathCombine(int id, int path1, int path2, byte op)2448 public void pathCombine(int id, int path1, int path2, byte op) { 2449 PathCombine.apply(mBuffer, id, path1, path2, op); 2450 } 2451 2452 /** 2453 * Perform a haptic feedback 2454 * 2455 * @param feedbackConstant 2456 */ performHaptic(int feedbackConstant)2457 public void performHaptic(int feedbackConstant) { 2458 HapticFeedback.apply(mBuffer, feedbackConstant); 2459 } 2460 2461 /** 2462 * Add a conditional operation 2463 * 2464 * @param type type of comparison 2465 * @param a first value 2466 * @param b second value 2467 */ addConditionalOperations(byte type, float a, float b)2468 public void addConditionalOperations(byte type, float a, float b) { 2469 ConditionalOperations.apply(mBuffer, type, a, b); 2470 } 2471 2472 /** 2473 * Add a debug message 2474 * 2475 * @param textId text id 2476 * @param value value 2477 * @param flags flags 2478 */ addDebugMessage(int textId, float value, int flags)2479 public void addDebugMessage(int textId, float value, int flags) { 2480 DebugMessage.apply(mBuffer, textId, value, flags); 2481 } 2482 2483 /** 2484 * Return a color attribute value on the given color 2485 * 2486 * @param baseColor 2487 * @param type type of attribute 2488 * @return 2489 */ getColorAttribute(int baseColor, short type)2490 public float getColorAttribute(int baseColor, short type) { 2491 int id = mRemoteComposeState.nextId(); 2492 ColorAttribute.apply(mBuffer, id, baseColor, type); 2493 return Utils.asNan(id); 2494 } 2495 } 2496