drawnui-react 0.1.0-preview.20 → 0.1.0-preview.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKIPPED.md +2 -2
- package/dist/controls/SkiaBackdrop.d.ts +6 -2
- package/dist/controls/SkiaBackdrop.js +46 -8
- package/dist/core/SkiaControl.d.ts +14 -0
- package/dist/core/SkiaControl.js +53 -8
- package/dist/core/SkiaEffect.d.ts +10 -0
- package/dist/core/SkiaEffect.js +18 -2
- package/package.json +5 -1
package/SKIPPED.md
CHANGED
|
@@ -15,7 +15,7 @@ DrawnUi name and semantics; nothing here is a redesign, only an omission.
|
|
|
15
15
|
| `Opacity`, `TranslationX/Y`, `Rotation`, `ScaleX/Y` (+`Scale`), `SkewX/Y`, `RotationX/Y/Z`, `TranslationZ`, `AnchorX/Y`, `HasTransform`, `RenderTransformMatrix`, `TransformPointToLocalSpace` | ported | Same matrix order as C# `ApplyTransforms` (pivot → rotation → scale/skew → 3D → translation; 3D = C# `Create3dMatrix`, camera 576 px, TranslationZ as depth), image caches drawn smooth once the final matrix distorts them (C# `WillDrawDistorted`, a picture holding image blits records again when its playback turns distorted or straight), opacity = `saveLayer` alpha over the subtree, gestures mapped through the inverse matrix (`IsGestureForChild`, child receives the local point), accessibility rect = transformed bounds. Animations: `AnimateAsync`, `AnimateRangeAsync`, `FadeToAsync`, `ScaleToAsync`, `TranslateToAsync`, `RotateToAsync` as Promises with `AbortSignal` (one running per kind, like the C# per-property cancellation). Not ported: `Perspective1/2`, `CameraAngle`, `LinkTransforms`, `AddTranslationX/Y`, `UseTranslation*` overrides, `CustomizeLayerPaint`, `SkipRenderingOutOfBounds`. |
|
|
16
16
|
| `Left` / `Top` | ported (as a translate) | Same paint-time offset in points, but folded into the render matrix (a `canvas.translate`, hit-testing follows) for every control, cached or not; C# applies them only when blitting a cache. |
|
|
17
17
|
| `AnimatedFramesRenderer` / `SkiaLottie` / `SkiaGif` | ported | `AutoPlay`, `Repeat`, `SpeedRatio`, `DefaultFrame` (-1 = last), `Start(delay)`/`Stop`/`Seek`, `IsPlaying`, `Started`/`Finished`, `PlayWhenAvailable`, `GetFrameAt`. Lottie on CanvasKit Skottie (needs the `full` build, +0.9 MB raw wasm): `Source` (URL / app path, JSON cached per source, in-flight dedup), `ColorTint` / `Colors` (C# `ApplyTint` JSON rewrite), `IsOn` + `DefaultFrameWhenOn` + `ApplyIsOnWhenNotPlaying`, `StopAtCurrentFrame`, `ProcessJson`, `GoToStart`/`GoToEnd`, `SetAnimation`, `LoadSource`, `CreateAnimation`, `TotalFrames`; default cache `Operations` (C# ImageDoubleBuffered, see PARITY "Lottie and GIF default caches"). GIF on CanvasKit AnimatedImage: every frame decoded once (`GifAnimation`: `SeekFrame`, `GetFrameNumber`, `DurationMs`, `TotalFrames`), `Aspect` (AspectFitFill), alignment, `Success`/`Error`, default cache `None` (C# ImageDoubleBuffered). Not ported: `SkiaGif.Display` inner SkiaImage (frames are drawn directly), custom-control ctor `SkiaGif(display)`, `SkiaLottie` Skottie property/slot editing, sound, `ReloadSource` sync file paths, `Dispose` wiring on unmount (no engine hook from the reconciler yet). |
|
|
18
|
-
| `SkiaBackdrop` | ported | `Blur` (5), `Brightness` (gamma), `UseContext`, `BackgroundColor` tint, `HasEffects`, `GetImage`; children drawn first and blurred with the snapshot (C# order); `UseContext` snapshots the surface the pixels end up on (on-screen, or the nearest Image cache surface through its `Origin`), also while an Operations picture is being recorded, since caches record inline; after each paint the ancestors' caches are staled for the next frame (no frame requested) so content that changes beneath without invalidating this branch (an image loading later) is picked up by the next frame. Used by the shell popups/modals (`ShellDefaults.PopupsBackgroundBlur`). Not ported: `CacheSource`, the ghost mode. |
|
|
18
|
+
| `SkiaBackdrop` | ported | `Blur` (5), `Brightness` (gamma), `UseContext`, `BackgroundColor` tint, `HasEffects`, `GetImage`; copies the area it covers through every ancestor transform, drawn back 1:1 (C# 6fd3bae3); inside a picture-cached ancestor it marks it `HostsBackdrop` and that ancestor draws live (C# bcce0739); inside an Image-cached ancestor it copies that cache's surface (no warning: C# reverted 9d6ae911, a valid use when what shows through is inside that cache); children drawn first and blurred with the snapshot (C# order); `UseContext` snapshots the surface the pixels end up on (on-screen, or the nearest Image cache surface through its `Origin`), also while an Operations picture is being recorded, since caches record inline; after each paint the ancestors' caches are staled for the next frame (no frame requested) so content that changes beneath without invalidating this branch (an image loading later) is picked up by the next frame. Used by the shell popups/modals (`ShellDefaults.PopupsBackgroundBlur`). Not ported: `CacheSource`, the ghost mode. |
|
|
19
19
|
| Disposal (`Dispose` / `OnDisposing`) | ported | The React renderer calls `Dispose()` when an element leaves the tree (`detachDeletedInstance`): caches, gradient shaders, running `*ToAsync` animations, overlay effects, the accessibility node, then the children (static and templated pool); `SkiaScroll`/`SnappingLayout` stop their animators, `SkiaSvg` drops its raster, `SkiaLottie`/`SkiaGif` free the Skottie animation / frames; `Canvas.Dispose` disposes the content. Not ported: `IsDisposing` staged disposal, `Disposing` event, `OnWillDisposeWithChildren`. |
|
|
20
20
|
| Hover: `ReceivesHover` (opt-in; on by default in SkiaButton, SkiaSlider, the toggles, SkiaRadioButton, SkiaCarousel, SkiaDrawer), `IsHovered`, `HoverChanged` (turns `ReceivesHover` on, as C# `OnHovered`) | ported | drawnui-cross 6m: every opted-in control under the mouse is hovered; nothing changes while a scroll, carousel or drawer moves its content, one check at the last pointer position when it stops and after an add / remove, a rebind, a hidden hovered control or an instant scroll jump; pointerleave clears at once; touch never hovers (`Canvas.Hover`, a `HoverManager`). Not ported: C# `SetHover`, `CheckHovered` / `OnHover` / `IsPointerOver` (no Pointer gestures are routed), SkiaSpinner / wheel picker (no such controls). |
|
|
21
21
|
| `KeyboardManager` | ported | Window-level keydown / keyup capture listeners like `drawnui-keyboard.js`: `KeyDown` / `KeyUp` with the DOM `event.code` ("KeyA", "ArrowLeft"; C# maps the same codes to `InputKey`), `KeyChar` for printable keys without Ctrl / Alt / Meta, `IsShiftPressed` / `IsControlPressed` / `IsAltPressed`, `AttachToKeyboard` (idempotent, pointerdown pulls window focus inside iframes), `BlurExternalTextInput`, `IsOwnedByElement` / `IsInputMethodKey` (React additions: a handler acting on keys skips keys a focused DOM element or the input method owns, C# 845b26e9); `Subscribe` / `Unsubscribe` instead of C# events. Not ported: `InputKey` enum (codes are strings). |
|
|
@@ -38,7 +38,7 @@ DrawnUi name and semantics; nothing here is a redesign, only an omission.
|
|
|
38
38
|
| Touch feedback: `AnimationTapped="Ripple"` + `TouchEffectColor` + `AnimationTappedSpeed` on any control, `SkiaButton.ApplyEffect="Ripple"` (on Down), `PlayRippleAnimation`, `RippleAnimator`, `ClipEffects`/`CreateClip` | ported | Same numbers as C#: 500ms CubicIn, radius 300pt, opacity 0.20 fading over 1.15x progress. |
|
|
39
39
|
| `Shimmer` touch animation, `ShimmerAnimator`, `ClippedEffectsWith`, `TransformView`, `DelayCallbackMs`, `removePrevious`, `Pause`/`Resume`, `UseInterpolator`, spring/deceleration timing, `AnimateExtensions` (`FadeToAsync`, `TranslateToAsync`, ...) | skipped | |
|
|
40
40
|
| `VisualEffects` + `SkiaEffect` / `IPostRendererEffect` / `ISkiaGestureProcessor` | partial | `VisualEffects` array on every control (assign a new array; effects attach / detach, dispose with the control, `DisableEffects`, `GetEffectMargin` aggregated into the effects margin); post renderers run after the direct paint (C# `DrawDirectInternal`) or instead of the Image cache blit (C# `DrawRenderObject`, the effect samples `CachedImage`); effects with `ProcessGestures` see the parent's gestures first (C# `EffectsGestureProcessors`). Not ported: `IRenderEffect` chain (`ChainEffectResult`, `Draw(ctx, drawControl)`), `IColorEffect` / `IImageEffect` on VisualEffects (`BlurEffect`, `DropShadowEffect`, `OuterGlowEffect`, `ColorPresetEffect`, `AdjustRGBEffect`, `ChainAdjust*`, `Chain*`), `IStateEffect.UpdateState`, `Tag` lookup. |
|
|
41
|
-
| `SkiaShaderEffect` / `ShaderDoubleTexturesEffect` / `ShaderTransitionEffect` / `AnimatedShaderEffect` | ported | CanvasKit `RuntimeEffect` (SkSL): `ShaderSource` (fetched once per url), `ShaderCode`, `ShaderTemplate` (`//script-goes-here`), compiled once per source text, Shadertoy uniforms `iResolution` / `iImageResolution` / `iTime` / `iOffset` / `iMouse` (`TimeSeconds`, `MouseCurrent`, `MouseInitial`), `Uniforms` + `SetUniform` (arrays sized by the next uniform slot, undeclared names skipped like C#), `UseBackground` Always / Once (`AquiredBackground`, `ReleaseFrozenSnapshot`) / Never, `AutoCreateInputTexture`, `UseContext`, `BlendMode`, `FilterMode` / `MipmapMode` / `TileMode`, `OnCompilationError` (logged when unset), `LoadedCode`, `IsCompiled`; texture children by declaration order (`uniform shader` names parsed, missing ones sample transparent); double textures: `ControlFrom` / `ControlTo` (their Image caches), `PrimarySource` / `SecondarySource` (resized to the parent box); transition: `Progress`, `ratio`, embedded gl-transitions `DefaultTemplate`; animated: `Play` / `Stop` / `Completed`, `DurationMs`, `Center` -> `iCenter`. `iOrigin` is not written (C# neither). Not ported: `SkiaShader` engine class as a standalone (`DrawRect` / `DrawImage` helpers), `CompareTo` / equality. |
|
|
41
|
+
| `SkiaShaderEffect` / `ShaderDoubleTexturesEffect` / `ShaderTransitionEffect` / `AnimatedShaderEffect` | ported | `ShouldInvalidateParentCache` (default false) + `UpdateUniforms()` (C# 0c6d9ef1: uniform changes and animation ticks keep the parent's cache unless asked to invalidate it). On a cached control the shader replaces it and reads its content (a picture cache hands effects its picture drawn into an image once per cache, C# caa1c8c1 `GetPictureImage`); on an uncached one it draws over the control and reads the canvas. CanvasKit `RuntimeEffect` (SkSL): `ShaderSource` (fetched once per url), `ShaderCode`, `ShaderTemplate` (`//script-goes-here`), compiled once per source text, Shadertoy uniforms `iResolution` / `iImageResolution` / `iTime` / `iOffset` / `iMouse` (`TimeSeconds`, `MouseCurrent`, `MouseInitial`), `Uniforms` + `SetUniform` (arrays sized by the next uniform slot, undeclared names skipped like C#), `UseBackground` Always / Once (`AquiredBackground`, `ReleaseFrozenSnapshot`) / Never, `AutoCreateInputTexture`, `UseContext`, `BlendMode`, `FilterMode` / `MipmapMode` / `TileMode`, `OnCompilationError` (logged when unset), `LoadedCode`, `IsCompiled`; texture children by declaration order (`uniform shader` names parsed, missing ones sample transparent); double textures: `ControlFrom` / `ControlTo` (their Image caches), `PrimarySource` / `SecondarySource` (resized to the parent box); transition: `Progress`, `ratio`, embedded gl-transitions `DefaultTemplate`; animated: `Play` / `Stop` / `Completed`, `DurationMs`, `Center` -> `iCenter`. `iOrigin` is not written (C# neither). Not ported: `SkiaShader` engine class as a standalone (`DrawRect` / `DrawImage` helpers), `CompareTo` / equality. |
|
|
42
42
|
| Multithreading / offscreen rendering | skipped | Browser main thread only. |
|
|
43
43
|
| Hot reload hook | skipped | Vite HMR reloads the page. |
|
|
44
44
|
| Second measure pass / fill-in-auto re-measure rules | skipped | Column/Row give children an infinite main axis; cross axis = available. |
|
|
@@ -4,8 +4,10 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
4
4
|
/**
|
|
5
5
|
* Mirrors DrawnUi SkiaBackdrop: draws what is already painted beneath its box through `Blur` (points, default 5)
|
|
6
6
|
* and `Brightness` (gamma, 1 = unchanged), tinted by `BackgroundColor`; children draw first and are blurred too.
|
|
7
|
-
* The snapshot comes from the surface being drawn (`UseContext
|
|
8
|
-
*
|
|
7
|
+
* The snapshot comes from the surface being drawn (`UseContext`, the area it covers there through every ancestor
|
|
8
|
+
* transform) or the on-screen surface. Not cached (re-blurs on every frame it is drawn in); picture-cached ancestors
|
|
9
|
+
* draw live once it is inside them (HostsBackdrop); inside an Image-cached ancestor it copies that cache's surface
|
|
10
|
+
* (right when what shows through the glass is inside that cache, wrong when it should show content outside it).
|
|
9
11
|
*/
|
|
10
12
|
export declare class SkiaBackdrop extends SkiaLayout {
|
|
11
13
|
/** Samples what is drawn below: a composite above redraws it whole when something inside changes (drawnui-cross 6m). */
|
|
@@ -23,5 +25,7 @@ export declare class SkiaBackdrop extends SkiaLayout {
|
|
|
23
25
|
protected PaintBackground(_ctx: DrawingContext): void;
|
|
24
26
|
protected PaintsBackgroundWithoutColor(): boolean;
|
|
25
27
|
protected Paint(ctx: DrawingContext): void;
|
|
28
|
+
/** C# StopPictureCachesAbove: the picture-cached ancestors draw live from the next frame, which this asks for. */
|
|
29
|
+
protected StopPictureCachesAbove(): void;
|
|
26
30
|
protected OnDisposing(): void;
|
|
27
31
|
}
|
|
@@ -4,8 +4,10 @@ import { SkiaLayout } from "./SkiaLayout";
|
|
|
4
4
|
/**
|
|
5
5
|
* Mirrors DrawnUi SkiaBackdrop: draws what is already painted beneath its box through `Blur` (points, default 5)
|
|
6
6
|
* and `Brightness` (gamma, 1 = unchanged), tinted by `BackgroundColor`; children draw first and are blurred too.
|
|
7
|
-
* The snapshot comes from the surface being drawn (`UseContext
|
|
8
|
-
*
|
|
7
|
+
* The snapshot comes from the surface being drawn (`UseContext`, the area it covers there through every ancestor
|
|
8
|
+
* transform) or the on-screen surface. Not cached (re-blurs on every frame it is drawn in); picture-cached ancestors
|
|
9
|
+
* draw live once it is inside them (HostsBackdrop); inside an Image-cached ancestor it copies that cache's surface
|
|
10
|
+
* (right when what shows through the glass is inside that cache, wrong when it should show content outside it).
|
|
9
11
|
*/
|
|
10
12
|
export class SkiaBackdrop extends SkiaLayout {
|
|
11
13
|
/** Samples what is drawn below: a composite above redraws it whole when something inside changes (drawnui-cross 6m). */
|
|
@@ -40,16 +42,36 @@ export class SkiaBackdrop extends SkiaLayout {
|
|
|
40
42
|
}
|
|
41
43
|
if (!this.HasEffects)
|
|
42
44
|
return;
|
|
45
|
+
// C# StopPictureCachesAbove: a screen copy recorded into an ancestor's picture would be replayed frozen; this frame
|
|
46
|
+
// draws no copy, the picture-cached ancestors draw live from the next frame on (HostsBackdrop)
|
|
47
|
+
if (ctx.Context.Recording) {
|
|
48
|
+
this.StopPictureCachesAbove();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
43
51
|
// C# UseContext: snapshot the surface the pixels end up on — the on-screen surface or the nearest Image cache
|
|
44
|
-
// surface
|
|
45
|
-
//
|
|
52
|
+
// surface. Copied where the backdrop really is on that surface: its rect through the canvas matrix (ancestor
|
|
53
|
+
// scale, move, rotation, 3D, the cache origin), rounded out, clamped to the surface, and drawn back 1:1 with the
|
|
54
|
+
// matrix undone, clipped to the backdrop's own (maybe turned) rect: the blur stays the same on screen while an
|
|
55
|
+
// ancestor scales (C# 6fd3bae3). The global surface (UseContext false) is copied at the layout rect.
|
|
46
56
|
const surface = this.UseContext ? ctx.Context.Surface : this.Superview?.Surface;
|
|
47
57
|
if (!surface)
|
|
48
58
|
return;
|
|
49
59
|
surface.flush();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
let l = Math.floor(d.Left), t = Math.floor(d.Top), r = Math.ceil(d.Right), b = Math.ceil(d.Bottom);
|
|
61
|
+
let device = null;
|
|
62
|
+
if (this.UseContext) {
|
|
63
|
+
const m = canvas.getTotalMatrix();
|
|
64
|
+
const p = CK.Matrix.mapPoints(m, [d.Left, d.Top, d.Right, d.Top, d.Right, d.Bottom, d.Left, d.Bottom]);
|
|
65
|
+
l = Math.max(0, Math.floor(Math.min(p[0], p[2], p[4], p[6])));
|
|
66
|
+
t = Math.max(0, Math.floor(Math.min(p[1], p[3], p[5], p[7])));
|
|
67
|
+
r = Math.min(surface.width(), Math.ceil(Math.max(p[0], p[2], p[4], p[6])));
|
|
68
|
+
b = Math.min(surface.height(), Math.ceil(Math.max(p[1], p[3], p[5], p[7])));
|
|
69
|
+
if (r <= l || b <= t)
|
|
70
|
+
return;
|
|
71
|
+
device = CK.Matrix.invert(m);
|
|
72
|
+
if (!device)
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
53
75
|
// whole-surface snapshot, sub-rect on draw: a bounded snapshot of a GPU surface is not origin-safe in CanvasKit
|
|
54
76
|
const image = surface.makeImageSnapshot();
|
|
55
77
|
if (!image)
|
|
@@ -63,7 +85,13 @@ export class SkiaBackdrop extends SkiaLayout {
|
|
|
63
85
|
paint.setColorFilter(gamma);
|
|
64
86
|
const saved = canvas.save();
|
|
65
87
|
canvas.clipRect(CK.LTRBRect(d.Left, d.Top, d.Right, d.Bottom), CK.ClipOp.Intersect, true);
|
|
66
|
-
|
|
88
|
+
if (device) {
|
|
89
|
+
canvas.concat(device);
|
|
90
|
+
canvas.drawImageRectOptions(image, CK.LTRBRect(l, t, r, b), CK.LTRBRect(l, t, r, b), CK.FilterMode.Nearest, CK.MipmapMode.None, paint);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
canvas.drawImageRectOptions(image, CK.LTRBRect(l, t, r, b), CK.LTRBRect(d.Left, d.Top, d.Right, d.Bottom), CK.FilterMode.Linear, CK.MipmapMode.None, paint);
|
|
94
|
+
}
|
|
67
95
|
canvas.restoreToCount(saved);
|
|
68
96
|
blur?.delete();
|
|
69
97
|
gamma?.delete();
|
|
@@ -78,5 +106,15 @@ export class SkiaBackdrop extends SkiaLayout {
|
|
|
78
106
|
p = p.Parent;
|
|
79
107
|
} });
|
|
80
108
|
}
|
|
109
|
+
/** C# StopPictureCachesAbove: the picture-cached ancestors draw live from the next frame, which this asks for. */
|
|
110
|
+
StopPictureCachesAbove() {
|
|
111
|
+
for (let p = this.Parent; p; p = p.Parent) {
|
|
112
|
+
if (p.UsingCacheType === "Operations") {
|
|
113
|
+
p.HostsBackdrop = true;
|
|
114
|
+
p.InvalidateCache();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
queueMicrotask(() => this.Update());
|
|
118
|
+
}
|
|
81
119
|
OnDisposing() { this.snapshot?.delete(); this.snapshot = undefined; }
|
|
82
120
|
}
|
|
@@ -52,6 +52,15 @@ export declare class CachedObject {
|
|
|
52
52
|
private static BlitPaint;
|
|
53
53
|
/** Replays the cache with its top-left moved to (left, top); `smooth` = an image drawn distorted (linear + mipmaps). */
|
|
54
54
|
Draw(canvas: SkCanvas, left: number, top: number, smooth?: boolean): void;
|
|
55
|
+
/**
|
|
56
|
+
* C# GetPictureImage: the picture of a picture cache (Operations, OperationsFull) drawn into an image, made on the
|
|
57
|
+
* first call (on `main`'s GPU context when given) and kept as long as this cache lives. Shader effects read it as
|
|
58
|
+
* their input, so on such a control they work on its own content, as on an Image cache.
|
|
59
|
+
*/
|
|
60
|
+
GetPictureImage(main?: Surface): Image | undefined;
|
|
61
|
+
private pictureImage?;
|
|
62
|
+
/** The picture drawn into an image, once GetPictureImage made it. */
|
|
63
|
+
get PictureImage(): Image | undefined;
|
|
55
64
|
Dispose(): void;
|
|
56
65
|
}
|
|
57
66
|
/**
|
|
@@ -148,6 +157,11 @@ export declare class SkiaControl {
|
|
|
148
157
|
/** What to cache for this subtree; see SkiaCacheType. Layouts default to None, SkiaLabel/SkiaSvg to Operations. */
|
|
149
158
|
UseCache: SkiaCacheType;
|
|
150
159
|
/** The cache type actually applied (None while caching is disabled globally). */
|
|
160
|
+
/**
|
|
161
|
+
* C# HostsBackdrop: a SkiaBackdrop inside asked this picture-cached control to draw live (a picture would replay
|
|
162
|
+
* the backdrop's screen copy frozen). Stays set after the backdrop is removed: the control just keeps drawing live.
|
|
163
|
+
*/
|
|
164
|
+
HostsBackdrop: boolean;
|
|
151
165
|
get UsingCacheType(): SkiaCacheType;
|
|
152
166
|
/** C# IsCacheComposite: the offscreen surface is kept and only the changed children are re-recorded. */
|
|
153
167
|
get IsCacheComposite(): boolean;
|
package/dist/core/SkiaControl.js
CHANGED
|
@@ -53,9 +53,36 @@ export class CachedObject {
|
|
|
53
53
|
canvas.restoreToCount(saved);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* C# GetPictureImage: the picture of a picture cache (Operations, OperationsFull) drawn into an image, made on the
|
|
58
|
+
* first call (on `main`'s GPU context when given) and kept as long as this cache lives. Shader effects read it as
|
|
59
|
+
* their input, so on such a control they work on its own content, as on an Image cache.
|
|
60
|
+
*/
|
|
61
|
+
GetPictureImage(main) {
|
|
62
|
+
if (this.pictureImage || !this.Picture)
|
|
63
|
+
return this.pictureImage;
|
|
64
|
+
const w = Math.ceil(this.Bounds.Width), h = Math.ceil(this.Bounds.Height);
|
|
65
|
+
if (w <= 0 || h <= 0)
|
|
66
|
+
return undefined;
|
|
67
|
+
const surface = main?.makeSurface({ ...main.imageInfo(), width: w, height: h }) ?? Super.CK.MakeSurface(w, h);
|
|
68
|
+
if (!surface)
|
|
69
|
+
return undefined;
|
|
70
|
+
const canvas = surface.getCanvas();
|
|
71
|
+
canvas.clear(Super.CK.TRANSPARENT);
|
|
72
|
+
canvas.translate(-this.Bounds.Left, -this.Bounds.Top);
|
|
73
|
+
canvas.drawPicture(this.Picture);
|
|
74
|
+
this.pictureImage = surface.makeImageSnapshot();
|
|
75
|
+
surface.delete();
|
|
76
|
+
return this.pictureImage;
|
|
77
|
+
}
|
|
78
|
+
pictureImage;
|
|
79
|
+
/** The picture drawn into an image, once GetPictureImage made it. */
|
|
80
|
+
get PictureImage() { return this.pictureImage; }
|
|
56
81
|
Dispose() {
|
|
57
82
|
this.Picture?.delete();
|
|
58
83
|
this.Image?.delete();
|
|
84
|
+
this.pictureImage?.delete();
|
|
85
|
+
this.pictureImage = undefined;
|
|
59
86
|
}
|
|
60
87
|
}
|
|
61
88
|
/**
|
|
@@ -168,13 +195,18 @@ export class SkiaControl {
|
|
|
168
195
|
/** What to cache for this subtree; see SkiaCacheType. Layouts default to None, SkiaLabel/SkiaSvg to Operations. */
|
|
169
196
|
UseCache = "None";
|
|
170
197
|
/** The cache type actually applied (None while caching is disabled globally). */
|
|
198
|
+
/**
|
|
199
|
+
* C# HostsBackdrop: a SkiaBackdrop inside asked this picture-cached control to draw live (a picture would replay
|
|
200
|
+
* the backdrop's screen copy frozen). Stays set after the backdrop is removed: the control just keeps drawing live.
|
|
201
|
+
*/
|
|
202
|
+
HostsBackdrop = false;
|
|
171
203
|
get UsingCacheType() {
|
|
172
204
|
if (!Super.CacheEnabled)
|
|
173
205
|
return "None";
|
|
174
206
|
switch (this.UseCache) {
|
|
175
207
|
case "None": return "None";
|
|
176
208
|
case "Operations":
|
|
177
|
-
case "OperationsFull": return "Operations";
|
|
209
|
+
case "OperationsFull": return this.HostsBackdrop ? "None" : "Operations";
|
|
178
210
|
case "ImageDoubleBuffered": return "ImageDoubleBuffered";
|
|
179
211
|
case "ImageComposite":
|
|
180
212
|
case "ImageCompositeGPU": return "ImageComposite";
|
|
@@ -358,10 +390,19 @@ export class SkiaControl {
|
|
|
358
390
|
*/
|
|
359
391
|
get CachedImage() {
|
|
360
392
|
const c = this.RenderObject;
|
|
361
|
-
if (!c
|
|
393
|
+
if (!c)
|
|
394
|
+
return undefined;
|
|
395
|
+
if (c.Image) {
|
|
396
|
+
const r = this.AlignedCacheRect(c.Scale);
|
|
397
|
+
return { Image: c.Image, Bounds: SKRect.Create(r.Left, r.Top, c.Bounds.Width, c.Bounds.Height) };
|
|
398
|
+
}
|
|
399
|
+
// a picture cache: its picture drawn into an image (made in RenderContent), so effects work on this control's
|
|
400
|
+
// content instead of a snapshot of what is behind it (C# caa1c8c1)
|
|
401
|
+
const image = c.PictureImage;
|
|
402
|
+
if (!image)
|
|
362
403
|
return undefined;
|
|
363
|
-
const r = this.
|
|
364
|
-
return { Image:
|
|
404
|
+
const r = this.ExpandedCacheRect(c.Scale);
|
|
405
|
+
return { Image: image, Bounds: SKRect.Create(r.Left, r.Top, image.width(), image.height()) };
|
|
365
406
|
}
|
|
366
407
|
/** Clip overlay effects to the control's shape (CreateClip). */
|
|
367
408
|
ClipEffects = true;
|
|
@@ -883,10 +924,14 @@ export class SkiaControl {
|
|
|
883
924
|
if (this.CacheStale(ctx.Scale) || (o?.Picture && o.HasImageBlits && o.PlaybackDistorted !== this.WillDrawDistorted(ctx)))
|
|
884
925
|
this.CreateRenderingObject({ ...ctx, Destination: this.DrawingRect }, cacheType);
|
|
885
926
|
if (this.RenderObject) {
|
|
886
|
-
// C# DrawRenderObject: with post renderers
|
|
887
|
-
//
|
|
888
|
-
|
|
889
|
-
|
|
927
|
+
// C# DrawRenderObject: with post renderers a cache is not blitted, the effects sample it (CachedImage) and paint
|
|
928
|
+
// the result, so the shader replaces the control; a picture cache hands them its picture drawn into an image
|
|
929
|
+
// (made once per cache, C# GetPictureImage); drawn as before only when that image could not be made
|
|
930
|
+
const cache = this.RenderObject;
|
|
931
|
+
if (post.length > 0 && cache.Picture)
|
|
932
|
+
cache.GetPictureImage(ctx.Context.Surface);
|
|
933
|
+
if (post.length === 0 || !(cache.Image ?? cache.PictureImage))
|
|
934
|
+
this.DrawCache(cache, ctx, r.Left + dx, r.Top + dy);
|
|
890
935
|
for (const e of post)
|
|
891
936
|
e.Render(own);
|
|
892
937
|
}
|
|
@@ -119,6 +119,16 @@ export declare class SkiaShaderEffect extends SkiaEffect implements IPostRendere
|
|
|
119
119
|
get NeedApply(): boolean;
|
|
120
120
|
/** Loads (async, once) and compiles (sync) when needed; true when a compiled effect exists. */
|
|
121
121
|
EnsureCompiled(): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* C# ShouldInvalidateParentCache (default false): animation ticks, `SetUniform` and `UpdateUniforms()` only redraw
|
|
124
|
+
* this effect over the parent's cache (ancestor caches still redraw), because the effect paints over the parent's
|
|
125
|
+
* cached content and a uniform change needs no new cache. True: they also invalidate the parent's cache, so the parent
|
|
126
|
+
* re-renders its content on every change; set it when that content refreshes only through the effect's redraws (a
|
|
127
|
+
* custom Paint reading changing data without calling Update()). `Update()` always invalidates the cache.
|
|
128
|
+
*/
|
|
129
|
+
ShouldInvalidateParentCache: boolean;
|
|
130
|
+
/** Call after changing a value only the shader reads (a uniform, an animation progress): redraws the effect over the parent's cache, or `Update()` when ShouldInvalidateParentCache is true. */
|
|
131
|
+
UpdateUniforms(): void;
|
|
122
132
|
SetUniform(name: string, ...values: number[]): this;
|
|
123
133
|
ReleaseFrozenSnapshot(): void;
|
|
124
134
|
private DisposeImage;
|
package/dist/core/SkiaEffect.js
CHANGED
|
@@ -116,11 +116,27 @@ export class SkiaShaderEffect extends SkiaEffect {
|
|
|
116
116
|
}
|
|
117
117
|
return this.compiled !== undefined;
|
|
118
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* C# ShouldInvalidateParentCache (default false): animation ticks, `SetUniform` and `UpdateUniforms()` only redraw
|
|
121
|
+
* this effect over the parent's cache (ancestor caches still redraw), because the effect paints over the parent's
|
|
122
|
+
* cached content and a uniform change needs no new cache. True: they also invalidate the parent's cache, so the parent
|
|
123
|
+
* re-renders its content on every change; set it when that content refreshes only through the effect's redraws (a
|
|
124
|
+
* custom Paint reading changing data without calling Update()). `Update()` always invalidates the cache.
|
|
125
|
+
*/
|
|
126
|
+
ShouldInvalidateParentCache = false;
|
|
127
|
+
/** Call after changing a value only the shader reads (a uniform, an animation progress): redraws the effect over the parent's cache, or `Update()` when ShouldInvalidateParentCache is true. */
|
|
128
|
+
UpdateUniforms() {
|
|
129
|
+
if (this.ShouldInvalidateParentCache) {
|
|
130
|
+
this.Update();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.Parent?.RepaintComposition();
|
|
134
|
+
}
|
|
119
135
|
// ---- uniforms API (C# SetUniform overloads) ----
|
|
120
136
|
SetUniform(name, ...values) {
|
|
121
137
|
if (name && values.length > 0) {
|
|
122
138
|
this.Uniforms.set(name, values);
|
|
123
|
-
this.
|
|
139
|
+
this.UpdateUniforms();
|
|
124
140
|
}
|
|
125
141
|
return this;
|
|
126
142
|
}
|
|
@@ -565,7 +581,7 @@ export class AnimatedShaderEffect extends SkiaShaderEffect {
|
|
|
565
581
|
return;
|
|
566
582
|
if (!this.Animator) {
|
|
567
583
|
const a = new SkiaValueAnimator(parent);
|
|
568
|
-
a.OnUpdated = (v) => { this.Progress = v; this.
|
|
584
|
+
a.OnUpdated = (v) => { this.Progress = v; this.UpdateUniforms(); };
|
|
569
585
|
a.Finished = () => this.Completed?.(this);
|
|
570
586
|
this.Animator = a;
|
|
571
587
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drawnui-react",
|
|
3
|
-
"version": "0.1.0-preview.
|
|
3
|
+
"version": "0.1.0-preview.21",
|
|
4
4
|
"description": "DrawnUi for React: the DrawnUi (.NET) drawn-UI engine in TypeScript on CanvasKit (Skia WASM). Same controls, same PascalCase API as DrawnUi.Net.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nick Kovalsky (AppoMobi)",
|
|
@@ -75,6 +75,10 @@
|
|
|
75
75
|
"check:culling": "node dev/check.mjs dev/check-culling.ts",
|
|
76
76
|
"check:responds": "node dev/check.mjs dev/check-responds.ts",
|
|
77
77
|
"check:3d": "node dev/check.mjs dev/check-3d.ts",
|
|
78
|
+
"check:gesture-owner": "node dev/check.mjs dev/check-gesture-owner.ts",
|
|
79
|
+
"check:shader-cache": "node dev/check.mjs dev/check-shader-cache.ts",
|
|
80
|
+
"check:shader-content": "node dev/check.mjs dev/check-shader-content.ts",
|
|
81
|
+
"check:backdrop": "node dev/check.mjs dev/check-backdrop.ts",
|
|
78
82
|
"site:extras": "node dev/site-extras.mjs dist/demo",
|
|
79
83
|
"build:lib": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
|
|
80
84
|
"prepublishOnly": "npm run build:lib",
|