Skip to content

11.16.3

Latest

Choose a tag to compare

@github-actions github-actions released this 11 Nov 02:35
· 4 commits to master since this release

Features

Automatic message delivery

Rive workers now deliver their messages on the main thread automatically, so apps don't need to poll them. Replies, errors, and property updates arrive without waiting for a poll, and the main thread doesn't run a poll every frame anymore. The polling APIs are deprecated, as described under Behavior changes. (Change)

Image loading in scripts

Scripts can now turn encoded image bytes into an Image with context:loadImage(buffer), then draw it or assign it to a view model image property. The bytes decode the same way as the file's own images. On Android, scripts should use loadImage to decode images, because context:decodeImage isn't available there. (Change)

Artboard properties in scripts

Scripts can now read and set view model artboard properties with vm:getArtboard(name), which wasn't available on Android before. context:bindableArtboard(name) returns one of the file's own artboards to assign to such a property, or the default artboard when called without a name. (Change)

Integration APIs

This release adds the opt-in @RiveImplementationApi annotation for runtimes and frameworks that integrate Rive into their own view systems, such as React Native. Four existing APIs are now public behind it: RiveWorker.stateMachineSettled, RiveWorker.unsettleStateMachine, RiveSurface.resize, and RiveFramePacer. They aren't part of the supported app API and can change or be removed in any minor release. Apps should keep using Rive, RiveCanvasSession, and the other public entry points. Contributed by @mfazekas in #483. (Change)

Fixes

  • Crashes when an image decode runs out of memory: An OutOfMemoryError while decoding an image crashed the app, for example on the Rive CmdServer thread. The decode now fails instead, and the image doesn't load. RiveWorker.decodeImage throws RiveImageException, and the worker keeps running. Reported in #481 and contributed by @mfazekas in #482. (Fix)
  • Legacy Canvas renderer crashes on undecodable images: With the Legacy Android Canvas renderer, an image that Android couldn't decode, for example because its bytes were malformed or memory ran out, crashed the app when drawn. The image now fails to load and draws nothing, as it does with the Rive renderer. (Fix)
  • List rows that contain themselves: A list whose rows ended up hosting rows of themselves crashed with a stack overflow. This happened, for example, when a list inside a row was bound to a path that the row's view model doesn't have, so the binding resolved to the list that made the row. Rive now stops building such rows and leaves them empty. (Fix)
  • Multi-finger scrolling: A scroll view dragged with two or more fingers could jump or fling. The newest finger now drives the scroll, and lifting it while other fingers stay down ends the drag without a fling. A second finger that moves during a scroll no longer clicks what it landed on. In the Legacy API, this applies when multiTouchEnabled is on. (Fix)
  • Presses stuck in hidden content: Lifting a finger while a nested artboard or list was hidden, collapsed, paused, or scaled to zero didn't reach the state machines inside it. The press could stay held and block later fingers. The lift now reaches them. (Fix)
  • List rows stuck in their hover state: A list row under the pointer never got a pointer exit when it scrolled out of a virtualized list or when its item was removed from a list. Whatever its enter listeners had set, such as a highlight or a view model value, stayed set. The row now gets its exit before it leaves, and the exit's events and view model changes are delivered. (Fix)
  • Row opacity overwritten by lists: A list's opacity replaced the opacity of its rows instead of combining with it, so an opacity that a row authored, animated, or bound was lost. Opacity changes also reached the rows a frame late, and rows added while the list was transparent didn't start hidden. Rows now keep their own opacity, and the list applies its opacity when it draws them. (Fix)
  • Text glyphs missing their image paint: Glyphs that a modifier faded, effect paths drawn below full opacity, and text with a shared global style drew their plain color instead of the image. The image was also fit to the glyphs, while the Editor fits it to the text's layout box. The image now covers every glyph and fits the layout box. (Fix)
  • Outdated list item indexes: A list item's List Index value, which scripts read with getIndex(), came from where a list component last placed the item, so it could lag behind the list. Right after an item was added, removed, or moved, the index could be outdated, and items of a list that no list component shows never got one. The index now updates as soon as the list changes. (Fix)
  • Stale layer masks on nested artboards: With GPU Canvas enabled, a layer mask over a nested artboard, or one using a nested artboard as its source, kept showing the content as it was first drawn while the nested artboard animated or was replaced. It now updates. (Fix)
  • GPU Canvas content a frame behind: With GPU Canvas enabled, a canvas that drew a script's GPU canvas showed that GPU canvas's previous frame. It now shows the current frame. (Fix)
  • Darkened script drawings with the Legacy Canvas renderer: With the Legacy Android Canvas renderer, a script's drawing under reduced opacity darkened as well as faded. For example, opaque red at 50% drew as dark red at half alpha. It now only fades, as images already did. (Fix)

Behavior changes

Polling APIs

With automatic delivery, the polling APIs are deprecated.

  • RiveWorker.beginPolling returns immediately. It used to suspend until the lifecycle was destroyed, so code after the call or in a surrounding finally block now runs right away. Release the worker when its lifecycle or owning component ends instead.
  • The autoPoll parameter of rememberRiveWorker, rememberRiveWorkerOrNull, and rememberDeferredRiveWorker is ignored. Messages are delivered even when it is false.
  • RiveWorker.pollMessages still delivers the queued messages at once, but apps no longer need to call it.

Messages also arrive while the lifecycle is below RESUMED, where polling used to stop.

Fills and strokes painted with an image

A fill or stroke painted with an image now draws nothing while the image is missing. It used to draw its plain color. This applies when no image is chosen, before the image loads or decodes, after it is cleared, and when an artboard paint can't render. On Android, artboard paints currently render only when GPU Canvas is enabled, so without GPU Canvas, fills and strokes that use one now draw nothing. (Change)

Performance

Faster first frame for RiveCanvasSession

RiveCanvasSession now draws its first frame as soon as it starts or resumes, without waiting for a frame callback, like the Rive composable. Previously, a busy main thread could delay that frame by more than one display refresh. Contributed by @charliesbot in #486. (Change)

Lists

Lists with many data-bound rows, such as charts, do less work each frame. Rows whose binds ease through an interpolator converter now go idle once their values land, instead of advancing every frame. A list applies its opacity when it draws its rows, rather than setting every row's opacity on each step of a fade. With the Rive renderer, virtualized lists also skip drawing buffered rows that the scroll viewport clips away. (Change, Change, Change)

In a C++ benchmark on an M2 Max Mac, the median idle advance of a line chart with 7,595 eased rows went from 4.96 ms to 0.22 ms. This illustrates one improvement, and gains depend on the content.

Data binding and state machines

A state machine now binds each view model property its conditions read once, not once per condition. A list row that wakes with nothing to change also goes idle again in the same frame. Adding and removing binds no longer scans a view model property's list of dependents, which speeds up opening and closing content where many binds read one property. After warm-up, state changes don't allocate. (Change, Change, Change)

In a C++ benchmark, content with about 182,000 binds to each of two view model properties showed its first frame after 1.42 s instead of 7.9 s. Gains depend on the content.


Full comparison