From d21bb3c5bf66f8a61930cfee130ed2c0d68398b0 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 15 Jul 2026 21:50:44 +0300 Subject: [PATCH 01/13] Fix JavaScript glass tab lens rendering --- .github/workflows/scripts-javascript.yml | 5 + .../impl/html5/BufferedGraphics.java | 4 +- .../codename1/impl/html5/HTML5Graphics.java | 4 +- .../impl/html5/HTML5Implementation.java | 12 +- .../impl/html5/graphics/LensRegion.java | 39 +--- .../graphics/SurfaceCommandRecorder.java | 19 +- scripts/verify-javascript-lens-parity.mjs | 77 +++++++ .../src/javascript/browser_bridge.js | 217 +++++++++++++++--- .../JavascriptTargetIntegrationTest.java | 9 + 9 files changed, 308 insertions(+), 78 deletions(-) create mode 100644 scripts/verify-javascript-lens-parity.mjs diff --git a/.github/workflows/scripts-javascript.yml b/.github/workflows/scripts-javascript.yml index a7156e0ae83..22196585d8b 100644 --- a/.github/workflows/scripts-javascript.yml +++ b/.github/workflows/scripts-javascript.yml @@ -13,6 +13,7 @@ on: - 'scripts/run-javascript-headless-browser.mjs' - 'scripts/run-javascript-lifecycle-tests.mjs' - 'scripts/run-javascript-lifecycle-tests.sh' + - 'scripts/verify-javascript-lens-parity.mjs' - 'scripts/build-javascript-port-hellocodenameone.sh' - 'scripts/javascript_browser_harness.py' - 'scripts/javascript/screenshots/**' @@ -35,6 +36,7 @@ on: - 'scripts/run-javascript-headless-browser.mjs' - 'scripts/run-javascript-lifecycle-tests.mjs' - 'scripts/run-javascript-lifecycle-tests.sh' + - 'scripts/verify-javascript-lens-parity.mjs' - 'scripts/build-javascript-port-hellocodenameone.sh' - 'scripts/javascript_browser_harness.py' - 'scripts/javascript/screenshots/**' @@ -141,6 +143,9 @@ jobs: with: node-version: '20' + - name: Verify JavaScript glass-tab lens parity + run: node scripts/verify-javascript-lens-parity.mjs + - name: Cache npm modules for scripts/ uses: actions/cache@v5 with: diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java index e86b2b9bd28..2217b085e89 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java @@ -183,9 +183,9 @@ public void blurRegion(int x, int y, int width, int height, float radius, float @Override public void lensRegion(int x, int y, int width, int height, float cornerRadius, float magnify, - int tintColor, float tintStrength) { + float aberration, int tintColor, float tintStrength) { addOp(new com.codename1.impl.html5.graphics.LensRegion(x, y, width, height, cornerRadius, - magnify, tintColor, tintStrength)); + magnify, aberration, tintColor, tintStrength)); } @Override diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Graphics.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Graphics.java index 3a01664d4b2..bdd53dbb3da 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Graphics.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Graphics.java @@ -461,9 +461,9 @@ public void blurRegion(int x, int y, int width, int height, float radius, float * {@link com.codename1.impl.html5.graphics.LensRegion}. */ public void lensRegion(int x, int y, int width, int height, float cornerRadius, float magnify, - int tintColor, float tintStrength) { + float aberration, int tintColor, float tintStrength) { dispatchOp(new com.codename1.impl.html5.graphics.LensRegion(x, y, width, height, cornerRadius, - magnify, tintColor, tintStrength)); + magnify, aberration, tintColor, tintStrength)); } public void clearRect(int x, int y, int width, int height) { diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java index 4bc1a656805..ab7ae779c06 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java @@ -6425,10 +6425,14 @@ public boolean glassRegion(Object graphics, int x, int y, int width, int height, @Override public boolean lensRegion(Object graphics, int x, int y, int width, int height, float cornerRadius, float magnify, float aberration, int tintColor, float tintStrength) { - // The iOS-26 tab selection drop: magnify the content bulge + accent - // tint. Chromatic aberration (the iOS Metal shader's extra) is dropped - // on the web; magnify + tint carries the recognisable effect. - g(graphics).lensRegion(x, y, width, height, cornerRadius, magnify, tintColor, tintStrength); + // The host-side canvas bridge runs the same per-pixel lens as JavaSE: + // centre magnification, rim refraction, chromatic aberration, luminance- + // keyed accent tint and the subtle glass highlights/shadows. Keeping the + // complete parameter set here is important -- the old browser fallback + // reduced this to a uniform zoom plus a blue rectangle, which made every + // in-flight frame visibly harsher than the Simulator. + g(graphics).lensRegion(x, y, width, height, cornerRadius, magnify, + aberration, tintColor, tintStrength); return true; } diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/LensRegion.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/LensRegion.java index 93977d56d66..8da4ebec0e0 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/LensRegion.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/LensRegion.java @@ -28,28 +28,28 @@ /** * In-place iOS 26 selection-drop lens for the Tabs glass indicator. Records * {@link SurfaceCommandRecorder#OP_LENS_SELF_REGION}: the host clips to the - * region, magnifies the surface's own pixels there (the content "bulge"), - * then optionally washes them toward the accent tint. + * region and applies the same per-pixel droplet shader as the JavaSE + * Simulator: centre magnification, rim refraction, chromatic aberration, + * luminance-keyed accent tint, saturation and subtle glass lighting. * - *

The native iOS port renders this with a Metal fragment shader that also - * applies chromatic aberration; the web keeps the magnify + tint, which is - * the readable part of the drop. Nothing crosses the worker<->host - * barrier - the magnify uses a self-referential {@code drawImage} during - * host-side command replay.

+ *

Nothing crosses the worker<->host barrier: the worker records the lens + * parameters and the host applies them to the destination canvas during + * ordered command replay.

*/ public class LensRegion implements ExecutableOp { final int x, y, w, h; - final float cornerRadius, magnify, tintStrength; + final float cornerRadius, magnify, aberration, tintStrength; final int tintColor; public LensRegion(int x, int y, int w, int h, float cornerRadius, float magnify, - int tintColor, float tintStrength) { + float aberration, int tintColor, float tintStrength) { this.x = x; this.y = y; this.w = w; this.h = h; this.cornerRadius = cornerRadius; this.magnify = magnify; + this.aberration = aberration; this.tintColor = tintColor; this.tintStrength = tintStrength; } @@ -59,25 +59,8 @@ public void execute(CanvasRenderingContext2D context) { if (!(context instanceof SurfaceCommandRecorder)) { return; } - String tintCss = null; - if (tintStrength > 0) { - int r = (tintColor >> 16) & 0xff; - int g = (tintColor >> 8) & 0xff; - int b = tintColor & 0xff; - // The model's tintStrength assumes the native lens's luminance - // KEYING (only dark glyph pixels drift to accent; the bright frost - // is untouched). On canvas we can't key without a forbidden pixel - // read, so a flat fill at the full strength paints a solid accent - // blob over the whole drop and buries the glyph. Scale it right - // down so the fill reads as a translucent blue CAST over the still- - // visible magnified content instead of covering it. - double a = Math.min(0.30, Math.max(0.0, tintStrength) * 0.32); - tintCss = "rgba(" + r + "," + g + "," + b + "," + a + ")"; - } - // magnify < ~1 would shrink; clamp to at least 1 so the drop only ever - // bulges (the model can pass values slightly under 1 at rest). - double mag = Math.max(1.0, magnify); - ((SurfaceCommandRecorder) context).lensSelfRegion(x, y, w, h, cornerRadius, mag, tintCss); + ((SurfaceCommandRecorder) context).lensSelfRegion(x, y, w, h, cornerRadius, + magnify, aberration, tintColor, tintStrength); } @Override diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/SurfaceCommandRecorder.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/SurfaceCommandRecorder.java index 7eafb9043a1..cfa27586702 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/SurfaceCommandRecorder.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/SurfaceCommandRecorder.java @@ -135,12 +135,10 @@ public final class SurfaceCommandRecorder implements CanvasRenderingContext2D { // side-channel host call would race the flush. public static final int OP_BLUR_SELF_REGION = 80; // 6 nums (x, y, w, h, sigmaPx, cornerRadius) - // In-place iOS 26 selection-DROP lens: MAGNIFY the surface's own pixels in - // the region (content bulge) then optionally wash them toward an accent - // tint. The full chromatic aberration of the native Metal shader is - // dropped on the web; magnify + tint carries the recognisable "drop". - // 6 nums (x, y, w, h, cornerRadius, magnify) + 1 obj (tint css color or - // null when tintStrength<=0). + // In-place iOS 26 selection-DROP lens. The host mirrors JavaSE's per-pixel + // lens, including aberration and luminance-keyed tint. + // 9 nums: x, y, w, h, cornerRadius, magnify, aberration, tintColor, + // tintStrength. public static final int OP_LENS_SELF_REGION = 81; private int[] ops = new int[64]; @@ -296,9 +294,12 @@ public void blurSelfRegion(double x, double y, double w, double h, double sigmaP op(OP_BLUR_SELF_REGION); num(x); num(y); num(w); num(h); num(sigmaPx); num(cornerRadius); } - /** Records an in-place selection-drop lens; see OP_LENS_SELF_REGION. tintCss null = no tint. */ - public void lensSelfRegion(double x, double y, double w, double h, double cornerRadius, double magnify, String tintCss) { - op(OP_LENS_SELF_REGION); num(x); num(y); num(w); num(h); num(cornerRadius); num(magnify); obj(tintCss); + /** Records an in-place selection-drop lens; see OP_LENS_SELF_REGION. */ + public void lensSelfRegion(double x, double y, double w, double h, double cornerRadius, + double magnify, double aberration, int tintColor, double tintStrength) { + op(OP_LENS_SELF_REGION); + num(x); num(y); num(w); num(h); num(cornerRadius); num(magnify); + num(aberration); num(tintColor); num(tintStrength); } @Override public String getFilter() { return "none"; } @Override public void clearRect(double x, double y, double width, double height) { diff --git a/scripts/verify-javascript-lens-parity.mjs b/scripts/verify-javascript-lens-parity.mjs new file mode 100644 index 00000000000..ccf45622188 --- /dev/null +++ b/scripts/verify-javascript-lens-parity.mjs @@ -0,0 +1,77 @@ +import fs from 'node:fs'; +import vm from 'node:vm'; + +// Pixel-parity guard for the iOS-26 glass-tab selection lens. This evaluates +// the implementation that browser_bridge.js actually ships (not a copied test +// implementation) against checksums produced by JavaSEPort.applyLensBuffer() +// for the same deterministic source image at every frozen animation-timing +// checkpoint. A one-channel/one-pixel drift changes the checksum. +const bridgePath = new URL('../vm/ByteCodeTranslator/src/javascript/browser_bridge.js', import.meta.url); +const bridge = fs.readFileSync(bridgePath, 'utf8'); +const start = bridge.indexOf(' var LENS_MAG_FLAT ='); +const end = bridge.indexOf(' // Replay one command stream', start); +if (start < 0 || end < 0) { + throw new Error('Unable to locate the lens implementation in browser_bridge.js'); +} +const sandbox = { Math, Uint8ClampedArray }; +vm.runInNewContext(bridge.substring(start, end) + '\nthis.applyLens = applyLensSelfRegion;', sandbox); + +function crc32(bytes) { + let crc = 0xffffffff; + for (const value of bytes) { + crc ^= value; + for (let bit = 0; bit < 8; bit++) { + crc = (crc >>> 1) ^ ((crc & 1) ? 0xedb88320 : 0); + } + } + return (crc ^ 0xffffffff) >>> 0; +} + +const frames = [ + { progress: 0, width: 80, height: 40, magnify: 1.08, aberration: 0, expected: 0x9d4a06ea }, + { progress: 10, width: 105, height: 54, magnify: 1.1726, aberration: 0.0185, expected: 0x66a714e8 }, + { progress: 25, width: 105, height: 56, magnify: 1.18, aberration: 0.02, expected: 0x25e7edb0 }, + { progress: 50, width: 105, height: 56, magnify: 1.18, aberration: 0.02, expected: 0x25e7edb0 }, + { progress: 75, width: 90, height: 55, magnify: 1.13, aberration: 0.01, expected: 0x471ccf1d }, + { progress: 90, width: 80, height: 42, magnify: 1.08, aberration: 0, expected: 0x0f8060ba }, + { progress: 100, width: 80, height: 40, magnify: 1.08, aberration: 0, expected: 0x9d4a06ea } +]; + +for (const frame of frames) { + const { width, height } = frame; + const source = new Uint8ClampedArray(width * height * 4); + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const offset = (y * width + x) * 4; + source[offset] = (x * 17 + y * 3) & 0xff; + source[offset + 1] = (x * 5 + y * 19) & 0xff; + source[offset + 2] = (x * 11 + y * 7) & 0xff; + source[offset + 3] = 0xff; + } + } + let output; + const context = { + canvas: { width, height }, + getTransform() { return { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }; }, + getImageData() { return { data: new Uint8ClampedArray(source) }; }, + createImageData(w, h) { return { data: new Uint8ClampedArray(w * h * 4) }; }, + putImageData(image) { output = image.data; } + }; + sandbox.applyLens(context, 0, 0, width, height, -1, + frame.magnify, frame.aberration, 0x0a84ff, 1); + + // JavaSE's reference buffer is packed ARGB; Canvas ImageData is RGBA. + const argb = new Uint8Array(width * height * 4); + for (let i = 0; i < width * height; i++) { + argb[i * 4] = output[i * 4 + 3]; + argb[i * 4 + 1] = output[i * 4]; + argb[i * 4 + 2] = output[i * 4 + 1]; + argb[i * 4 + 3] = output[i * 4 + 2]; + } + const actual = crc32(argb); + if (actual !== frame.expected) { + throw new Error(`Lens parity failed at ${frame.progress}%: ` + + `expected ${frame.expected.toString(16)}, got ${actual.toString(16)}`); + } + console.log(`lens parity PASS t=${frame.progress}% size=${width}x${height} crc=${actual.toString(16)}`); +} diff --git a/vm/ByteCodeTranslator/src/javascript/browser_bridge.js b/vm/ByteCodeTranslator/src/javascript/browser_bridge.js index e86883d1df8..6e9a6af8d06 100644 --- a/vm/ByteCodeTranslator/src/javascript/browser_bridge.js +++ b/vm/ByteCodeTranslator/src/javascript/browser_bridge.js @@ -979,6 +979,184 @@ return s; } + // iOS-26 tab selection lens. Keep these values and the math in lock-step + // with JavaSEPort.applyLensBuffer(): the Simulator is the browser renderer's + // pixel reference for the glass-tab animation. + var LENS_MAG_FLAT = 0.75; + var LENS_TINT_HI = 150; + var LENS_TINT_LO = 55; + var LENS_LIFT_COEF = 0.40; + var LENS_GLARE = 0.09; + var LENS_RIM = 0.06; + var LENS_RIM_W = 0.06; + var LENS_REFRACT = 0.16; + var LENS_EDGE_SHADOW = 0.12; + var LENS_RIM_SCALE = 0.84; + var LENS_GLASS_TINT = 0xbcd8ff; + var LENS_GLASS_TINT_STR = 0.10; + var LENS_SAT_BOOST = 1.32; + + function lensSmoothstep(a, b, x) { + var t = (x - a) / (b - a); + t = t < 0 ? 0 : (t > 1 ? 1 : t); + return t * t * (3 - 2 * t); + } + + function lensBilinear(a, b, c, d, tx, ty) { + var top = a + (b - a) * tx; + var bottom = c + (d - c) * tx; + return (top + (bottom - top) * ty) | 0; + } + + function lensSample(data, width, height, fx, fy, channel) { + if (fx < 0) { fx = 0; } else if (fx > width - 1) { fx = width - 1; } + if (fy < 0) { fy = 0; } else if (fy > height - 1) { fy = height - 1; } + var x0 = fx | 0, y0 = fy | 0; + var x1 = Math.min(x0 + 1, width - 1), y1 = Math.min(y0 + 1, height - 1); + var tx = fx - x0, ty = fy - y0; + var row0 = y0 * width, row1 = y1 * width; + return lensBilinear( + data[(row0 + x0) * 4 + channel], data[(row0 + x1) * 4 + channel], + data[(row1 + x0) * 4 + channel], data[(row1 + x1) * 4 + channel], + tx, ty + ); + } + + function lensDeviceRect(ctx, x, y, width, height) { + var tr = ctx.getTransform ? ctx.getTransform() : null; + if (!tr) { + return { x: Math.round(x), y: Math.round(y), w: Math.round(width), h: Math.round(height), scale: 1 }; + } + // Tabs paint under translation/uniform scaling only. A pixel read ignores + // Canvas transforms, so resolve that axis-aligned transform explicitly. + // A rotated/sheared lens is outside the v1 contract and safely no-ops. + if (Math.abs(tr.b) > 1e-9 || Math.abs(tr.c) > 1e-9) { + return null; + } + var x0 = tr.a * x + tr.e, x1 = tr.a * (x + width) + tr.e; + var y0 = tr.d * y + tr.f, y1 = tr.d * (y + height) + tr.f; + return { + x: Math.round(Math.min(x0, x1)), + y: Math.round(Math.min(y0, y1)), + w: Math.round(Math.abs(x1 - x0)), + h: Math.round(Math.abs(y1 - y0)), + scale: Math.min(Math.abs(tr.a), Math.abs(tr.d)) + }; + } + + function applyLensSelfRegion(ctx, x, y, width, height, cornerRadius, + magnify, aberration, tintColor, tintStrength) { + if (!ctx.canvas || width <= 0 || height <= 0) { + return; + } + var rect = lensDeviceRect(ctx, x, y, width, height); + if (!rect || rect.w <= 0 || rect.h <= 0) { + return; + } + var rx = rect.x, ry = rect.y, rw = rect.w, rh = rect.h; + var canvasWidth = ctx.canvas.width | 0, canvasHeight = ctx.canvas.height | 0; + if (rx < 0) { rw += rx; rx = 0; } + if (ry < 0) { rh += ry; ry = 0; } + if (rx + rw > canvasWidth) { rw = canvasWidth - rx; } + if (ry + rh > canvasHeight) { rh = canvasHeight - ry; } + if (rw <= 0 || rh <= 0) { + return; + } + + var source = ctx.getImageData(rx, ry, rw, rh); + var src = source.data; + var result = ctx.createImageData(rw, rh); + var out = result.data; + var hw = rw / 2.0, hh = rh / 2.0; + var scaledCorner = cornerRadius * rect.scale; + var radius = scaledCorner < 0 ? Math.min(hw, hh) + : Math.min(scaledCorner, Math.min(hw, hh)); + if (radius < 0) { radius = 0; } + var tr = (tintColor >> 16) & 0xff; + var tg = (tintColor >> 8) & 0xff; + var tb = tintColor & 0xff; + var liftMax = LENS_LIFT_COEF * (magnify - 1.0) * hh; + var glassAmount = lensSmoothstep(1.085, 1.25, magnify); + + for (var yy = 0; yy < rh; yy++) { + var py = yy + 0.5 - hh; + for (var xx = 0; xx < rw; xx++) { + var px = xx + 0.5 - hw; + var index = (yy * rw + xx) * 4; + var dxe = Math.abs(px) - (hw - radius); + var dye = Math.abs(py) - (hh - radius); + var axx = Math.max(dxe, 0), ayy = Math.max(dye, 0); + var outside = Math.sqrt(axx * axx + ayy * ayy); + var inside = Math.min(Math.max(dxe, dye), 0); + var depth = -(outside + inside - radius); + if (depth <= 0) { + out[index] = src[index]; + out[index + 1] = src[index + 1]; + out[index + 2] = src[index + 2]; + out[index + 3] = src[index + 3]; + continue; + } + + var alpha = Math.min(depth, 1.0); + var rd = Math.min(1.0, Math.sqrt((px * px) / (hw * hw) + (py * py) / (hh * hh))); + var edge = lensSmoothstep(LENS_MAG_FLAT, 1.0, rd); + var rimScale = 1.0 + (LENS_RIM_SCALE - 1.0) * glassAmount; + var mag = magnify + (rimScale - magnify) * edge; + if (mag < 0.2) { mag = 0.2; } + var abr = aberration * edge; + var magR = mag * (1 - abr), magB = mag * (1 + abr); + if (magR < 0.05) { magR = 0.05; } + if (magB < 0.05) { magB = 0.05; } + var lift = liftMax * (1 - rd * rd); + var refract = 1.0 + LENS_REFRACT * glassAmount * lensSmoothstep(0.70, 1.0, rd); + var sampleYR = hh + (py / magR) * refract + lift; + var sampleYG = hh + (py / mag) * refract + lift; + var sampleYB = hh + (py / magB) * refract + lift; + var sr = lensSample(src, rw, rh, hw + (px / magR) * refract, sampleYR, 0); + var sg = lensSample(src, rw, rh, hw + (px / mag) * refract, sampleYG, 1); + var sb = lensSample(src, rw, rh, hw + (px / magB) * refract, sampleYB, 2); + var lum = 0.2126 * sr + 0.7152 * sg + 0.0722 * sb; + var tint = tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + var fr = sr + (tr - sr) * tint; + var fg = sg + (tg - sg) * tint; + var fb = sb + (tb - sb) * tint; + var glassTint = LENS_GLASS_TINT_STR * glassAmount; + fr += (((LENS_GLASS_TINT >> 16) & 0xff) - fr) * glassTint; + fg += (((LENS_GLASS_TINT >> 8) & 0xff) - fg) * glassTint; + fb += ((LENS_GLASS_TINT & 0xff) - fb) * glassTint; + var saturationLum = 0.2126 * fr + 0.7152 * fg + 0.0722 * fb; + fr = saturationLum + (fr - saturationLum) * LENS_SAT_BOOST; + fg = saturationLum + (fg - saturationLum) * LENS_SAT_BOOST; + fb = saturationLum + (fb - saturationLum) * LENS_SAT_BOOST; + var gx = px / hw, gy = (py + 0.42 * hh) / hh; + var glare = LENS_GLARE * glassAmount * Math.exp(-(gx * gx * 1.15 + gy * gy * 2.6) * 2.1); + var rimWidth = Math.max(2.0, LENS_RIM_W * hh); + var rim = depth < rimWidth ? (1.0 - depth / rimWidth) * LENS_RIM : 0; + var bright = glare + rim; + if (bright > 0) { + fr += bright * (255 - fr); + fg += bright * (255 - fg); + fb += bright * (255 - fb); + } + var edgeShadowWidth = Math.max(2.0, 0.13 * Math.min(hw, hh)); + if (depth < edgeShadowWidth) { + var edgeShadow = (1.0 - depth / edgeShadowWidth) * LENS_EDGE_SHADOW * glassAmount; + fr *= 1 - edgeShadow; + fg *= 1 - edgeShadow; + fb *= 1 - edgeShadow; + } + fr = fr < 0 ? 0 : (fr > 255 ? 255 : fr | 0); + fg = fg < 0 ? 0 : (fg > 255 ? 255 : fg | 0); + fb = fb < 0 ? 0 : (fb > 255 ? 255 : fb | 0); + out[index] = (src[index] + (fr - src[index]) * alpha) | 0; + out[index + 1] = (src[index + 1] + (fg - src[index + 1]) * alpha) | 0; + out[index + 2] = (src[index + 2] + (fb - src[index + 2]) * alpha) | 0; + out[index + 3] = src[index + 3]; + } + } + ctx.putImageData(result, rx, ry); + } + // Replay one command stream (opcodes + nums + objs) onto ``ctx``. function replaySurfaceCommands(ctx, ops, opCount, nums, objs) { var ni = 0; // num cursor @@ -1122,44 +1300,17 @@ break; } case SURF.LENS_SELF_REGION: { - // iOS-26 selection DROP: magnify this surface's own pixels in the - // region (content bulge), then optionally wash them toward an accent - // tint. drawImage(self) snapshots the source bitmap per the HTML - // spec. cornerRadius: 0 = rect, -1 = capsule, >0 = rounded px. + // iOS-26 selection DROP: run the Simulator-equivalent per-pixel lens + // over this surface's own pixels. cornerRadius: 0 = rect, -1 = + // capsule, >0 = rounded px. var _lx = nums[ni++], _ly = nums[ni++], _lw = nums[ni++], _lh = nums[ni++]; var _lcr = nums[ni++], _lmag = nums[ni++]; - var _ltint = objs[oi++]; + var _lab = nums[ni++], _ltint = nums[ni++] | 0, _ltintStrength = nums[ni++]; if (_lw > 0 && _lh > 0 && ctx.canvas) { try { - ctx.save(); - ctx.beginPath(); - if (_lcr) { - var _lrr = _lcr < 0 ? Math.min(_lw, _lh) / 2 - : Math.min(_lcr, Math.min(_lw, _lh) / 2); - ctx.moveTo(_lx + _lrr, _ly); - ctx.arcTo(_lx + _lw, _ly, _lx + _lw, _ly + _lh, _lrr); - ctx.arcTo(_lx + _lw, _ly + _lh, _lx, _ly + _lh, _lrr); - ctx.arcTo(_lx, _ly + _lh, _lx, _ly, _lrr); - ctx.arcTo(_lx, _ly, _lx + _lw, _ly, _lrr); - ctx.closePath(); - } else { - ctx.rect(_lx, _ly, _lw, _lh); - } - ctx.clip(); - // Magnify: draw the region's own pixels scaled about its centre. - // Source rect = region shrunk by 1/mag around centre; dest = the - // full region -> the content bulges outward like the native lens. - var _lm = _lmag > 1 ? _lmag : 1; - var _lsw = _lw / _lm, _lsh = _lh / _lm; - var _lsx = _lx + (_lw - _lsw) / 2, _lsy = _ly + (_lh - _lsh) / 2; - ctx.drawImage(ctx.canvas, _lsx, _lsy, _lsw, _lsh, _lx, _ly, _lw, _lh); - if (_ltint) { - ctx.fillStyle = _ltint; - ctx.fillRect(_lx, _ly, _lw, _lh); - } + applyLensSelfRegion(ctx, _lx, _ly, _lw, _lh, _lcr, + _lmag, _lab, _ltint, _ltintStrength); } catch (_elr) { - } finally { - try { ctx.restore(); } catch (_elr2) {} } } break; diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java index 462e7dbfbb8..2b9bb1ee85f 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java @@ -14,6 +14,7 @@ import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class JavascriptTargetIntegrationTest { @@ -90,6 +91,14 @@ void generatesBrowserBundleForJavascriptTarget(CompilerHelper.CompilerConfig con "Generated host page should use the JavaScript port browser shell assets"); assertTrue(browserBridge.contains("cn1HostBridge") && browserBridge.contains("host-callback") && browserBridge.contains("host-call"), "Browser bridge should expose host-call plumbing for the JavaScript port shell"); + assertTrue(browserBridge.contains("function applyLensSelfRegion") + && browserBridge.contains("ctx.getImageData(rx, ry, rw, rh)") + && browserBridge.contains("LENS_GLASS_TINT_STR") + && browserBridge.contains("_lab = nums[ni++]") + && browserBridge.contains("_ltintStrength = nums[ni++]"), + "Browser bridge should ship the Simulator-equivalent per-pixel glass-tab lens"); + assertFalse(browserBridge.contains("var _lsw = _lw / _lm"), + "Glass-tab lens must not regress to the coarse uniform-zoom fallback"); assertTrue(protocolDoc.contains("Version: 1") && protocolDoc.contains("host-call") && protocolDoc.contains("host-callback") From 4e978bc4c5fbe525c46bf908ec1c9b78cd83d300 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 16 Jul 2026 04:45:11 +0300 Subject: [PATCH 02/13] Match JavaScript glass material to native --- .../impl/html5/BufferedGraphics.java | 7 + .../codename1/impl/html5/HTML5Graphics.java | 10 + .../impl/html5/HTML5Implementation.java | 11 +- .../impl/html5/graphics/GlassRegion.java | 66 ++++ .../graphics/SurfaceCommandRecorder.java | 14 + scripts/verify-javascript-lens-parity.mjs | 67 +++- .../src/javascript/browser_bridge.js | 306 ++++++++++++++++-- .../JavascriptTargetIntegrationTest.java | 30 ++ 8 files changed, 474 insertions(+), 37 deletions(-) create mode 100644 Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/GlassRegion.java diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java index 2217b085e89..c2b77958776 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java @@ -181,6 +181,13 @@ public void blurRegion(int x, int y, int width, int height, float radius, float addOp(new com.codename1.impl.html5.graphics.BlurRegion(x, y, width, height, radius, cornerRadius)); } + @Override + public void glassRegion(int x, int y, int width, int height, float radius, float cornerRadius, + float saturation, float scale, float offset, float refraction, float specular) { + addOp(new com.codename1.impl.html5.graphics.GlassRegion(x, y, width, height, + radius, cornerRadius, saturation, scale, offset, refraction, specular)); + } + @Override public void lensRegion(int x, int y, int width, int height, float cornerRadius, float magnify, float aberration, int tintColor, float tintStrength) { diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Graphics.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Graphics.java index bdd53dbb3da..67952773511 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Graphics.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Graphics.java @@ -456,6 +456,16 @@ public void blurRegion(int x, int y, int width, int height, float radius, float dispatchOp(new com.codename1.impl.html5.graphics.BlurRegion(x, y, width, height, radius, cornerRadius)); } + /** + * In-place Liquid Glass material; see + * {@link com.codename1.impl.html5.graphics.GlassRegion}. + */ + public void glassRegion(int x, int y, int width, int height, float radius, float cornerRadius, + float saturation, float scale, float offset, float refraction, float specular) { + dispatchOp(new com.codename1.impl.html5.graphics.GlassRegion(x, y, width, height, + radius, cornerRadius, saturation, scale, offset, refraction, specular)); + } + /** * In-place selection-drop lens (magnify + tint); see * {@link com.codename1.impl.html5.graphics.LensRegion}. diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java index ab7ae779c06..789b7a3679e 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/HTML5Implementation.java @@ -6414,11 +6414,12 @@ public boolean blurRegion(Object graphics, int x, int y, int width, int height, @Override public boolean glassRegion(Object graphics, int x, int y, int width, int height, float radius, float cornerRadius, float sat, float scale, float offset, float refract, float specular) { - // Plain backdrop blur clipped to the glass shape. The full Liquid Glass - // colour transform (saturation/scale/offset/refraction/specular) is the - // iOS Metal port's material; the browser gets the honest frost, which - // is what backdrop-filter delivers on the web anyway. - g(graphics).blurRegion(x, y, width, height, radius, cornerRadius); + // Preserve the complete named GlassRecipe. The host bridge applies the + // same affine colour material and rounded-shape optics as iOS around + // the browser's native Gaussian blur. Dropping these parameters made + // the tab-bar pill far more transparent than native. + g(graphics).glassRegion(x, y, width, height, radius, cornerRadius, + sat, scale, offset, refract, specular); return true; } diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/GlassRegion.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/GlassRegion.java new file mode 100644 index 00000000000..9b0aea225d0 --- /dev/null +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/GlassRegion.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + +package com.codename1.impl.html5.graphics; + +import com.codename1.html5.js.canvas.CanvasRenderingContext2D; + +/** + * Records a complete named Liquid Glass material for ordered host-side replay. + * The host applies the native affine colour transform, browser Gaussian blur, + * rounded shape mask, edge refraction and specular rim to the surface's own + * already-painted pixels. + */ +public final class GlassRegion implements ExecutableOp { + private final int x, y, width, height; + private final float radius, cornerRadius, saturation, scale, offset, refraction, specular; + + public GlassRegion(int x, int y, int width, int height, float radius, float cornerRadius, + float saturation, float scale, float offset, float refraction, float specular) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.radius = radius; + this.cornerRadius = cornerRadius; + this.saturation = saturation; + this.scale = scale; + this.offset = offset; + this.refraction = refraction; + this.specular = specular; + } + + @Override + public void execute(CanvasRenderingContext2D context) { + if (!(context instanceof SurfaceCommandRecorder)) { + return; + } + ((SurfaceCommandRecorder) context).glassSelfRegion(x, y, width, height, + radius, cornerRadius, saturation, scale, offset, refraction, specular); + } + + @Override + public String getDescription() { + return "GlassRegion"; + } +} diff --git a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/SurfaceCommandRecorder.java b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/SurfaceCommandRecorder.java index cfa27586702..365eb3e35a7 100644 --- a/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/SurfaceCommandRecorder.java +++ b/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/graphics/SurfaceCommandRecorder.java @@ -141,6 +141,11 @@ public final class SurfaceCommandRecorder implements CanvasRenderingContext2D { // tintStrength. public static final int OP_LENS_SELF_REGION = 81; + // Complete named Liquid Glass material: affine colour transform + blur + + // rounded shape optics. 11 nums: x, y, w, h, blurRadius, cornerRadius, + // saturation, scale, offset, refraction, specular. + public static final int OP_GLASS_SELF_REGION = 82; + private int[] ops = new int[64]; private int opCount; private double[] nums = new double[256]; @@ -301,6 +306,15 @@ public void lensSelfRegion(double x, double y, double w, double h, double corner num(x); num(y); num(w); num(h); num(cornerRadius); num(magnify); num(aberration); num(tintColor); num(tintStrength); } + + /** Records a complete in-place Liquid Glass material; see OP_GLASS_SELF_REGION. */ + public void glassSelfRegion(double x, double y, double w, double h, double blurRadius, + double cornerRadius, double saturation, double scale, double offset, + double refraction, double specular) { + op(OP_GLASS_SELF_REGION); + num(x); num(y); num(w); num(h); num(blurRadius); num(cornerRadius); + num(saturation); num(scale); num(offset); num(refraction); num(specular); + } @Override public String getFilter() { return "none"; } @Override public void clearRect(double x, double y, double width, double height) { // Cull provable no-ops: a zero/negative-area rect paints nothing, so do diff --git a/scripts/verify-javascript-lens-parity.mjs b/scripts/verify-javascript-lens-parity.mjs index ccf45622188..41fc4a218cb 100644 --- a/scripts/verify-javascript-lens-parity.mjs +++ b/scripts/verify-javascript-lens-parity.mjs @@ -1,11 +1,11 @@ import fs from 'node:fs'; import vm from 'node:vm'; -// Pixel-parity guard for the iOS-26 glass-tab selection lens. This evaluates +// Pixel-parity guard for the complete iOS-26 glass-tab effect. This evaluates // the implementation that browser_bridge.js actually ships (not a copied test // implementation) against checksums produced by JavaSEPort.applyLensBuffer() -// for the same deterministic source image at every frozen animation-timing -// checkpoint. A one-channel/one-pixel drift changes the checksum. +// and IOSImplementation's material/optics routines. A one-channel/one-pixel +// drift changes the checksum. const bridgePath = new URL('../vm/ByteCodeTranslator/src/javascript/browser_bridge.js', import.meta.url); const bridge = fs.readFileSync(bridgePath, 'utf8'); const start = bridge.indexOf(' var LENS_MAG_FLAT ='); @@ -14,7 +14,10 @@ if (start < 0 || end < 0) { throw new Error('Unable to locate the lens implementation in browser_bridge.js'); } const sandbox = { Math, Uint8ClampedArray }; -vm.runInNewContext(bridge.substring(start, end) + '\nthis.applyLens = applyLensSelfRegion;', sandbox); +vm.runInNewContext(bridge.substring(start, end) + + '\nthis.applyLens = applyLensSelfRegion;' + + '\nthis.applyMaterial = glassMaterialInPlace;' + + '\nthis.applyOptics = applyGlassOptics;', sandbox); function crc32(bytes) { let crc = 0xffffffff; @@ -27,6 +30,31 @@ function crc32(bytes) { return (crc ^ 0xffffffff) >>> 0; } +function rgbaToArgb(rgba) { + const argb = new Uint8Array(rgba.length); + for (let i = 0; i < rgba.length / 4; i++) { + argb[i * 4] = rgba[i * 4 + 3]; + argb[i * 4 + 1] = rgba[i * 4]; + argb[i * 4 + 2] = rgba[i * 4 + 1]; + argb[i * 4 + 3] = rgba[i * 4 + 2]; + } + return argb; +} + +function glassPattern(width, height) { + const source = new Uint8ClampedArray(width * height * 4); + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const offset = (y * width + x) * 4; + source[offset] = (x * 17 + y * 3) & 0xff; + source[offset + 1] = (x * 5 + y * 19) & 0xff; + source[offset + 2] = (x * 11 + y * 7) & 0xff; + source[offset + 3] = (x * 7 + y * 13 + 31) & 0xff; + } + } + return source; +} + const frames = [ { progress: 0, width: 80, height: 40, magnify: 1.08, aberration: 0, expected: 0x9d4a06ea }, { progress: 10, width: 105, height: 54, magnify: 1.1726, aberration: 0.0185, expected: 0x66a714e8 }, @@ -61,17 +89,32 @@ for (const frame of frames) { frame.magnify, frame.aberration, 0x0a84ff, 1); // JavaSE's reference buffer is packed ARGB; Canvas ImageData is RGBA. - const argb = new Uint8Array(width * height * 4); - for (let i = 0; i < width * height; i++) { - argb[i * 4] = output[i * 4 + 3]; - argb[i * 4 + 1] = output[i * 4]; - argb[i * 4 + 2] = output[i * 4 + 1]; - argb[i * 4 + 3] = output[i * 4 + 2]; - } - const actual = crc32(argb); + const actual = crc32(rgbaToArgb(output)); if (actual !== frame.expected) { throw new Error(`Lens parity failed at ${frame.progress}%: ` + `expected ${frame.expected.toString(16)}, got ${actual.toString(16)}`); } console.log(`lens parity PASS t=${frame.progress}% size=${width}x${height} crc=${actual.toString(16)}`); } + +for (const recipe of [ + { name: 'pill-light', saturation: 1.8, scale: 1, offset: 108, expected: 0x5d960bce }, + { name: 'pill-dark', saturation: 2.5, scale: 0.3, offset: 13, expected: 0x836a16cf } +]) { + const material = glassPattern(64, 40); + sandbox.applyMaterial(material, recipe.saturation, recipe.scale, recipe.offset); + const actual = crc32(rgbaToArgb(material)); + if (actual !== recipe.expected) { + throw new Error(`Glass material parity failed for ${recipe.name}: ` + + `expected ${recipe.expected.toString(16)}, got ${actual.toString(16)}`); + } + console.log(`glass material parity PASS recipe=${recipe.name} crc=${actual.toString(16)}`); +} + +const optics = sandbox.applyOptics(glassPattern(52, 32), 52, 32, 6, + 40, 20, -1, 0.4, 0.5); +const opticsCrc = crc32(rgbaToArgb(optics)); +if (opticsCrc !== 0x8057dd7a) { + throw new Error(`Glass optics parity failed: expected 8057dd7a, got ${opticsCrc.toString(16)}`); +} +console.log(`glass optics parity PASS crc=${opticsCrc.toString(16)}`); diff --git a/vm/ByteCodeTranslator/src/javascript/browser_bridge.js b/vm/ByteCodeTranslator/src/javascript/browser_bridge.js index 6e9a6af8d06..ca22ecb6d97 100644 --- a/vm/ByteCodeTranslator/src/javascript/browser_bridge.js +++ b/vm/ByteCodeTranslator/src/javascript/browser_bridge.js @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + (function(global) { function shouldEnableDiag() { if (global.__parparDiagEnabled != null) { @@ -932,7 +955,7 @@ CREATE_PATTERN_SURFACE: 57, DRAW_IMAGE_XY: 60, DRAW_IMAGE_XYWH: 61, DRAW_IMAGE_SRCDST: 62, BLIT_SURFACE_XY: 70, BLIT_SURFACE_XYWH: 71, BLIT_SURFACE_SRCDST: 72, - BLUR_SELF_REGION: 80, LENS_SELF_REGION: 81 + BLUR_SELF_REGION: 80, LENS_SELF_REGION: 81, GLASS_SELF_REGION: 82 }; // The display surface id. Mirrors HTML5Implementation.DISPLAY_SURFACE_ID. var SURF_DISPLAY_ID = 1; @@ -1157,6 +1180,253 @@ ctx.putImageData(result, rx, ry); } + function glassFloatMul(a, b) { + return Math.fround(Math.fround(a) * Math.fround(b)); + } + + function glassFloatAdd(a, b) { + return Math.fround(Math.fround(a) + Math.fround(b)); + } + + // Mirrors IOSImplementation.glassMaterialInPlace(). Math.fround preserves + // the native float evaluation points so the material does not drift by a + // channel value merely because JavaScript normally evaluates as double. + function glassMaterialInPlace(data, saturation, scale, offset) { + var sat = Math.fround(saturation), scl = Math.fround(scale), off = Math.fround(offset); + var lr = Math.fround(0.2126), lg = Math.fround(0.7152), lb = Math.fround(0.0722); + for (var i = 0; i < data.length; i += 4) { + var r = Math.fround(data[i]), g = Math.fround(data[i + 1]), b = Math.fround(data[i + 2]); + var lum = glassFloatAdd(glassFloatAdd(glassFloatMul(lr, r), glassFloatMul(lg, g)), + glassFloatMul(lb, b)); + r = glassFloatAdd(glassFloatMul(glassFloatAdd(lum, + glassFloatMul(glassFloatAdd(r, -lum), sat)), scl), off); + g = glassFloatAdd(glassFloatMul(glassFloatAdd(lum, + glassFloatMul(glassFloatAdd(g, -lum), sat)), scl), off); + b = glassFloatAdd(glassFloatMul(glassFloatAdd(lum, + glassFloatMul(glassFloatAdd(b, -lum), sat)), scl), off); + data[i] = r < 0 ? 0 : (r > 255 ? 255 : r | 0); + data[i + 1] = g < 0 ? 0 : (g > 255 ? 255 : g | 0); + data[i + 2] = b < 0 ? 0 : (b > 255 ? 255 : b | 0); + } + } + + function glassBilinear(a, b, c, d, tx, ty) { + var ftx = Math.fround(tx), fty = Math.fround(ty); + var top = glassFloatAdd(a, glassFloatMul(glassFloatAdd(b, -a), ftx)); + var bottom = glassFloatAdd(c, glassFloatMul(glassFloatAdd(d, -c), ftx)); + return glassFloatAdd(glassFloatAdd(top, + glassFloatMul(glassFloatAdd(bottom, -top), fty)), 0.5) | 0; + } + + function glassSample(data, width, height, fx, fy, channel) { + var x = Math.fround(fx), y = Math.fround(fy); + if (x < 0) { x = 0; } else if (x > width - 1) { x = width - 1; } + if (y < 0) { y = 0; } else if (y > height - 1) { y = height - 1; } + var x0 = x | 0, y0 = y | 0; + var x1 = x0 + 1 < width ? x0 + 1 : x0; + var y1 = y0 + 1 < height ? y0 + 1 : y0; + var tx = Math.fround(x - x0), ty = Math.fround(y - y0); + var row0 = y0 * width, row1 = y1 * width; + return glassBilinear( + data[(row0 + x0) * 4 + channel], data[(row0 + x1) * 4 + channel], + data[(row1 + x0) * 4 + channel], data[(row1 + x1) * 4 + channel], + tx, ty + ); + } + + // Mirrors IOSImplementation.applyGlassOptics(). The returned patch retains + // the native shape alpha and is composited with drawImage below, just like + // IOSImplementation draws its generated ARGB image back onto the target. + function applyGlassOptics(blurred, bufferWidth, bufferHeight, pad, + width, height, cornerRadius, refraction, specular) { + var result = new Uint8ClampedArray(width * height * 4); + var hw = Math.fround(width / 2), hh = Math.fround(height / 2); + var radius = cornerRadius < 0 ? Math.min(hw, hh) + : Math.min(cornerRadius, Math.min(hw, hh)); + if (radius < 0) { radius = 0; } + var band = glassFloatMul(Math.min(hw, hh), Math.fround(0.6)); + var rimWidth = Math.fround(3.0); + var refract = Math.fround(refraction), spec = Math.fround(specular); + for (var yy = 0; yy < height; yy++) { + var py = Math.fround(yy + 0.5); + for (var xx = 0; xx < width; xx++) { + var px = Math.fround(xx + 0.5); + var dx = Math.fround(Math.abs(Math.fround(px - hw)) - Math.fround(hw - radius)); + var dy = Math.fround(Math.abs(Math.fround(py - hh)) - Math.fround(hh - radius)); + var ax = dx > 0 ? dx : 0, ay = dy > 0 ? dy : 0; + var outside = Math.fround(Math.sqrt(glassFloatAdd(glassFloatMul(ax, ax), glassFloatMul(ay, ay)))); + var inside = Math.min(Math.max(dx, dy), 0); + var sdf = glassFloatAdd(glassFloatAdd(outside, inside), -radius); + var depth = Math.fround(-sdf); + if (depth <= 0) { + continue; + } + var coverage = depth >= 1 ? 1 : depth; + var sx = Math.fround(xx), sy = Math.fround(yy); + if (refract > 0 && band > 0 && depth < band) { + var edgeT = Math.fround(1 - Math.fround(depth / band)); + var root = Math.fround(Math.sqrt(Math.max(0, + glassFloatAdd(1, -glassFloatMul(edgeT, edgeT))))); + var distortion = glassFloatAdd(1, -root); + sx = Math.fround(xx - glassFloatMul(glassFloatMul(Math.fround(px - hw), distortion), refract)); + sy = Math.fround(yy - glassFloatMul(glassFloatMul(Math.fround(py - hh), distortion), refract)); + } + var red = glassSample(blurred, bufferWidth, bufferHeight, + Math.fround(sx + pad), Math.fround(sy + pad), 0); + var green = glassSample(blurred, bufferWidth, bufferHeight, + Math.fround(sx + pad), Math.fround(sy + pad), 1); + var blue = glassSample(blurred, bufferWidth, bufferHeight, + Math.fround(sx + pad), Math.fround(sy + pad), 2); + if (spec > 0 && depth < rimWidth) { + var rim = Math.fround(1 - Math.fround(depth / rimWidth)); + var topBias = Math.fround(0.55 + glassFloatMul(0.45, + Math.fround(1 - Math.fround(py / height)))); + var add = glassFloatMul(glassFloatMul(glassFloatMul(spec, rim), topBias), 70) | 0; + red = Math.min(255, red + add); + green = Math.min(255, green + add); + blue = Math.min(255, blue + add); + } + var index = (yy * width + xx) * 4; + result[index] = red; + result[index + 1] = green; + result[index + 2] = blue; + result[index + 3] = glassFloatMul(coverage, 255) | 0; + } + } + return result; + } + + function createGlassScratchCanvas(width, height) { + if (typeof global.OffscreenCanvas === 'function') { + return new global.OffscreenCanvas(width, height); + } + var doc = global.document || (global.window && global.window.document); + if (!doc || !doc.createElement) { + return null; + } + var canvas = doc.createElement('canvas'); + canvas.width = width; + canvas.height = height; + return canvas; + } + + function applyBlurSelfRegion(ctx, x, y, width, height, sigma, cornerRadius) { + if (width <= 0 || height <= 0 || !ctx.canvas) { + return; + } + ctx.save(); + try { + ctx.beginPath(); + if (cornerRadius) { + var round = cornerRadius < 0 ? Math.min(width, height) / 2 + : Math.min(cornerRadius, Math.min(width, height) / 2); + ctx.moveTo(x + round, y); + ctx.arcTo(x + width, y, x + width, y + height, round); + ctx.arcTo(x + width, y + height, x, y + height, round); + ctx.arcTo(x, y + height, x, y, round); + ctx.arcTo(x, y, x + width, y, round); + ctx.closePath(); + } else { + ctx.rect(x, y, width, height); + } + ctx.clip(); + ctx.filter = 'blur(' + sigma + 'px)'; + ctx.drawImage(ctx.canvas, 0, 0); + } finally { + ctx.restore(); + } + } + + function applyGlassSelfRegion(ctx, x, y, width, height, blurRadius, cornerRadius, + saturation, scale, offset, refraction, specular) { + if (!ctx.canvas || width <= 0 || height <= 0) { + return; + } + var rect = lensDeviceRect(ctx, x, y, width, height); + if (!rect || rect.w <= 0 || rect.h <= 0) { + return; + } + var rx = rect.x, ry = rect.y, rw = rect.w, rh = rect.h; + var canvasWidth = ctx.canvas.width | 0, canvasHeight = ctx.canvas.height | 0; + if (rx < 0) { rw += rx; rx = 0; } + if (ry < 0) { rh += ry; ry = 0; } + if (rx + rw > canvasWidth) { rw = canvasWidth - rx; } + if (ry + rh > canvasHeight) { rh = canvasHeight - ry; } + if (rw <= 0 || rh <= 0) { + return; + } + + // CSS blur() accepts Gaussian sigma while the CN1/iOS recipe carries the + // calibrated blur radius. Keep the same radius-to-sigma conversion used + // by BlurRegion; the material transform is the missing opacity, not a + // stronger blur. + var sigma = Math.max(1, blurRadius * rect.scale / 2); + var pad = Math.ceil(sigma) * 3 + 1; + var bufferWidth = rw + pad * 2, bufferHeight = rh + pad * 2; + var ax0 = Math.max(0, rx - pad), ay0 = Math.max(0, ry - pad); + var ax1 = Math.min(canvasWidth, rx + rw + pad), ay1 = Math.min(canvasHeight, ry + rh + pad); + var availableWidth = ax1 - ax0, availableHeight = ay1 - ay0; + if (availableWidth <= 0 || availableHeight <= 0) { + return; + } + var available = ctx.getImageData(ax0, ay0, availableWidth, availableHeight).data; + var padded = new Uint8ClampedArray(bufferWidth * bufferHeight * 4); + for (var by = 0; by < bufferHeight; by++) { + var sourceY = ry - pad + by - ay0; + if (sourceY < 0) { sourceY = 0; } + if (sourceY >= availableHeight) { sourceY = availableHeight - 1; } + for (var bx = 0; bx < bufferWidth; bx++) { + var sourceX = rx - pad + bx - ax0; + if (sourceX < 0) { sourceX = 0; } + if (sourceX >= availableWidth) { sourceX = availableWidth - 1; } + var sourceIndex = (sourceY * availableWidth + sourceX) * 4; + var targetIndex = (by * bufferWidth + bx) * 4; + padded[targetIndex] = available[sourceIndex]; + padded[targetIndex + 1] = available[sourceIndex + 1]; + padded[targetIndex + 2] = available[sourceIndex + 2]; + padded[targetIndex + 3] = available[sourceIndex + 3]; + } + } + glassMaterialInPlace(padded, saturation, scale, offset); + + var materialCanvas = createGlassScratchCanvas(bufferWidth, bufferHeight); + var blurredCanvas = createGlassScratchCanvas(bufferWidth, bufferHeight); + if (!materialCanvas || !blurredCanvas) { + applyBlurSelfRegion(ctx, x, y, width, height, sigma, cornerRadius); + return; + } + var materialContext = materialCanvas.getContext('2d'); + var blurredContext = blurredCanvas.getContext('2d'); + if (!materialContext || !blurredContext) { + applyBlurSelfRegion(ctx, x, y, width, height, sigma, cornerRadius); + return; + } + var materialImage = materialContext.createImageData(bufferWidth, bufferHeight); + materialImage.data.set(padded); + materialContext.putImageData(materialImage, 0, 0); + blurredContext.filter = 'blur(' + sigma + 'px)'; + blurredContext.drawImage(materialCanvas, 0, 0); + var blurred = blurredContext.getImageData(0, 0, bufferWidth, bufferHeight).data; + var scaledCorner = cornerRadius * rect.scale; + var output = applyGlassOptics(blurred, bufferWidth, bufferHeight, pad, + rw, rh, scaledCorner, refraction, specular); + var outputCanvas = createGlassScratchCanvas(rw, rh); + var outputContext = outputCanvas && outputCanvas.getContext('2d'); + if (!outputContext) { + return; + } + var result = outputContext.createImageData(rw, rh); + result.data.set(output); + outputContext.putImageData(result, 0, 0); + ctx.save(); + try { + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.drawImage(outputCanvas, rx, ry); + } finally { + ctx.restore(); + } + } + // Replay one command stream (opcodes + nums + objs) onto ``ctx``. function replaySurfaceCommands(ctx, ops, opCount, nums, objs) { var ni = 0; // num cursor @@ -1275,26 +1545,8 @@ var _bsig = nums[ni++], _bcr = nums[ni++]; if (_bw > 0 && _bh > 0 && ctx.canvas) { try { - ctx.save(); - ctx.beginPath(); - if (_bcr) { - var _brr = _bcr < 0 ? Math.min(_bw, _bh) / 2 - : Math.min(_bcr, Math.min(_bw, _bh) / 2); - ctx.moveTo(_bx + _brr, _by); - ctx.arcTo(_bx + _bw, _by, _bx + _bw, _by + _bh, _brr); - ctx.arcTo(_bx + _bw, _by + _bh, _bx, _by + _bh, _brr); - ctx.arcTo(_bx, _by + _bh, _bx, _by, _brr); - ctx.arcTo(_bx, _by, _bx + _bw, _by, _brr); - ctx.closePath(); - } else { - ctx.rect(_bx, _by, _bw, _bh); - } - ctx.clip(); - ctx.filter = 'blur(' + _bsig + 'px)'; - ctx.drawImage(ctx.canvas, 0, 0); + applyBlurSelfRegion(ctx, _bx, _by, _bw, _bh, _bsig, _bcr); } catch (_ebr) { - } finally { - try { ctx.restore(); } catch (_ebr2) {} } } break; @@ -1315,6 +1567,20 @@ } break; } + case SURF.GLASS_SELF_REGION: { + var _gx = nums[ni++], _gy = nums[ni++], _gw = nums[ni++], _gh = nums[ni++]; + var _gblur = nums[ni++], _gcr = nums[ni++], _gsat = nums[ni++]; + var _gscale = nums[ni++], _goffset = nums[ni++], _grefract = nums[ni++]; + var _gspecular = nums[ni++]; + if (_gw > 0 && _gh > 0 && ctx.canvas) { + try { + applyGlassSelfRegion(ctx, _gx, _gy, _gw, _gh, _gblur, _gcr, + _gsat, _gscale, _goffset, _grefract, _gspecular); + } catch (_egr) { + } + } + break; + } case SURF.BLIT_SURFACE_XY: { var b1 = surfaceTable[nums[ni++]]; if (b1 && b1.canvas) { ctx.drawImage(b1.canvas, nums[ni++], nums[ni++]); } else { ni += 2; } diff --git a/vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java b/vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java index 2b9bb1ee85f..bb3f1f2e3bb 100644 --- a/vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java +++ b/vm/tests/src/test/java/com/codename1/tools/translator/JavascriptTargetIntegrationTest.java @@ -1,3 +1,26 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ + package com.codename1.tools.translator; import org.junit.jupiter.params.ParameterizedTest; @@ -97,6 +120,13 @@ void generatesBrowserBundleForJavascriptTarget(CompilerHelper.CompilerConfig con && browserBridge.contains("_lab = nums[ni++]") && browserBridge.contains("_ltintStrength = nums[ni++]"), "Browser bridge should ship the Simulator-equivalent per-pixel glass-tab lens"); + assertTrue(browserBridge.contains("GLASS_SELF_REGION: 82") + && browserBridge.contains("function glassMaterialInPlace") + && browserBridge.contains("function applyGlassOptics") + && browserBridge.contains("function applyGlassSelfRegion") + && browserBridge.contains("_goffset = nums[ni++]") + && browserBridge.contains("ctx.drawImage(outputCanvas, rx, ry)"), + "Browser bridge should preserve and render the complete native glass recipe"); assertFalse(browserBridge.contains("var _lsw = _lw / _lm"), "Glass-tab lens must not regress to the coarse uniform-zoom fallback"); assertTrue(protocolDoc.contains("Version: 1") From 001b7fcb2f5357701202047ac824c6f299d283af Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 16 Jul 2026 09:32:08 +0300 Subject: [PATCH 03/13] Match liquid glass tabs to native animation frames --- .../src/com/codename1/ui/Graphics.java | 8 +- .../com/codename1/ui/TabSelectionMorph.java | 126 ++++++---- CodenameOne/src/com/codename1/ui/Tabs.java | 68 ++--- .../com/codename1/impl/javase/JavaSEPort.java | 82 +++++-- .../nativeSources/CN1MetalShaders.metal | 37 +-- Ports/iOSPort/nativeSources/METALView.m | 46 ++-- .../iOSPort/nativeSources/iOSModernTheme.res | Bin 155611 -> 156252 bytes Themes/iOSModernTheme.res | Bin 156565 -> 156252 bytes .../codename1/ui/TabSelectionMorphTest.java | 150 +++++++---- native-themes/ios-modern/theme.css | 43 ++-- .../tests/Cn1ssDeviceRunner.java | 1 + ...absLiquidGlassAnimationScreenshotTest.java | 232 ++++++++++++++++++ scripts/verify-javascript-lens-parity.mjs | 64 ++++- .../src/javascript/browser_bridge.js | 42 ++-- 14 files changed, 686 insertions(+), 213 deletions(-) create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/TabsLiquidGlassAnimationScreenshotTest.java diff --git a/CodenameOne/src/com/codename1/ui/Graphics.java b/CodenameOne/src/com/codename1/ui/Graphics.java index 388a8dd793a..b37d540a29a 100644 --- a/CodenameOne/src/com/codename1/ui/Graphics.java +++ b/CodenameOne/src/com/codename1/ui/Graphics.java @@ -1526,9 +1526,11 @@ public boolean glassRegion(int x, int y, int width, int height, float radius, fl /// Applies the iOS 26 selection "drop" LENS to the contents already painted into /// the region (the bar + glyphs UNDER it): radial magnification, edge chromatic - /// aberration, and a luminance-keyed dark->accent tint so dark glyphs read in - /// the accent colour only where the lens covers them. Unlike glassRegion this is - /// drawn OVER the content. cornerRadius<0 = capsule. tintColor is 0xRRGGBB. + /// aberration, and a luminance-keyed accent tint so glyphs read in the accent + /// colour only where the lens covers them. Positive tintStrength keys dark + /// glyphs on a light surface; negative tintStrength keys light glyphs on a dark + /// surface. Unlike glassRegion this is drawn OVER the content. + /// cornerRadius<0 = capsule. tintColor is 0xRRGGBB. public boolean lensRegion(int x, int y, int width, int height, float cornerRadius, float magnify, float aberration, int tintColor, float tintStrength) { if (width <= 0 || height <= 0) { return true; diff --git a/CodenameOne/src/com/codename1/ui/TabSelectionMorph.java b/CodenameOne/src/com/codename1/ui/TabSelectionMorph.java index e48edd97779..53f680d3a6c 100644 --- a/CodenameOne/src/com/codename1/ui/TabSelectionMorph.java +++ b/CodenameOne/src/com/codename1/ui/TabSelectionMorph.java @@ -36,6 +36,19 @@ /// the tokens + geometry from the theme/component and then paints from the returned model. final class TabSelectionMorph { + // At 60fps the 350ms native transition advances about 5% per frame. JavaSE can + // occasionally coalesce repaints and hand the next animation tick a completed + // wall-clock Motion; cap the visible jump so at least the characteristic liquid + // frames are painted instead of snapping directly from 0 to 100. + private static final int MAX_VISIBLE_FRAME_STEP = 14; + + static int paceVisibleFrame(int previous, int wallClockValue) { + int prior = previous < 0 ? 0 : (previous > 100 ? 100 : previous); + int raw = wallClockValue < prior ? prior : (wallClockValue > 100 ? 100 : wallClockValue); + int capped = prior + MAX_VISIBLE_FRAME_STEP; + return raw > capped ? capped : raw; + } + /// The morph's tuning tokens. Themes do not set these individually: a NAMED /// PRESET ({@link #preset}) supplies the full envelope set, and the theme /// exposes only high-level controls -- the preset name (tabsMorphPreset), @@ -58,45 +71,54 @@ static final class Tokens { float peakAb; // mid-flight chromatic aberration (fraction) float tintStrength; // lens accent-tint strength 0..1 int barGrowPct; // whole-bar grow pulse percent (0 = off) - float spring = 1f; // settle-overshoot scale (1 = preset amount, 0 = none) + float spring = 1f; // settle-deformation scale (0 = no stop squash/swell) /// The named motion presets. "ios26" (the default, and any unknown name) - /// is the measured iOS 26 Liquid Glass morph; "subtle" halves the - /// deformation and optics for a calmer selection change. + /// is bounded from the recorded iOS 26 UITabBar transition: the travelling + /// drop stays close to one cell, grows only a few percent vertically, and + /// uses barely-visible chromatic separation. "subtle" reduces those cues + /// further for applications that want an almost-flat selection change. static Tokens preset(String name) { Tokens tk = new Tokens(); if ("subtle".equals(name)) { - tk.stretch = 0.16f; - tk.squashW = 0.08f; - tk.grow = 0.07f; - tk.squashH = 0.09f; - tk.liftMm = 0.25f; - tk.bubbleWidthPct = 96; - tk.overflowPct = 12; - tk.downBiasMm = 0.15f; - tk.restMag = 1.04f; - tk.peakMag = 1.09f; - tk.peakAb = 0.01f; + tk.stretch = 0f; + tk.squashW = 0.03f; + tk.grow = 0.005f; + tk.squashH = 0.025f; + tk.liftMm = 0.03f; + tk.bubbleWidthPct = 100; + tk.overflowPct = 0; + tk.downBiasMm = 0f; + tk.restMag = 1.01f; + tk.peakMag = 1.05f; + tk.peakAb = 0.00025f; tk.tintStrength = 1f; tk.barGrowPct = 0; + tk.spring = 0.20f; return tk; } // "ios26" -- the shipped iOS Modern tuning. - tk.stretch = 0.32f; - tk.squashW = 0.16f; - tk.grow = 0.14f; - tk.squashH = 0.18f; - tk.liftMm = 0.5f; - // Wider than the cell: the native iOS 26 selected pill overlaps its - // neighbour cells (276px over a 253px cell on the @3x reference bar). - tk.bubbleWidthPct = 109; - tk.overflowPct = 18; - tk.downBiasMm = 0.3f; - tk.restMag = 1.08f; - tk.peakMag = 1.18f; - tk.peakAb = 0.02f; + // The recorded native drop keeps essentially one cell's width while + // it travels. Its liquid character comes from refraction across the + // moving edge, not from stretching across the neighbouring glyphs. + // A wider lens duplicated two tabs at once and looked like distortion + // instead of the compact UIKit bubble. + tk.stretch = 0f; + tk.squashW = 0.05f; + tk.grow = 0.01f; + tk.squashH = 0.04f; + tk.liftMm = 0.05f; + tk.bubbleWidthPct = 100; + tk.overflowPct = 0; + tk.downBiasMm = 0f; + tk.restMag = 1.02f; + tk.peakMag = 1.08f; + tk.peakAb = 0.0005f; tk.tintStrength = 1f; tk.barGrowPct = 0; + // Native keeps a restrained stop deformation without positional + // overshoot; scale the width/height settle envelope to 35%. + tk.spring = 0.35f; return tk; } @@ -145,24 +167,42 @@ static float smooth(float a, float b, float x) { return t * t * (3 - 2 * t); } - /// Position easing: an even ease-in-out travel reaching the target ~t=0.78, then a - /// small damped overshoot that settles by t=1 (the "stop" bounce). The - /// springiness scales the overshoot amplitude: 1 = the preset 0.09, 0 = no - /// overshoot (a plain ease-in-out stop), 2 = double the bounce. - static float springEase(float t, float springiness) { + // Measured centres of the travelling native selection drop, normalized from + // the first visible frame through its 350ms settle. UIKit's curve accelerates + // more quickly than smoothstep, then spends the last third easing into place; + // approximating it with a short 200ms ease made the CN1 drop look like a jump. + private static final float[] NATIVE_POSITION_TIME = { + 0f, 0.049f, 0.100f, 0.151f, 0.200f, 0.251f, 0.294f, + 0.337f, 0.391f, 0.443f, 0.486f, 0.537f, 0.577f, + 0.629f, 0.677f, 0.723f, 0.766f, 0.820f, 0.863f, 0.914f, 1f + }; + private static final float[] NATIVE_POSITION_VALUE = { + 0f, 0.032f, 0.099f, 0.219f, 0.288f, 0.394f, 0.492f, + 0.582f, 0.678f, 0.728f, 0.770f, 0.804f, 0.876f, + 0.907f, 0.932f, 0.951f, 0.966f, 0.981f, 0.996f, 1f, 1f + }; + + /// Position easing sampled from the recorded iOS 26 UITabBar transition. + /// The path is monotonic: the native drop deforms at the stop but does not + /// shoot past the end of the bar and snap back. + static float springEase(float t) { if (t <= 0f) { return 0f; } if (t >= 1f) { return 1f; } - float travelEnd = 0.78f; - if (t <= travelEnd) { - float u = t / travelEnd; - return u * u * (3 - 2 * u); + for (int i = 1; i < NATIVE_POSITION_TIME.length; i++) { + if (t <= NATIVE_POSITION_TIME[i]) { + float ta = NATIVE_POSITION_TIME[i - 1]; + float tb = NATIVE_POSITION_TIME[i]; + float u = (t - ta) / (tb - ta); + float va = NATIVE_POSITION_VALUE[i - 1]; + float vb = NATIVE_POSITION_VALUE[i]; + return va + (vb - va) * u; + } } - float u = (t - travelEnd) / (1f - travelEnd); - return 1f + 0.09f * springiness * (float) (Math.sin(u * Math.PI) * (1f - u)); + return 1f; } /// Computes one morph frame. @@ -183,7 +223,7 @@ static TabSelectionMorph compute(float t, int fromX, int fromW, int toX, int toW TabSelectionMorph m = new TabSelectionMorph(); float tp = t < 0 ? 0 : (t > 1 ? 1 : t); - float pos = springEase(tp, tk.spring); + float pos = springEase(tp); int x = fromX + (int) ((toX - fromX) * pos); int w = fromW + (int) ((toW - fromW) * pos); @@ -192,6 +232,10 @@ static TabSelectionMorph compute(float t, int fromX, int fromW, int toX, int toW float moving = smooth(0f, 0.08f, tp) * (1f - smooth(0.88f, 1f, tp)); float sd = (tp - 0.80f) / 0.14f; float squash = (sd > -1f && sd < 1f) ? (1f - sd * sd) * (1f - sd * sd) : 0f; // settle bump + // Springiness controls the stop DEFORMATION, not positional overshoot. + // The native centre path above is monotonic; the liquid cue is its brief + // width compression/height swell as it comes to rest. + squash *= tk.spring; float grow = smooth(0f, 0.10f, tp) * (1f - smooth(0.20f, 0.42f, tp)); // early whole-bar swell // horizontal elongation while moving, then width compression at the stop @@ -206,8 +250,8 @@ static TabSelectionMorph compute(float t, int fromX, int fromW, int toX, int toW capX += (w - bubbleW) / 2; w = bubbleW; - // The drop may be wider than its cell (bubbleWidthPct > 100) and the - // spring overshoots the end cells -- but it must never leave the bar: + // The drop may be wider than its cell (bubbleWidthPct > 100), but it + // must never leave the bar: // the native pill overlaps NEIGHBOUR cells, not the backdrop. if (capX < barLeftX) { w -= barLeftX - capX; diff --git a/CodenameOne/src/com/codename1/ui/Tabs.java b/CodenameOne/src/com/codename1/ui/Tabs.java index 21fc3bd9c67..8435bedaa90 100644 --- a/CodenameOne/src/com/codename1/ui/Tabs.java +++ b/CodenameOne/src/com/codename1/ui/Tabs.java @@ -157,12 +157,15 @@ public class Tabs extends Container { private int animatedIndicatorDurationMs = 200; private int animatedIndicatorThicknessMm = 1; // 1mm-tall underline private Motion indicatorAnimMotion; + // Last progress actually presented. This is paced separately from the + // wall-clock Motion so a coalesced/slow repaint cannot skip the whole morph. + private int indicatorAnimValue; // The tab index the indicator morph is currently travelling TO. Lets a re-entrant // setSelectedIndex (e.g. the content-slide finishing) recognise that a morph to the // same tab is already in flight and NOT restart it mid-travel. private int indicatorTargetIndex = -1; // TEST-ONLY: when >=0, paintSelectionCapsule renders the morph at this fixed - // 0..120 progress instead of the live motion (for the JavaSE capture probe). + // 0..100 progress instead of the live motion (for the JavaSE capture probe). private int morphTestValue = -1; // Tab bounds at the start of the indicator animation. private int indicatorFromX; @@ -398,7 +401,10 @@ public boolean animate() { // slide motion control deregistration; the indicator motion is // cheap enough to run alongside without coordination. if (indicatorAnimMotion != null) { - if (indicatorAnimMotion.isFinished()) { + int wallClockValue = indicatorAnimMotion.getValue(); + indicatorAnimValue = TabSelectionMorph.paceVisibleFrame( + indicatorAnimValue, wallClockValue); + if (indicatorAnimValue >= 100) { indicatorAnimMotion = null; indicatorTargetIndex = -1; // Paint ONE more frame now that the morph is over so the SETTLED capsule @@ -469,7 +475,7 @@ void deregisterAnimatedInternal() { // indicator morph are done. Previously a finished 200ms slide deregistered the // animation while the 550ms morph was still in flight, freezing the drop mid-travel. if ((slideToDestMotion == null || slideToDestMotion.isFinished()) - && (indicatorAnimMotion == null || indicatorAnimMotion.isFinished())) { + && indicatorAnimMotion == null) { Form f = getComponentForm(); if (f != null) { f.deregisterAnimatedInternal(this); @@ -1474,7 +1480,7 @@ private void startIndicatorAnimation(int fromIndex, int toIndex) { // If a motion is already in flight, start from the *current* // interpolated position, not from the previous tab -- otherwise // rapid double-clicks jump back to a stale baseline. - if (indicatorAnimMotion != null && !indicatorAnimMotion.isFinished()) { + if (indicatorAnimMotion != null && indicatorAnimValue < 100) { if (toIndex == indicatorTargetIndex) { // A morph to this SAME tab is already running. The content-slide finishing // re-invokes setSelectedIndex(active) to finalise the selection; restarting @@ -1482,7 +1488,7 @@ private void startIndicatorAnimation(int fromIndex, int toIndex) { // Let the in-flight morph run to completion instead. return; } - int v = indicatorAnimMotion.getValue(); + int v = indicatorAnimValue; indicatorFromX = indicatorFromX + ((indicatorToX - indicatorFromX) * v / 100); indicatorFromW = indicatorFromW + ((indicatorToW - indicatorFromW) * v / 100); } else { @@ -1493,11 +1499,12 @@ private void startIndicatorAnimation(int fromIndex, int toIndex) { indicatorToW = to[1]; indicatorTargetIndex = toIndex; // LINEAR-TIME motion: the value is the morph timeline 0..100 and - // paintSelectionCapsule derives the spring position (springEaseTabs, an - // ease-out-back overshoot) AND the height/squash envelopes from it -- so the - // bubble stays tall while travelling and compresses at the stop. (The non-glass + // paintSelectionCapsule derives the recorded native position curve AND the + // height/squash envelopes from it -- so the bubble stays tall while travelling + // and compresses at the stop. (The non-glass // Material underline path reads the same value as a plain position fraction.) indicatorAnimMotion = Motion.createLinearMotion(0, 100, animatedIndicatorDurationMs); + indicatorAnimValue = 0; indicatorAnimMotion.start(); Form f = getComponentForm(); if (f != null) { @@ -1538,7 +1545,7 @@ void paintBottomDivider(Graphics g) { g.setAlpha(oldAlpha); } - // Position easing (springEaseTabs) + smoothstep (lensSmooth) now live in the pure + // Position easing + smoothstep now live in the pure // TabSelectionMorph model (springEase / smooth) so the morph math is unit-testable. /// The thin frost rim left around the selection capsule (tabSelInsetMm, default a hair). @@ -1590,7 +1597,7 @@ private void capsuleCellBounds(int index, int inset, int[] out) { } /// TEST-ONLY hook: render the selection morph frozen at a fixed progress - /// (`value` 0..120, where 100 is the target and >100 is the settle overshoot) + /// (`value` 0..100, where 100 is the settled target) /// travelling from `fromIndex` to `toIndex`, so a JavaSE probe can capture exact /// frames of the animation without racing the real-time motion. Pass `value` < 0 /// to clear and resume normal behaviour. @@ -1617,18 +1624,20 @@ public void setMorphTestState(int fromIndex, int toIndex, int value) { /// The iOS "selected cell" background: a subtle grey capsule kept at bar height /// (the lens drop, drawn over it, bulges taller). Travels + elongates with the /// drop. systemFill grey so it reads neutral, not blue. Alpha via tabSelPillAlphaInt. - private void drawSelectionPill(Graphics g, int capX, int capY, int w, int capH, float bump) { + private void drawSelectionPill(Graphics g, int capX, int capY, int w, int capH, + boolean dark) { int pillInset = capH * 7 / 100; // pill a hair shorter than the lens int py = capY + pillInset; int ph = capH - 2 * pillInset; if (ph <= 0) { return; } - // FADE the grey pill out as the drop travels: the settled "selected cell" - // background is grey, but MID-FLIGHT the bubble is pure transparent glass - // (otherwise the grey shows through the gap between tabs as an empty blob). + // The native frame sequence keeps the system-fill body visible for the + // entire trip (#b4 light / #78 dark on the flat reference backdrop). + // Fading 85% of it away in flight left only a thin lens outline, which is + // why the foreground liquid bubble disappeared in JavaSE/JavaScript. int baseAlpha = getUIManager().getThemeConstant("tabSelPillAlphaInt", 34); - int alpha = (int) (baseAlpha * (1f - 0.85f * bump)); + int alpha = baseAlpha; if (alpha <= 0) { return; } @@ -1636,7 +1645,9 @@ private void drawSelectionPill(Graphics g, int capX, int capY, int w, int capH, int oldA = g.getAlpha(); boolean aa = g.isAntiAliased(); g.setAntiAliased(true); - g.setColor(getUIManager().getThemeConstant("tabSelPillColorInt", 0x767680)); + g.setColor(getUIManager().getThemeConstant(dark + ? "tabSelPillDarkColorInt" : "tabSelPillColorInt", + dark ? 0xc9c9ce : 0x767680)); g.setAlpha(alpha); g.fillRoundRect(capX, py, w, ph, ph, ph); g.setAntiAliased(aa); @@ -1684,7 +1695,7 @@ void paintSelectionCapsule(Graphics g) { int toW; float t; if (morphTestValue >= 0 || indicatorAnimMotion != null) { - int v = morphTestValue >= 0 ? morphTestValue : indicatorAnimMotion.getValue(); + int v = morphTestValue >= 0 ? morphTestValue : indicatorAnimValue; t = (v < 0 ? 0 : (v > 100 ? 100 : v)) / 100f; fromX = indicatorFromX; fromW = indicatorFromW; @@ -1733,15 +1744,14 @@ void paintSelectionCapsule(Graphics g) { g.lensRegion(m.barGrowX, m.barGrowY, m.barGrowW, m.barGrowH, -1f, m.barGrowMag, 0f, 0x000000, 0f); } - drawSelectionPill(g, m.capX, m.capY, m.capW, m.capH, m.flight); - // Accent supplied by the lens in LIGHT mode only: the keying tints DARK - // pixels toward the accent, which is right over a light frost (the - // deliberately-dark glyphs turn blue) but floods a dark bar solid blue, - // because everything under the drop is dark there. On dark bars the - // glyphs carry the accent directly (theme) and the lens keeps only its - // magnify/aberration optics. + drawSelectionPill(g, m.capX, m.capY, m.capW, m.capH, dark); + // The sign selects the luminance key: positive tints dark glyphs on the + // light bar, negative tints light glyphs on the dark bar. This keeps the + // accent attached to the travelling drop in both appearances instead of + // painting the destination tab blue before the bubble reaches it. int tint = getUIManager().getThemeConstant("tabSelLensTintColorInt", 0x0a84ff); - g.lensRegion(m.lensX, m.lensY, m.lensW, m.lensH, -1f, m.magnify, m.aberration, tint, dark ? 0f : m.tintStrength); + g.lensRegion(m.lensX, m.lensY, m.lensW, m.lensH, -1f, m.magnify, + m.aberration, tint, dark ? -m.tintStrength : m.tintStrength); return; } // Non-glass platforms: a translucent rounded capsule. @@ -1761,8 +1771,8 @@ void paintSelectionCapsule(Graphics g) { /// only (review: fewer, coherent morph knobs): tabsMorphPreset picks a named /// envelope set inside the motion model ("ios26" default / "subtle"), /// tabsMorphLensIntensityPct scales the lens optics around the preset (100 = - /// as authored) and tabsMorphSpringPct scales the settle overshoot (100 = - /// preset bounce, 0 = plain stop). Duration remains + /// as authored) and tabsMorphSpringPct scales the preset's settle deformation + /// (100 = preset deformation, 0 = plain stop). Duration remains /// tabsAnimatedIndicatorDurationInt. The mm lengths carried by the preset /// are converted to px here so the model stays Display-free. private TabSelectionMorph.Tokens morphTokens() { @@ -1770,7 +1780,7 @@ private TabSelectionMorph.Tokens morphTokens() { TabSelectionMorph.Tokens tk = TabSelectionMorph.Tokens.preset( uim.getThemeConstant("tabsMorphPreset", "ios26")); tk.scaleLensIntensity(uim.getThemeConstant("tabsMorphLensIntensityPct", 100) / 100f); - tk.spring = uim.getThemeConstant("tabsMorphSpringPct", 100) / 100f; + tk.spring *= uim.getThemeConstant("tabsMorphSpringPct", 100) / 100f; tk.liftPx = Display.getInstance().convertToPixels(tk.liftMm); tk.downBiasPx = Display.getInstance().convertToPixels(tk.downBiasMm); return tk; @@ -1785,7 +1795,7 @@ void paintAnimatedIndicator(Graphics g) { int x; int w; if (indicatorAnimMotion != null) { - int v = indicatorAnimMotion.getValue(); // 0..100 + int v = indicatorAnimValue; // paced visible progress, 0..100 x = indicatorFromX + ((indicatorToX - indicatorFromX) * v / 100); w = indicatorFromW + ((indicatorToW - indicatorFromW) * v / 100); } else { diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java index 9a77893b38b..a4c7567d6a6 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java @@ -9128,6 +9128,25 @@ public void componentHidden(ComponentEvent e) { Display.getInstance().setDefaultVirtualKeyboard(null); } + // Deterministic visual probes sometimes need to render at the physical + // density of a reference device while keeping the Simulator window small + // enough to fit the host display. A skin normally supplies this value via + // ppi/pixelRatio; this opt-in override decouples density from window chrome + // and is intentionally applied after skin/desktop-mode initialization. + String forcedPixelMilliRatio = System.getProperty("cn1.javase.pixelMilliRatio"); + if (forcedPixelMilliRatio != null) { + try { + double ratio = Double.parseDouble(forcedPixelMilliRatio.trim()); + if (ratio > 0) { + pixelMilliRatio = Double.valueOf(ratio); + setDefaultPixelMilliRatio(pixelMilliRatio); + } + } catch (NumberFormatException err) { + System.err.println("Ignoring invalid cn1.javase.pixelMilliRatio=" + + forcedPixelMilliRatio); + } + } + float factor = ((float) getDisplayHeight()) / 480.0f; if (factor > 0 && autoAdjustFontSize && getSkin() != null) { // set a reasonable default font size @@ -17208,29 +17227,33 @@ public boolean blurRegion(Object graphics, int x, int y, int width, int height, private static final double LENS_MAG_FLAT = 0.75; private static final double LENS_TINT_HI = 150; private static final double LENS_TINT_LO = 55; + private static final double LENS_LIGHT_KEY_LO = 150; + private static final double LENS_LIGHT_KEY_HI = 220; // The drop LIFTS the content under it upward (like a magnifying droplet pulling the // glyph up) -- this is the "tabs grow/rise" in the native morph, not just magnify. // Lift is proportional to (magnify-1) so it tracks the travel bump, peaks at centre. - private static final double LENS_LIFT_COEF = 0.40; + private static final double LENS_LIFT_COEF = 0.10; // The drop is a 3D GLASS droplet: a soft specular GLARE (light sheen) near the top, // and a bright EDGE RIM that defines the glass boundary. Without these it reads as a // flat tinted pill, not glass. - private static final double LENS_GLARE = 0.09; // specular sheen strength - private static final double LENS_RIM = 0.06; // edge-rim brightness - private static final double LENS_RIM_W = 0.06; // rim band width (fraction of half-height) - private static final double LENS_REFRACT = 0.16; // edge lensing: content bends at the rim - private static final double LENS_EDGE_SHADOW = 0.12; // soft dark band at the inner edge (glass depth) - // The periphery (bar / other tabs seen through the drop) is slightly SHRUNK while the - // central glyph stays enlarged -- mag falls from `magnify` at the centre to RIM_SCALE - // (<1) at the rim. And the glass carries a faint cool TINT inside it. - private static final double LENS_RIM_SCALE = 0.84; - private static final int LENS_GLASS_TINT = 0xbcd8ff; // faint cool-blue glass cast - private static final double LENS_GLASS_TINT_STR = 0.10; + private static final double LENS_GLARE = 0.07; // broad moving specular sheen + private static final double LENS_RIM = 0.11; // directional top/left rim light + private static final double LENS_RIM_W = 0.10; // rim band width (fraction of half-height) + private static final double LENS_REFRACT = 0.015; // native barely displaces content at the rim + private static final double LENS_EDGE_SHADOW = 0.07; // directional bottom/right depth + // Magnification falls gently from `magnify` at the centre to 1 at the rim. The + // previous 0.84 rim scale visibly crushed neighbouring glyphs and read as a lens + // distortion rather than the native tab's shallow convex surface. + private static final double LENS_RIM_SCALE = 1.0; + private static final int LENS_GLASS_TINT = 0xbcd8ff; + private static final double LENS_GLASS_TINT_STR = 0.0; // native rim is neutral white, not a blue outline // The selected glyph seen through the drop reads MORE vivid/saturated than a flat // tint -- native's selected blue is punchy. Boost chroma around the pixel luminance: // coloured pixels (the blue glyph) push further from grey; neutral greys (the bar) // are barely touched, so only the glyph gets more saturated. - private static final double LENS_SAT_BOOST = 1.32; + private static final double LENS_SAT_BOOST = 1.12; + private static final double LENS_GLASS_START = 1.025; + private static final double LENS_GLASS_FULL = 1.075; @Override public boolean lensRegion(Object graphics, int x, int y, int width, int height, @@ -17295,7 +17318,11 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, // The 3D-glass cues (edge refraction / edge shadow / glare) belong to the MORPH // droplet, not the settled pill -- scale them by how magnified the drop is so a // resting selection stays a flat subtle pill. - double glassAmt = lensSmoothstep(1.085, 1.25, magnify); + // The bounded iOS-26 morph rests at 1.02x and peaks at 1.08x. Keep the + // liquid foreground completely absent at rest, but let its rim/glare/ + // refraction reach full strength during flight. The previous 1.085x + // lower edge sat above the new peak, silently disabling the liquid layer. + double glassAmt = lensSmoothstep(LENS_GLASS_START, LENS_GLASS_FULL, magnify); for (int yy = 0; yy < rh; yy++) { double py = yy + 0.5 - hh; for (int xx = 0; xx < rw; xx++) { @@ -17310,9 +17337,8 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, double alpha = Math.min(depth, 1.0); double rd = Math.min(1.0, Math.sqrt((px * px) / (hw * hw) + (py * py) / (hh * hh))); double edge = lensSmoothstep(LENS_MAG_FLAT, 1.0, rd); - // Centre ENLARGES (magnify); periphery SHRINKS (mag -> RIM_SCALE < 1) so the - // bar / other tabs seen through the drop read slightly minified, while the - // central glyph stays big. No shrink when settled (scaled by glassAmt). + // Centre enlarges gently; the rim returns to 1:1 instead of minifying + // neighbouring content. This keeps the native shallow-lens character. double rimScale = 1.0 + (LENS_RIM_SCALE - 1.0) * glassAmt; double mag = magnify + (rimScale - magnify) * edge; if (mag < 0.2) mag = 0.2; @@ -17330,7 +17356,9 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, int sg = (lensSample(src, rw, rh, hw + (px / mag) * refr, hh + (py / mag) * refr + lift) >> 8) & 0xff; int sb = lensSample(src, rw, rh, hw + (px / magB) * refr, hh + (py / magB) * refr + lift) & 0xff; double lum = 0.2126 * sr + 0.7152 * sg + 0.0722 * sb; - double t = tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + double t = tintStrength < 0 + ? -tintStrength * lensSmoothstep(LENS_LIGHT_KEY_LO, LENS_LIGHT_KEY_HI, lum) + : tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); double fr = sr + (tr - sr) * t; double fg = sg + (tg - sg) * t; double fb = sb + (tb - sb) * t; @@ -17347,15 +17375,17 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, fr = sl + (fr - sl) * LENS_SAT_BOOST; fg = sl + (fg - sl) * LENS_SAT_BOOST; fb = sl + (fb - sl) * LENS_SAT_BOOST; - // 3D GLASS: a soft specular GLARE near the top (an elliptical sheen) plus - // a bright EDGE RIM (small `depth` = near the boundary) so the drop reads - // as a raised glass droplet, not a flat tint. Both lift the colour toward - // white. + // 3D GLASS: native depth is a directional highlight/shadow surface, not + // large content displacement. Light the top-left rim and shade the + // bottom-right rim so the travelling capsule reads as a convex volume. double gx = px / hw, gy = (py + 0.42 * hh) / hh; double glare = LENS_GLARE * glassAmt * Math.exp(-(gx * gx * 1.15 + gy * gy * 2.6) * 2.1); double rimW = Math.max(2.0, LENS_RIM_W * hh); - double rim = depth < rimW ? (1.0 - depth / rimW) * LENS_RIM : 0; - double bright = glare + rim; + double rim = depth < rimW ? (1.0 - depth / rimW) * LENS_RIM + * (0.35 + 0.65 * glassAmt) : 0; + double lightDir = Math.max(0.2, Math.min(1.0, + 0.68 - 0.28 * (py / hh) - 0.12 * (px / hw))); + double bright = glare + rim * lightDir; if (bright > 0) { fr += bright * (255 - fr); fg += bright * (255 - fg); @@ -17365,7 +17395,9 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, // shadow at its edge) -- gives the droplet depth instead of a flat cutout. double esW = Math.max(2.0, 0.13 * Math.min(hw, hh)); if (depth < esW) { - double es = (1.0 - depth / esW) * LENS_EDGE_SHADOW * glassAmt; + double shadowDir = Math.max(0.1, Math.min(1.0, + 0.55 + 0.35 * (py / hh) + 0.10 * (px / hw))); + double es = (1.0 - depth / esW) * LENS_EDGE_SHADOW * glassAmt * shadowDir; fr *= (1 - es); fg *= (1 - es); fb *= (1 - es); diff --git a/Ports/iOSPort/nativeSources/CN1MetalShaders.metal b/Ports/iOSPort/nativeSources/CN1MetalShaders.metal index a22e2f92fa6..d5c332a1a2d 100644 --- a/Ports/iOSPort/nativeSources/CN1MetalShaders.metal +++ b/Ports/iOSPort/nativeSources/CN1MetalShaders.metal @@ -342,16 +342,20 @@ fragment float4 cn1_fs_multistop_gradient( constant float LENS_MAG_FLAT = 0.75; constant float LENS_TINT_HI = 150.0; constant float LENS_TINT_LO = 55.0; -constant float LENS_LIFT_COEF = 0.40; -constant float LENS_GLARE = 0.09; -constant float LENS_RIM = 0.06; -constant float LENS_RIM_W = 0.06; -constant float LENS_REFRACT = 0.16; -constant float LENS_EDGE_SHADOW = 0.12; -constant float LENS_RIM_SCALE = 0.84; +constant float LENS_LIGHT_KEY_LO = 150.0; +constant float LENS_LIGHT_KEY_HI = 220.0; +constant float LENS_LIFT_COEF = 0.10; +constant float LENS_GLARE = 0.07; +constant float LENS_RIM = 0.11; +constant float LENS_RIM_W = 0.10; +constant float LENS_REFRACT = 0.015; +constant float LENS_EDGE_SHADOW = 0.07; +constant float LENS_RIM_SCALE = 1.0; constant float3 LENS_GLASS_TINT = float3(188.0, 216.0, 255.0); // 0xbcd8ff, 0..255 -constant float LENS_GLASS_TINT_STR = 0.10; -constant float LENS_SAT_BOOST = 1.32; +constant float LENS_GLASS_TINT_STR = 0.0; +constant float LENS_SAT_BOOST = 1.12; +constant float LENS_GLASS_START = 1.025; +constant float LENS_GLASS_FULL = 1.075; static inline float cn1_lens_smoothstep(float a, float b, float x) { float t = clamp((x - a) / (b - a), 0.0, 1.0); @@ -389,7 +393,7 @@ fragment float4 cn1_fs_lens( float rd = min(1.0, sqrt((px * px) / (hw * hw) + (py * py) / (hh * hh))); float liftMax = LENS_LIFT_COEF * (magnify - 1.0) * hh; - float glassAmt = cn1_lens_smoothstep(1.085, 1.25, magnify); + float glassAmt = cn1_lens_smoothstep(LENS_GLASS_START, LENS_GLASS_FULL, magnify); float edge = cn1_lens_smoothstep(LENS_MAG_FLAT, 1.0, rd); float rimScale = 1.0 + (LENS_RIM_SCALE - 1.0) * glassAmt; @@ -408,7 +412,9 @@ fragment float4 cn1_fs_lens( float sb = src.sample(smp, cB).b * 255.0; float lum = 0.2126 * sr + 0.7152 * sg + 0.0722 * sb; - float t = tintStrength * cn1_lens_smoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + float t = tintStrength < 0.0 + ? -tintStrength * cn1_lens_smoothstep(LENS_LIGHT_KEY_LO, LENS_LIGHT_KEY_HI, lum) + : tintStrength * cn1_lens_smoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); float fr = sr + (tintc.r - sr) * t; float fg = sg + (tintc.g - sg) * t; float fb = sb + (tintc.b - sb) * t; @@ -426,15 +432,18 @@ fragment float4 cn1_fs_lens( float gx = px / hw, gy = (py + 0.42 * hh) / hh; float glare = LENS_GLARE * glassAmt * exp(-(gx * gx * 1.15 + gy * gy * 2.6) * 2.1); float rimW = max(2.0, LENS_RIM_W * hh); - float rim = depth < rimW ? (1.0 - depth / rimW) * LENS_RIM : 0.0; - float bright = glare + rim; + float rim = depth < rimW ? (1.0 - depth / rimW) * LENS_RIM + * (0.35 + 0.65 * glassAmt) : 0.0; + float lightDir = clamp(0.68 - 0.28 * (py / hh) - 0.12 * (px / hw), 0.2, 1.0); + float bright = glare + rim * lightDir; fr += bright * (255.0 - fr); fg += bright * (255.0 - fg); fb += bright * (255.0 - fb); float esW = max(2.0, 0.13 * min(hw, hh)); if (depth < esW) { - float es = (1.0 - depth / esW) * LENS_EDGE_SHADOW * glassAmt; + float shadowDir = clamp(0.55 + 0.35 * (py / hh) + 0.10 * (px / hw), 0.1, 1.0); + float es = (1.0 - depth / esW) * LENS_EDGE_SHADOW * glassAmt * shadowDir; fr *= (1.0 - es); fg *= (1.0 - es); fb *= (1.0 - es); } fr = clamp(fr, 0.0, 255.0); fg = clamp(fg, 0.0, 255.0); fb = clamp(fb, 0.0, 255.0); diff --git a/Ports/iOSPort/nativeSources/METALView.m b/Ports/iOSPort/nativeSources/METALView.m index 6fdbe77259e..ea6f9bfce59 100644 --- a/Ports/iOSPort/nativeSources/METALView.m +++ b/Ports/iOSPort/nativeSources/METALView.m @@ -214,16 +214,20 @@ static inline float glassSmoothstep(float a, float b, float x) { #define LENS_MAG_FLAT 0.75f // uniform-magnify fraction of the (elliptical) radius #define LENS_TINT_HI 150.0f // luminance >= HI: no tint #define LENS_TINT_LO 55.0f // luminance <= LO: full dark->accent tint -#define LENS_LIFT_COEF 0.40f // upward pull of the content under the drop -#define LENS_GLARE 0.09f // specular sheen strength -#define LENS_RIM 0.06f // edge-rim brightness -#define LENS_RIM_W 0.06f // rim band width (fraction of half-height) -#define LENS_REFRACT 0.16f // edge lensing: content bends at the rim -#define LENS_EDGE_SHADOW 0.12f // soft dark band just inside the rim -#define LENS_RIM_SCALE 0.84f // periphery shrinks (< 1) while the centre enlarges +#define LENS_LIGHT_KEY_LO 150.0f // dark bar/pill remain neutral +#define LENS_LIGHT_KEY_HI 220.0f // light glyph becomes full accent +#define LENS_LIFT_COEF 0.10f // restrained upward pull under the drop +#define LENS_GLARE 0.07f // broad moving specular sheen +#define LENS_RIM 0.11f // directional top/left rim light +#define LENS_RIM_W 0.10f // rim band width (fraction of half-height) +#define LENS_REFRACT 0.015f // native barely displaces content at the rim +#define LENS_EDGE_SHADOW 0.07f // directional bottom/right depth +#define LENS_RIM_SCALE 1.0f // no funhouse minification at the periphery #define LENS_GLASS_TINT 0xbcd8ff /* faint cool-blue cast through the whole glass */ -#define LENS_GLASS_TINT_STR 0.10f -#define LENS_SAT_BOOST 1.32f // push the tinted blue glyph more vivid +#define LENS_GLASS_TINT_STR 0.0f +#define LENS_SAT_BOOST 1.12f // restrained blue saturation +#define LENS_GLASS_START 1.025f +#define LENS_GLASS_FULL 1.075f __attribute__((unused)) static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, @@ -240,7 +244,9 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, float liftMax = LENS_LIFT_COEF * (magnify - 1.0f) * hh; // The 3D-glass cues belong to the morph droplet, not the settled pill -- fade them // out by how magnified the drop is, so a resting selection is a flat subtle pill. - float glassAmt = glassSmoothstep(1.085f, 1.25f, magnify); + // The bounded tab morph rests at 1.02x and peaks at 1.08x. Fade the + // foreground optics in above rest and reach full liquid character in flight. + float glassAmt = glassSmoothstep(LENS_GLASS_START, LENS_GLASS_FULL, magnify); for (int y = 0; y < rh; y++) { float py = (y + 0.5f) - hh; for (int x = 0; x < rw; x++) { @@ -256,9 +262,8 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, float alpha = depth >= 1.0f ? 1.0f : depth; float rd = sqrtf((px * px) / (hw * hw) + (py * py) / (hh * hh)); // elliptical 0..1 if (rd > 1.0f) rd = 1.0f; - // Centre ENLARGES (magnify); periphery SHRINKS toward RIM_SCALE (< 1) so the - // bar/other tabs seen through the drop read minified while the central glyph - // stays big. No shrink when settled (rimScale -> 1 as glassAmt -> 0). + // Centre enlarges gently; the rim returns to 1:1 instead of minifying + // neighbouring content. This keeps the native shallow-lens character. float edge = glassSmoothstep(LENS_MAG_FLAT, 1.0f, rd); float rimScale = 1.0f + (LENS_RIM_SCALE - 1.0f) * glassAmt; float mag = magnify + (rimScale - magnify) * edge; @@ -273,7 +278,9 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, int sg = (glassSampleBilinear(src, rw, rh, hw + (px / mag) * refr, hh + (py / mag) * refr + lift) >> 8) & 0xff; int sb = glassSampleBilinear(src, rw, rh, hw + (px / magB) * refr, hh + (py / magB) * refr + lift) & 0xff; float lum = 0.2126f * sr + 0.7152f * sg + 0.0722f * sb; - float t = tintStrength * glassSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + float t = tintStrength < 0.0f + ? -tintStrength * glassSmoothstep(LENS_LIGHT_KEY_LO, LENS_LIGHT_KEY_HI, lum) + : tintStrength * glassSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); float fr = sr + (tr - sr) * t; float fg = sg + (tg - sg) * t; float fb = sb + (tb - sb) * t; @@ -291,8 +298,11 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, float gx = px / hw, gy = (py + 0.42f * hh) / hh; float glare = LENS_GLARE * glassAmt * expf(-(gx * gx * 1.15f + gy * gy * 2.6f) * 2.1f); float rimW = LENS_RIM_W * hh; if (rimW < 2.0f) rimW = 2.0f; - float rim = depth < rimW ? (1.0f - depth / rimW) * LENS_RIM : 0.0f; - float bright = glare + rim; + float rim = depth < rimW ? (1.0f - depth / rimW) * LENS_RIM + * (0.35f + 0.65f * glassAmt) : 0.0f; + float lightDir = fmaxf(0.2f, fminf(1.0f, + 0.68f - 0.28f * (py / hh) - 0.12f * (px / hw))); + float bright = glare + rim * lightDir; if (bright > 0.0f) { fr += bright * (255.0f - fr); fg += bright * (255.0f - fg); @@ -301,7 +311,9 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, // soft dark band just inside the rim (glass depth), morph-only float esW = 0.13f * minhh; if (esW < 2.0f) esW = 2.0f; if (depth < esW) { - float es = (1.0f - depth / esW) * LENS_EDGE_SHADOW * glassAmt; + float shadowDir = fmaxf(0.1f, fminf(1.0f, + 0.55f + 0.35f * (py / hh) + 0.10f * (px / hw))); + float es = (1.0f - depth / esW) * LENS_EDGE_SHADOW * glassAmt * shadowDir; fr *= (1.0f - es); fg *= (1.0f - es); fb *= (1.0f - es); diff --git a/Ports/iOSPort/nativeSources/iOSModernTheme.res b/Ports/iOSPort/nativeSources/iOSModernTheme.res index 4909edc72674610ee440f9537426b3765e22daca..98a82ad9bcee356ca6517cacde6c6dcd7d67f5a6 100644 GIT binary patch delta 2172 zcmZWrdr*{B6yI}Q_W7~Ac3D{34Nw!4hd_kmOgwBRguw=Uq-ZEK=_rDVp@lD2qbX-p z#DjBPP~4`bFio`2`tlL0ljAcL#m47=nKq&bSW}}-YIk?VmMRTaD{<%l~gu;zXN(p;aoJ7aYc1mH50zz%%hWu}B~4E3ZR{MZ*aV zK3}F28-i8p@PM%oJs~27QBW3Yc!DGF@kdc@(-G~q0YUa%Ru zYakL^9Y!2%nK2Y%V;*K&n92ff*q*?wp)DpFlSTwjvfLdJV&C>TdjD+Z?lq#bEr_1) z1vOT*EgEW3iUVyD@w9rxo(R#ky8{AUqQs`JXNO?7OOGWTp*Z*vTcaP=i-R4b!p#vm zHRbP2nOj-3pt3Tza>@Mi{6#eil8L5^`6N9brkXG2>roZWQ;6)Z@VJy@e z>4@<#3d@o_J1$Ub2H1Tn$&~>`gig+cBwCUSf|NZDJ_fgW4DFZz0zI1nHYt2Oe5Ry> z6Ts@@iVp&gEQUbOOaP^3-%WLRh7evyf6|kA#`B?=}-d(A&3Oe}{Nu`V75|`<*YOvA#YFNcF?EIIXS?T^| zta3L|NIf-hG5}=3vPnwm=yI5><;^zOVJ8o=%g&yK%Z=+`o-F_IA?Pifx#&PETQbJl zo(Ynyt#HuHBVGCynms&ALnQVTtG@%ge2pjH!wEUs;X{xd%^`QY;2=-fd>IbQ?!{N& zo_{_bU1g1C^P#3c^|Kqzz6PT?_t6h~;Q+^{--lf?{%Rka46fhDFj?Smvk~3+3Nh3E zL{iJo@1?VIY%&R=tx2Q<98zgANd~XcKgbtSNF+U;LQa8$ZX8QK2?V#NGB9Hm2B!Kn z@&b#IkVfn_|GGtCaa%OKJC%gv>^2p>UPKB5jooe0Qcf{>S1#Q-h4}sq(0$X0Nt!i{ H*meH`^X5g> delta 1441 zcmZXTZBUd|6vy}6UEl%MW!YU>SeD&o(J9geTnN{f3Y00d)Ez~ovJ{o3WLZ`~(=Y^Q z8G6$J4su6%iy3LkG?^}Wofd^utgM_E#wA7w7$s{=)6y?CMRyna(DdOx_nhDV{LkFE z&pjg-!>{#*>t$q0CX`OgJ46V5%26(vW1DSgHU2zWQwEZ+75voCsM5R z#6pqkQZC?l%Kd5dY1%_TxxF18ZODk7*4>&=9I>~fH~rfHHPW*=ph9O&D0?SE(qVIM zEt|LE0H9^1p0*8x5{Fm*jn?O8Q}s@e*k8{MMZ#Q!=e6{5HRw^w_p-zJ=>)gG7{hM9 zPziWAU(G5v*;K51$1XM6D-MdK$FYj*V8(mNs>)cgz%NY3of0WAhJ;MhA(qu_3Utj>asA5P{4*C#Rsah_=lk zMht6m1g~p((|QHia3UcI`;RuyME8%zVrO%j+$J;N&K51+YCbisfJNkSIMHGkT5?cJ zZmpeh<+R4)gVr>Wm9)&pC2dNuvD~)uNbLXUpjdhoFAANYV3o%|Cd__v2n5yQ4zC(3 zk84@(si&f_=(IX=U7@RRTY2I3yn?N`rq_bIeE(&2zWJ!D*3d(niG)Xd+JFhKR;#J; zrg&niJ`;X*PK{60O894w7SEpBZLnl*b1$%Mea*dnaqtR6=M+zAVOd!c*8AeMrvJ+b zeHLL=3h zZv;JiFsvg?GqM+0rPAc7wVwO#!_Dt{w zGd__CPC`2u!5sebB0vaGQ$w198<)U$3c4!?bhy-}q?#=71T)^A1wR3uTm}Xz97^83 z46-30#Zuu>a%&D8d?Y0LX;(oxW{<_t!6K;RE*BsP(FY*iYz8Y&ErCwpqaL^dw7V3V z0v`h}E`uKk6_abku2o9DdpCR;lw;`kdqBar?18o6)K?A7eDeYL8MxC6TY>v(Ak09s z+d<2JY=?e5J;6o8KhB8D*mMS}2%r2M&I9l2fyd(L^{en1UF#P=VV_GFF8QH>@Pc8u z0etl}Sgod=x1fv4ZwqtDuh2+n#~o2-{tfzp-@Xgdli&!<1GT1-+L_RwN(R8eKTIPj zFoWcFG71hlVJC0RkfwApAa{t7v9%dQaZLuPGSS29h=y9$5f>LxB>@8#d=t+d8_3&0 RGxCWB-Cl{`*huz7`~#`59PI!A diff --git a/Themes/iOSModernTheme.res b/Themes/iOSModernTheme.res index 0b2d8c17a2df4cfeb96bb180f993ba27cf27e45f..98a82ad9bcee356ca6517cacde6c6dcd7d67f5a6 100644 GIT binary patch delta 423 zcmbPwob%2R&Iw{1oD32wE{R3ilP_KtS!mS2{QC9R$sg;*C;K#%a|78>+2H9L;+e!J zKW)gJY`=+XvPGlNkg{wAsDskl^HtlSL-WPH6Be$-9$zCjaYXo18ySX!<)#CZ5Tg=Zj2#X~iV8`TD#K@{ z*wkS1xl0nwe3!TLU1n5>Wt<7}#B?1CCcf#J227PuKEEN;MaG%i9~v_0F+zoGjG68- i&YUh~!qfp3Id1||G<~5dlMIj&G-ndoe#w;Sur2_h)~L7u delta 443 zcmca}gmda~&Iw|Sd>h538<@FT{!ji`FFrjlmWgL_L_;=M;8;Tsm^N&boSfIV5UNOc z`rPM??3;C(?h1lMc&1DO(VG`eabV;EG8p7lToQ}2r_WPil9|jt&0=yw70YJlX`w8P zx|1Ku>P$X5Yc`OTIJ<;dmqBWB;T++~uV=3Z%Nb2RJI4(ustFd=oZAi*-8i=dNaoKG zooqdihf#O4$2@z5$=TZkxPw!3Qj<$kQ$iAxCj0MKm|VIWRcyj;oyj7NLeu-=nK&lT z+${{!H2G?yz;v1Cj2x5s4#-VDu}@&KZ9B*0-+P27@7-4>rQnd9XQ-Q$nU`V(_5sjj zy@I0D;$r2=6L(7s%VA1SW;~z()G9E!en0Q#fc;!TlZ!99GnrU4@42{r&qYSdSVjww zm!~V~GqppRH%*!Nra#eVI?QOXeT4y&9wShk@u}`~79*xRj26=$8ZmVst6XBtbeP!! R7*4wLq<}hFO_;9h0sx+hsNMho diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/TabSelectionMorphTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/TabSelectionMorphTest.java index 16113d2c319..769f445a783 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/TabSelectionMorphTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/TabSelectionMorphTest.java @@ -34,6 +34,22 @@ */ class TabSelectionMorphTest { + @Test + void visibleFramePacingPreventsAOneTickJump() { + assertEquals(8, TabSelectionMorph.paceVisibleFrame(0, 8), + "normal 60fps progress remains unchanged"); + assertEquals(14, TabSelectionMorph.paceVisibleFrame(0, 100), + "a completed wall-clock motion still presents an intermediate frame"); + int visible = 0; + int frames = 0; + while (visible < 100) { + visible = TabSelectionMorph.paceVisibleFrame(visible, 100); + frames++; + } + assertEquals(8, frames, "a coalesced transition still paints a short frame sequence"); + assertEquals(100, visible); + } + // A cell that travels from x=0..w=100 to x=200..w=100, bar inner-x 10, pill y=50 h=40. private static final int FROM_X = 0, FROM_W = 100, TO_X = 200, TO_W = 100; private static final int INNER_X = 10, CAP_Y = 50, CAP_H = 40; @@ -120,8 +136,7 @@ void barGrowFiresEarlyThenStops() { * (0, 10, 25, 50, 75, 90, 100): pill rect, lens rect, magnify, aberration, * flight envelope and the bar-grow flag are pinned EXACTLY per frame. This is * the numeric ground truth the fidelity animation-frame captures validate - * against -- any change to the motion path, overshoot (see t=0.90: the pill - * passes the 220px target to 230px before settling), lens size or tint + * against -- any change to the measured monotonic motion path, lens size or tint * timing shows up here as a frame-value diff, not as a vague "feels wrong". */ @Test @@ -129,15 +144,11 @@ void frameTableAtFixedProgressPoints() { // {t, capX, capW, lensX, lensY, lensW, lensH, magnify, aberration, flight, barGrow} Object[][] frames = { {0.00f, 20, 80, 20, 50, 80, 40, 1.0800f, 0.0000f, 0.0000f, false}, - {0.10f, 32, 105, 32, 39, 105, 54, 1.1726f, 0.0185f, 0.9259f, true}, - {0.25f, 71, 105, 71, 37, 105, 56, 1.1800f, 0.0200f, 1.0000f, true}, - {0.50f, 164, 105, 164, 37, 105, 56, 1.1800f, 0.0200f, 1.0000f, false}, - {0.75f, 220, 90, 220, 41, 90, 55, 1.1300f, 0.0100f, 0.5000f, false}, - // t=0.9: the spring overshoots the target cell; the drop's right - // edge (230+98=328) would leave the bar (right edge 310), so the - // bar-bounds clamp trims the width -- the drop compresses against - // the bar end instead of painting over the backdrop. - {0.90f, 230, 80, 230, 49, 80, 42, 1.0800f, 0.0000f, 0.0000f, false}, + {0.10f, 42, 105, 42, 39, 105, 54, 1.1726f, 0.0185f, 0.9259f, true}, + {0.25f, 101, 105, 101, 37, 105, 56, 1.1800f, 0.0200f, 1.0000f, true}, + {0.50f, 178, 105, 178, 37, 105, 56, 1.1800f, 0.0200f, 1.0000f, false}, + {0.75f, 213, 90, 213, 41, 90, 55, 1.1300f, 0.0100f, 0.5000f, false}, + {0.90f, 221, 89, 221, 49, 89, 42, 1.0800f, 0.0000f, 0.0000f, false}, {1.00f, 220, 80, 220, 50, 80, 40, 1.0800f, 0.0000f, 0.0000f, false}, }; for (Object[] f : frames) { @@ -166,25 +177,24 @@ void frameTableAtFixedProgressPoints() { @Test void presetIos26MatchesShippedTuning() { TabSelectionMorph.Tokens tk = TabSelectionMorph.Tokens.preset("ios26"); - assertEquals(0.32f, tk.stretch, 1e-6); - assertEquals(0.16f, tk.squashW, 1e-6); - assertEquals(0.14f, tk.grow, 1e-6); - assertEquals(0.18f, tk.squashH, 1e-6); - assertEquals(0.5f, tk.liftMm, 1e-6); - // 109: the native selected pill is wider than its cell (overlaps the - // neighbours) -- measured 276px over a 253px cell on the @3x reference bar. - assertEquals(109, tk.bubbleWidthPct); - assertEquals(18, tk.overflowPct); - assertEquals(0.3f, tk.downBiasMm, 1e-6); - assertEquals(1.08f, tk.restMag, 1e-6); - assertEquals(1.18f, tk.peakMag, 1e-6); - assertEquals(0.02f, tk.peakAb, 1e-6); + assertEquals(0f, tk.stretch, 1e-6); + assertEquals(0.05f, tk.squashW, 1e-6); + assertEquals(0.01f, tk.grow, 1e-6); + assertEquals(0.04f, tk.squashH, 1e-6); + assertEquals(0.05f, tk.liftMm, 1e-6); + assertEquals(100, tk.bubbleWidthPct); + assertEquals(0, tk.overflowPct); + assertEquals(0f, tk.downBiasMm, 1e-6); + assertEquals(1.02f, tk.restMag, 1e-6); + assertEquals(1.08f, tk.peakMag, 1e-6); + assertEquals(0.0005f, tk.peakAb, 1e-6); assertEquals(1f, tk.tintStrength, 1e-6); assertEquals(0, tk.barGrowPct); - assertEquals(1f, tk.spring, 1e-6, "preset springiness is the reference bounce"); + assertEquals(0.35f, tk.spring, 1e-6, "native settle deformation is restrained"); // unknown names fall back to ios26, "subtle" is a calmer variant assertEquals(tk.stretch, TabSelectionMorph.Tokens.preset("nonsense").stretch, 1e-6); - assertTrue(TabSelectionMorph.Tokens.preset("subtle").stretch < tk.stretch); + assertEquals(0f, TabSelectionMorph.Tokens.preset("subtle").stretch, 1e-6, + "both native presets keep the travelling drop to one cell"); } @Test @@ -195,33 +205,91 @@ void lensIntensityScalesOpticsNotGeometry() { assertEquals(1f, tk.peakMag, 1e-6); assertEquals(0f, tk.peakAb, 1e-6); assertEquals(0f, tk.tintStrength, 1e-6); - assertEquals(0.32f, tk.stretch, 1e-6, "geometry untouched by lens intensity"); + assertEquals(0f, tk.stretch, 1e-6, "geometry untouched by lens intensity"); TabSelectionMorph.Tokens twice = TabSelectionMorph.Tokens.preset("ios26"); twice.scaleLensIntensity(2f); - assertEquals(1.36f, twice.peakMag, 1e-5, "intensity 2 doubles the magnify delta"); + assertEquals(1.16f, twice.peakMag, 1e-5, "intensity 2 doubles the magnify delta"); assertEquals(1f, twice.tintStrength, 1e-6, "tint strength clamps at 1"); } @Test - void springinessControlsOvershoot() { - // Sample mid-settle (t=0.9, inside the overshoot window) at three - // springiness values: no spring stops dead on an ease-out, more spring - // overshoots further. + void ios26PresetStaysWithinRecordedNativeDeformationBounds() { + TabSelectionMorph.Tokens tk = TabSelectionMorph.Tokens.preset("ios26"); + tk.liftPx = 1; + tk.downBiasPx = 0; + int maxWidth = 0; + int maxHeight = 0; + float maxAberration = 0f; + for (int frame = 0; frame <= 100; frame++) { + TabSelectionMorph m = TabSelectionMorph.compute(frame / 100f, + FROM_X, FROM_W, TO_X, TO_W, INNER_X, CAP_Y, CAP_H, + BAR_LEFT, BAR_RIGHT, tk); + maxWidth = Math.max(maxWidth, m.lensW); + maxHeight = Math.max(maxHeight, m.lensH); + maxAberration = Math.max(maxAberration, m.aberration); + } + assertTrue(maxWidth <= 100, + "native drop stays at one cell; it must not engulf two tab glyphs"); + assertTrue(maxHeight <= 44, + "native drop only subtly bulges beyond the 40px bar"); + assertTrue(maxAberration <= 0.00051f, + "native chromatic separation is a rim detail, not a visible double image"); + } + + @Test + void ios26PresetFrameTableMatchesRecordedTuning() { + TabSelectionMorph.Tokens tk = TabSelectionMorph.Tokens.preset("ios26"); + tk.liftPx = 1; + tk.downBiasPx = 0; + // {time, capX, capW, lensY, lensW, lensH, magnify, aberration, flight} + Object[][] frames = { + {0.00f, 10, 100, 50, 100, 40, 1.0200000f, 0.0000000f, 0.0000000f}, + {0.10f, 29, 100, 50, 100, 40, 1.0755556f, 0.0004630f, 0.9259260f}, + {0.25f, 88, 100, 49, 100, 40, 1.0800000f, 0.0005000f, 1.0000000f}, + {0.50f, 165, 100, 49, 100, 40, 1.0800000f, 0.0005000f, 1.0000000f}, + {0.75f, 202, 98, 50, 98, 40, 1.0500000f, 0.0002500f, 0.5000000f}, + {0.90f, 209, 99, 50, 99, 40, 1.0200000f, 0.0000000f, 0.0000000f}, + {1.00f, 210, 100, 50, 100, 40, 1.0200000f, 0.0000000f, 0.0000000f} + }; + for (Object[] frame : frames) { + float time = (Float) frame[0]; + TabSelectionMorph m = TabSelectionMorph.compute(time, + FROM_X, FROM_W, TO_X, TO_W, INNER_X, CAP_Y, CAP_H, + BAR_LEFT, BAR_RIGHT, tk); + String at = "t=" + time + " "; + assertEquals(((Integer) frame[1]).intValue(), m.capX, at + "capX"); + assertEquals(((Integer) frame[2]).intValue(), m.capW, at + "capW"); + assertEquals(((Integer) frame[3]).intValue(), m.lensY, at + "lensY"); + assertEquals(((Integer) frame[4]).intValue(), m.lensW, at + "lensW"); + assertEquals(((Integer) frame[5]).intValue(), m.lensH, at + "lensH"); + assertEquals((Float) frame[6], m.magnify, 5e-7f, at + "magnify"); + assertEquals((Float) frame[7], m.aberration, 5e-7f, at + "aberration"); + assertEquals((Float) frame[8], m.flight, 5e-7f, at + "flight"); + } + } + + @Test + void springinessControlsStopDeformationWithoutPositionOvershoot() { + // Sample the stop envelope at three springiness values. The native + // centre path remains monotonic; spring changes only the liquid + // width-compression/height-swell at the stop. TabSelectionMorph.Tokens none = tokens(); none.spring = 0f; TabSelectionMorph.Tokens strong = tokens(); strong.spring = 2f; - int xNone = TabSelectionMorph.compute(0.9f, FROM_X, FROM_W, TO_X, TO_W, - INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, none).capX; - int xRef = at(0.9f).capX; - int xStrong = TabSelectionMorph.compute(0.9f, FROM_X, FROM_W, TO_X, TO_W, - INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, strong).capX; - assertTrue(xNone < xRef, "reference bounce overshoots past the springless stop"); - assertTrue(xRef < xStrong, "stronger springiness overshoots further"); + TabSelectionMorph flat = TabSelectionMorph.compute(0.8f, FROM_X, FROM_W, TO_X, TO_W, + INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, none); + TabSelectionMorph reference = at(0.8f); + TabSelectionMorph elastic = TabSelectionMorph.compute(0.8f, FROM_X, FROM_W, TO_X, TO_W, + INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, strong); + assertTrue(flat.lensW > reference.lensW && reference.lensW > elastic.lensW, + "stronger spring compresses the stop width further"); + assertTrue(flat.lensH < reference.lensH && reference.lensH < elastic.lensH, + "stronger spring swells the stop height further"); assertEquals(at(1f).capX, TabSelectionMorph.compute(1f, FROM_X, FROM_W, TO_X, TO_W, INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, strong).capX, - "every springiness settles on the same target"); + "every springiness settles on the same target without overshoot"); } @Test @@ -237,7 +305,7 @@ void isDeterministic() { /** * A drop wider than its cell (bubbleWidthPct > 100, the shipped ios26 - * geometry) travelling to an END cell, with spring overshoot, must never + * geometry) travelling to an END cell must never * paint outside the bar: the native pill overlaps neighbour cells, not * the backdrop past the bar's rounded ends. */ diff --git a/native-themes/ios-modern/theme.css b/native-themes/ios-modern/theme.css index 98034faef99..582b2ce3ba9 100644 --- a/native-themes/ios-modern/theme.css +++ b/native-themes/ios-modern/theme.css @@ -96,7 +96,7 @@ controls: tabsMorphPreset "ios26" (default) | "subtle" tabsMorphLensIntensityPct scales the lens optics around 100 - tabsMorphSpringPct scales the settle overshoot around 100 + tabsMorphSpringPct scales the settle deformation around 100 tabsAnimatedIndicatorDurationInt the morph duration (ms), below */ tabsMorphPreset: "ios26"; /* Pressed = the SUSTAINED held-down look: iOS 26 dims a Liquid Glass button @@ -161,11 +161,18 @@ underline that tweens between tabs when the user changes selection. Off by default in the framework (so existing apps don't suddenly animate); on by default in the modern native - theme. Duration 200ms matches Material 3's reference. */ + theme. */ /* iOS 26 tab bars use a sliding CAPSULE selection indicator, not the Material 3 underline -- so the underline is off and the capsule is on. */ tabsAnimatedIndicatorBool: false; - tabsAnimatedIndicatorDurationInt: 480; + /* The native last-to-first travel reaches its final resting x at ~350ms. + 200ms made JavaSE/JavaScript burn through the localized bubble motion so + quickly that it read as a jump. */ + tabsAnimatedIndicatorDurationInt: 350; + /* Native flat-backdrop samples: outer bar #cd, settled selection #b4 over + a #808080 backdrop. 72 alpha with the system-fill selection colour lands + on that measured contrast instead of an almost invisible inner pill. */ + tabSelPillAlphaInt: 72; tabsAnimatedIndicatorThicknessMm: 1; /* iOS 26 sliding selection capsule: a single Liquid Glass blob behind the selected tab that tweens between tabs on selection change. Replaces the @@ -691,10 +698,10 @@ TabsContainer { /* Floating WHITE glass capsule (native iOS 26 bar): a translucent white fill over the blurred backdrop so it reads as a distinct white pill even over light-grey content, not a near-invisible frost. The blur still lets colour bleed through. */ - /* Sampled against the golden over the shared backdrop: the native pill is - only ~22% white over a LIGHTLY blurred local backdrop (yellow stays - yellow); 0.60/blur40 washed and cross-mixed the colours. */ - background-color: rgba(255,255,255,0.22); + /* Sampled against the animation's flat #808080 stage: the native pill is + #cd, which is a 60% white composite. Keep the blur local so textured + backdrops retain their colour instead of cross-mixing adjacent regions. */ + background-color: rgba(255,255,255,0.60); color: #3c3c43; /* Tight local blur: 24px still dragged neighbouring backdrop colour across the pill (blue bleeding into the warm left gap); native frost stays local. */ @@ -1146,29 +1153,27 @@ PopupContent { Tabs { background-color: transparent; } TabbedPane { background-color: transparent; } - /* Dark glass tab pill: translucent so the backdrop reads through like the - native Liquid Glass bar (0.55 was too opaque -> flat grey vs native's tint; - fully transparent left NO visible pill on plain-blur ports and even on - Metal read lighter than the native bar's slight dark wash). */ - TabsContainer { background-color: rgba(22,22,26,0.18); color: #ebebf5; cn1-background-type: cn1-pill-border; backdrop-filter: blur(40px); border: none; } + /* Dark flat-backdrop sample is #58 over #808080, matching a roughly 38% + composite of the #16161a bar colour. */ + TabsContainer { background-color: rgba(22,22,26,0.38); color: #ebebf5; cn1-background-type: cn1-pill-border; backdrop-filter: blur(40px); border: none; } Tab { color: #ebebf5; background-color: transparent; } /* Dark bottom tab bar: the selected item sits on a lighter frosted sub-capsule over the dark pill (layered glass), matching the native UITabBar. */ /* No per-tab background -- the sliding glass capsule is the only selection visual (a cn1-pill-border here double-painted a brighter inner pill / dark ring). */ - Tab.selected { color: var(--accent-color-dark, #0a84ff); background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } - Tab.pressed { color: var(--accent-color-dark, #0a84ff); background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } - SelectedTab { color: var(--accent-color-dark, #0a84ff); background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } + Tab.selected { color: #ebebf5; background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } + Tab.pressed { color: #ebebf5; background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } + SelectedTab { color: #ebebf5; background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } UnselectedTab { color: #ebebf5; } /* Dark overrides for the icon-only UIID. Background stays transparent so the pill shows through; only the glyph (fgColor) is forced white. */ TabIcon { color: #ffffff; background-color: transparent; } - /* With the dark lens tint disabled (it flooded the dark bar solid blue) - the selected glyph carries the accent itself, like the native dark bar. */ - TabIcon.selected { color: #0a84ff; background-color: transparent; } - TabIcon.pressed { color: #0a84ff; background-color: transparent; } + /* The lens light-luminance key colours these white glyphs blue only while + the travelling drop covers them; the destination does not turn blue early. */ + TabIcon.selected { color: #ffffff; background-color: transparent; } + TabIcon.pressed { color: #ffffff; background-color: transparent; } /* Dark glass panel: a translucent dark tint so the backdrop reads through as Liquid Glass (matches the dark UIVisualEffectView in the isolation tests). */ diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index 9482f98386a..f43afa00217 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -151,6 +151,7 @@ private static int testTimeoutMs(BaseTest testClass) { new MorphTransitionScrubScreenshotTest(), new MorphElementMorphScreenshotTest(), new TabsAnimatedIndicatorScreenshotTest(), + new TabsLiquidGlassAnimationScreenshotTest(), new PullToRefreshSpinnerScreenshotTest(), new AnimateLayoutScreenshotTest(), new AnimateHierarchyScreenshotTest(), diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/TabsLiquidGlassAnimationScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/TabsLiquidGlassAnimationScreenshotTest.java new file mode 100644 index 00000000000..28414fce920 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/TabsLiquidGlassAnimationScreenshotTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2026, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.io.Util; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Display; +import com.codename1.ui.FontImage; +import com.codename1.ui.Form; +import com.codename1.ui.Graphics; +import com.codename1.ui.Image; +import com.codename1.ui.Tabs; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.plaf.Style; +import com.codename1.ui.plaf.UIManager; +import com.codename1.ui.util.Resources; + +import java.io.IOException; +import java.io.InputStream; + +/// Mac JavaSE frame probe for the iOS 26 UITabBar Liquid Glass selection morph. +/// +/// The native reference video is 1178x2556. Its 60mm x 16mm tab tile is the +/// centred 1088x290 crop at (45,1133). This test renders that exact tile size and +/// emits every distinct frame from the first complete native last-to-first +/// transition, including the long ease-out/settle tail, rather than scaling six +/// samples into a contact sheet. +/// +/// Native source frames (seconds): +/// 1.163333 through 1.760000. Relative times are normalized to the recorded +/// 350ms travel; the remaining frames deliberately hold the settled state so a +/// lingering optical/shape effect cannot disappear from the comparison. Each +/// emitted PNG remains full resolution so magnification, refraction, rim light, +/// glyph lift, and antialiasing can be compared one frame at a time. +public class TabsLiquidGlassAnimationScreenshotTest extends BaseTest { + private static final int TILE_WIDTH = 1088; + private static final int TILE_HEIGHT = 290; + private static final int[] FRAME_MS = { + 0, 17, 35, 53, 70, 88, 103, 118, + 137, 155, 170, 188, 202, 220, 237, 253, + 268, 287, 302, 320, 335, 353, 370, 387, + 402, 457, 468, 487, 522, 538, 553, 597 + }; + private static final int[] FRAME_PROGRESS = { + 0, 5, 10, 15, 20, 25, 29, 34, + 39, 44, 49, 54, 58, 63, 68, 72, + 77, 82, 86, 91, 96, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100 + }; + + private Form layoutHost; + private Container tile; + private Tabs tabs; + + @Override + public boolean runTest() { + if (!"SE".equals(Display.getInstance().getProperty("OS", ""))) { + System.out.println("CN1SS:INFO:test=TabsLiquidGlassAnimation" + + " status=SKIPPED reason=mac-javase-reference-only platform=" + + Display.getInstance().getPlatformName()); + done(); + return true; + } + if (!installIosModernTheme()) { + fail("Unable to load /iOSModernTheme.res for the Liquid Glass frame probe"); + return false; + } + System.out.println("CN1SS:INFO:test=TabsLiquidGlassAnimation" + + " display=" + Display.getInstance().getDisplayWidth() + "x" + + Display.getInstance().getDisplayHeight() + + " pxPerMm=" + Display.getInstance().convertToPixels(1f) + + " platform=" + Display.getInstance().getPlatformName()); + markCaptureStarted(); + renderAppearance(false, "light", 0); + return true; + } + + private void renderAppearance(boolean dark, String appearance, int frameIndex) { + if (frameIndex == 0) { + Display.getInstance().setDarkMode(Boolean.valueOf(dark)); + UIManager.getInstance().refreshTheme(); + buildTile(dark); + } + if (frameIndex >= FRAME_PROGRESS.length) { + if (!dark) { + renderAppearance(true, "dark", 0); + } else { + restoreAppTheme(); + done(); + } + return; + } + + int progress = FRAME_PROGRESS[frameIndex]; + tabs.setMorphTestState(2, 0, progress); + Image frame = Image.createImage(TILE_WIDTH, TILE_HEIGHT, 0xff808080); + Graphics g = frame.getGraphics(); + tile.paintComponent(g, true); + String name = "TabsLiquidGlassAnimation_" + appearance + + "_ms" + pad3(FRAME_MS[frameIndex]) + + "_p" + pad3(progress); + final int next = frameIndex + 1; + Cn1ssDeviceRunnerHelper.emitImage(frame, name, + () -> renderAppearance(dark, appearance, next)); + } + + private void buildTile(boolean dark) { + // No title bar: the native reference crop begins at UITabBar y=0. + // A titled offscreen Form shifts its content pane down by the title's + // 104px preferred height even though only the tile itself is painted. + layoutHost = new Form(new BorderLayout()); + layoutHost.setWidth(TILE_WIDTH); + layoutHost.setHeight(TILE_HEIGHT); + layoutHost.setVisible(true); + + // Match the native UITabBar's component frame, not the JavaSE Tabs + // preferred size. The visible floating pill is inset by the iOS theme, + // but UIKit gives the tab bar the full 60mm x 16mm capture tile. Letting + // FlowLayout shrink Tabs to JavaSE's preferred size produced a 260px toy + // pill and made every lens comparison meaningless. + tile = new Container(new BorderLayout()); + Style tileStyle = tile.getAllStyles(); + tileStyle.setBgColor(0x808080); + tileStyle.setBgTransparency(255); + tileStyle.setPadding(0, 0, 0, 0); + tileStyle.setMargin(0, 0, 0, 0); + + tabs = new Tabs(Component.TOP); + tabs.setTabTextPosition(Component.BOTTOM); + Style iconStyle = new Style(); + iconStyle.setFgColor(dark ? 0xebebf5 : 0x2c2c2e); + iconStyle.setBgTransparency(0); + Image star = FontImage.createSFOrMaterial(FontImage.MATERIAL_STAR, iconStyle, 4.1f); + Image search = FontImage.createSFOrMaterial(FontImage.MATERIAL_SEARCH, iconStyle, 4.1f); + Image more = FontImage.createSFOrMaterial(FontImage.MATERIAL_MORE_HORIZ, iconStyle, 4.1f); + tabs.addTab("Featured", star, star, new Container()); + tabs.addTab("Search", search, search, new Container()); + tabs.addTab("More", more, more, new Container()); + tabs.setSelectedIndex(0); + + // UIKit's bar frame starts at the top edge of this crop. NORTH keeps + // the production Tabs preferred height while preserving the full tile + // width, which lands the themed pill at the native y=0 position. + tile.add(BorderLayout.NORTH, tabs); + layoutHost.add(BorderLayout.CENTER, tile); + layoutHost.layoutContainer(); + tile.layoutContainer(); + tabs.layoutContainer(); + tabs.getTabsContainer().layoutContainer(); + System.out.println("CN1SS:INFO:test=TabsLiquidGlassAnimation geometry=" + + "tile=" + tile.getX() + "," + tile.getY() + "," + + tile.getWidth() + "x" + tile.getHeight() + + " tabs=" + tabs.getX() + "," + tabs.getY() + "," + + tabs.getWidth() + "x" + tabs.getHeight() + + " bar=" + tabs.getTabsContainer().getX() + "," + + tabs.getTabsContainer().getY() + "," + + tabs.getTabsContainer().getWidth() + "x" + + tabs.getTabsContainer().getHeight() + + " tab0=" + tabs.getTabsContainer().getComponentAt(0).getX() + "," + + tabs.getTabsContainer().getComponentAt(0).getY() + "," + + tabs.getTabsContainer().getComponentAt(0).getWidth() + "x" + + tabs.getTabsContainer().getComponentAt(0).getHeight()); + } + + private boolean installIosModernTheme() { + InputStream in = Display.getInstance().getResourceAsStream( + TabsLiquidGlassAnimationScreenshotTest.class, "/iOSModernTheme.res"); + if (in == null) { + in = TabsLiquidGlassAnimationScreenshotTest.class.getResourceAsStream("/iOSModernTheme.res"); + } + if (in == null) { + return false; + } + try { + Resources resources = Resources.open(in); + String[] names = resources.getThemeResourceNames(); + if (names == null || names.length == 0) { + return false; + } + UIManager.getInstance().setThemeProps(resources.getTheme(names[0])); + UIManager.getInstance().refreshTheme(); + return UIManager.getInstance().isThemeConstant("glassMaterialBool", false) + && UIManager.getInstance().isThemeConstant("tabsSelectionCapsuleBool", false); + } catch (IOException ex) { + System.out.println("CN1SS:ERR:test=TabsLiquidGlassAnimation theme=" + ex); + return false; + } finally { + Util.cleanup(in); + } + } + + private void restoreAppTheme() { + tabs.setMorphTestState(2, 0, -1); + layoutHost = null; + tile = null; + tabs = null; + Display.getInstance().setDarkMode(null); + UIManager.initFirstTheme("/theme"); + UIManager.getInstance().refreshTheme(); + } + + private static String pad3(int value) { + if (value < 10) { + return "00" + value; + } + if (value < 100) { + return "0" + value; + } + return String.valueOf(value); + } +} diff --git a/scripts/verify-javascript-lens-parity.mjs b/scripts/verify-javascript-lens-parity.mjs index 41fc4a218cb..890df149fa7 100644 --- a/scripts/verify-javascript-lens-parity.mjs +++ b/scripts/verify-javascript-lens-parity.mjs @@ -8,6 +8,12 @@ import vm from 'node:vm'; // drift changes the checksum. const bridgePath = new URL('../vm/ByteCodeTranslator/src/javascript/browser_bridge.js', import.meta.url); const bridge = fs.readFileSync(bridgePath, 'utf8'); +const javaSource = fs.readFileSync(new URL( + '../Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java', import.meta.url), 'utf8'); +const metalReference = fs.readFileSync(new URL( + '../Ports/iOSPort/nativeSources/METALView.m', import.meta.url), 'utf8'); +const metalShader = fs.readFileSync(new URL( + '../Ports/iOSPort/nativeSources/CN1MetalShaders.metal', import.meta.url), 'utf8'); const start = bridge.indexOf(' var LENS_MAG_FLAT ='); const end = bridge.indexOf(' // Replay one command stream', start); if (start < 0 || end < 0) { @@ -19,6 +25,40 @@ vm.runInNewContext(bridge.substring(start, end) + '\nthis.applyMaterial = glassMaterialInPlace;' + '\nthis.applyOptics = applyGlassOptics;', sandbox); +// The same foreground lens is implemented four times because each backend has +// a different pixel API. Fail before the CRC probe if a tuning constant drifts. +const lensConstants = [ + 'LENS_MAG_FLAT', 'LENS_TINT_HI', 'LENS_TINT_LO', + 'LENS_LIGHT_KEY_LO', 'LENS_LIGHT_KEY_HI', 'LENS_LIFT_COEF', + 'LENS_GLARE', 'LENS_RIM', 'LENS_RIM_W', 'LENS_REFRACT', + 'LENS_EDGE_SHADOW', 'LENS_RIM_SCALE', 'LENS_GLASS_TINT_STR', + 'LENS_SAT_BOOST', 'LENS_GLASS_START', 'LENS_GLASS_FULL' +]; + +function constant(source, pattern, name, backend) { + const match = source.match(new RegExp(pattern.replace('%s', name))); + if (!match) { + throw new Error(`Missing ${name} in ${backend} lens implementation`); + } + return Number(match[1]); +} + +for (const name of lensConstants) { + const values = { + javascript: constant(bridge, `var\\s+%s\\s*=\\s*([0-9.]+)\\s*;`, name, 'JavaScript'), + javase: constant(javaSource, + `private\\s+static\\s+final\\s+double\\s+%s\\s*=\\s*([0-9.]+)\\s*;`, name, 'JavaSE'), + metalReference: constant(metalReference, + `#define\\s+%s\\s+([0-9.]+)f`, name, 'Metal CPU reference'), + metalShader: constant(metalShader, + `constant\\s+float\\s+%s\\s*=\\s*([0-9.]+)\\s*;`, name, 'Metal shader') + }; + if (new Set(Object.values(values)).size !== 1) { + throw new Error(`Lens constant drift for ${name}: ${JSON.stringify(values)}`); + } +} +console.log(`lens constant parity PASS backends=4 constants=${lensConstants.length}`); + function crc32(bytes) { let crc = 0xffffffff; for (const value of bytes) { @@ -56,13 +96,15 @@ function glassPattern(width, height) { } const frames = [ - { progress: 0, width: 80, height: 40, magnify: 1.08, aberration: 0, expected: 0x9d4a06ea }, - { progress: 10, width: 105, height: 54, magnify: 1.1726, aberration: 0.0185, expected: 0x66a714e8 }, - { progress: 25, width: 105, height: 56, magnify: 1.18, aberration: 0.02, expected: 0x25e7edb0 }, - { progress: 50, width: 105, height: 56, magnify: 1.18, aberration: 0.02, expected: 0x25e7edb0 }, - { progress: 75, width: 90, height: 55, magnify: 1.13, aberration: 0.01, expected: 0x471ccf1d }, - { progress: 90, width: 80, height: 42, magnify: 1.08, aberration: 0, expected: 0x0f8060ba }, - { progress: 100, width: 80, height: 40, magnify: 1.08, aberration: 0, expected: 0x9d4a06ea } + { progress: 0, width: 100, height: 40, magnify: 1.02, aberration: 0, expected: 0x54b0a28a }, + { progress: 10, width: 100, height: 40, magnify: 1.0755556, aberration: 0.000463, expected: 0x0cb3f189 }, + { progress: 25, width: 100, height: 40, magnify: 1.08, aberration: 0.0005, expected: 0xbb9b8ae3 }, + { progress: 50, width: 100, height: 40, magnify: 1.08, aberration: 0.0005, expected: 0xbb9b8ae3 }, + { progress: 75, width: 98, height: 40, magnify: 1.05, aberration: 0.00025, expected: 0x3a6519ac }, + { progress: 90, width: 99, height: 40, magnify: 1.02, aberration: 0, expected: 0x70153f60 }, + { progress: 100, width: 100, height: 40, magnify: 1.02, aberration: 0, expected: 0x54b0a28a }, + { progress: 'dark-25', width: 100, height: 40, magnify: 1.08, + aberration: 0.0005, tintStrength: -1, expected: 0x045fe638 } ]; for (const frame of frames) { @@ -86,15 +128,17 @@ for (const frame of frames) { putImageData(image) { output = image.data; } }; sandbox.applyLens(context, 0, 0, width, height, -1, - frame.magnify, frame.aberration, 0x0a84ff, 1); + frame.magnify, frame.aberration, 0x0a84ff, + frame.tintStrength === undefined ? 1 : frame.tintStrength); // JavaSE's reference buffer is packed ARGB; Canvas ImageData is RGBA. const actual = crc32(rgbaToArgb(output)); - if (actual !== frame.expected) { + if (actual !== frame.expected && process.env.CN1_UPDATE_LENS_PARITY !== '1') { throw new Error(`Lens parity failed at ${frame.progress}%: ` + `expected ${frame.expected.toString(16)}, got ${actual.toString(16)}`); } - console.log(`lens parity PASS t=${frame.progress}% size=${width}x${height} crc=${actual.toString(16)}`); + const status = process.env.CN1_UPDATE_LENS_PARITY === '1' ? 'UPDATE' : 'PASS'; + console.log(`lens parity ${status} t=${frame.progress}% size=${width}x${height} crc=${actual.toString(16)}`); } for (const recipe of [ diff --git a/vm/ByteCodeTranslator/src/javascript/browser_bridge.js b/vm/ByteCodeTranslator/src/javascript/browser_bridge.js index ca22ecb6d97..9828c9c1327 100644 --- a/vm/ByteCodeTranslator/src/javascript/browser_bridge.js +++ b/vm/ByteCodeTranslator/src/javascript/browser_bridge.js @@ -1008,16 +1008,20 @@ var LENS_MAG_FLAT = 0.75; var LENS_TINT_HI = 150; var LENS_TINT_LO = 55; - var LENS_LIFT_COEF = 0.40; - var LENS_GLARE = 0.09; - var LENS_RIM = 0.06; - var LENS_RIM_W = 0.06; - var LENS_REFRACT = 0.16; - var LENS_EDGE_SHADOW = 0.12; - var LENS_RIM_SCALE = 0.84; + var LENS_LIGHT_KEY_LO = 150; + var LENS_LIGHT_KEY_HI = 220; + var LENS_LIFT_COEF = 0.10; + var LENS_GLARE = 0.07; + var LENS_RIM = 0.11; + var LENS_RIM_W = 0.10; + var LENS_REFRACT = 0.015; + var LENS_EDGE_SHADOW = 0.07; + var LENS_RIM_SCALE = 1.0; var LENS_GLASS_TINT = 0xbcd8ff; - var LENS_GLASS_TINT_STR = 0.10; - var LENS_SAT_BOOST = 1.32; + var LENS_GLASS_TINT_STR = 0.0; + var LENS_SAT_BOOST = 1.12; + var LENS_GLASS_START = 1.025; + var LENS_GLASS_FULL = 1.075; function lensSmoothstep(a, b, x) { var t = (x - a) / (b - a); @@ -1099,7 +1103,9 @@ var tg = (tintColor >> 8) & 0xff; var tb = tintColor & 0xff; var liftMax = LENS_LIFT_COEF * (magnify - 1.0) * hh; - var glassAmount = lensSmoothstep(1.085, 1.25, magnify); + // The bounded tab morph rests at 1.02x and peaks at 1.08x. Fade the + // foreground optics in above rest and reach full liquid character in flight. + var glassAmount = lensSmoothstep(LENS_GLASS_START, LENS_GLASS_FULL, magnify); for (var yy = 0; yy < rh; yy++) { var py = yy + 0.5 - hh; @@ -1139,7 +1145,9 @@ var sg = lensSample(src, rw, rh, hw + (px / mag) * refract, sampleYG, 1); var sb = lensSample(src, rw, rh, hw + (px / magB) * refract, sampleYB, 2); var lum = 0.2126 * sr + 0.7152 * sg + 0.0722 * sb; - var tint = tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + var tint = tintStrength < 0 + ? -tintStrength * lensSmoothstep(LENS_LIGHT_KEY_LO, LENS_LIGHT_KEY_HI, lum) + : tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); var fr = sr + (tr - sr) * tint; var fg = sg + (tg - sg) * tint; var fb = sb + (tb - sb) * tint; @@ -1154,8 +1162,11 @@ var gx = px / hw, gy = (py + 0.42 * hh) / hh; var glare = LENS_GLARE * glassAmount * Math.exp(-(gx * gx * 1.15 + gy * gy * 2.6) * 2.1); var rimWidth = Math.max(2.0, LENS_RIM_W * hh); - var rim = depth < rimWidth ? (1.0 - depth / rimWidth) * LENS_RIM : 0; - var bright = glare + rim; + var rim = depth < rimWidth ? (1.0 - depth / rimWidth) * LENS_RIM + * (0.35 + 0.65 * glassAmount) : 0; + var lightDirection = Math.max(0.2, Math.min(1.0, + 0.68 - 0.28 * (py / hh) - 0.12 * (px / hw))); + var bright = glare + rim * lightDirection; if (bright > 0) { fr += bright * (255 - fr); fg += bright * (255 - fg); @@ -1163,7 +1174,10 @@ } var edgeShadowWidth = Math.max(2.0, 0.13 * Math.min(hw, hh)); if (depth < edgeShadowWidth) { - var edgeShadow = (1.0 - depth / edgeShadowWidth) * LENS_EDGE_SHADOW * glassAmount; + var shadowDirection = Math.max(0.1, Math.min(1.0, + 0.55 + 0.35 * (py / hh) + 0.10 * (px / hw))); + var edgeShadow = (1.0 - depth / edgeShadowWidth) * LENS_EDGE_SHADOW + * glassAmount * shadowDirection; fr *= 1 - edgeShadow; fg *= 1 - edgeShadow; fb *= 1 - edgeShadow; From ecc4c0e65c61b63ac59cc3054f6b4a9b72bcc1eb Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 16 Jul 2026 11:40:21 +0300 Subject: [PATCH 04/13] Restore the blessed frosted-bubble morph; fix the real sim gap (glassRegion) The previous commits re-derived the whole material+motion model from the committed grey-backdrop phone recording, which flattened the effect on every port including iOS -- diverging from the doc-blessed fidelity target and the committed ios-26-metal-frames goldens. Revert the shared model (TabSelectionMorph tokens/curve), Tabs paint behaviour, the Metal shader and CPU reference constants, and the iOS-modern theme back to master's tuning. The actual reason the simulator looked wrong was never the model: JavaSE had NO glassRegion implementation, so the bar's Liquid Glass material fell back to a plain blur. The lens then sampled a flat grey bar instead of the bright frost, and every in-flight frame read as a murky smudge. Implement JavaSEPort.glassRegion as a step-for-step mirror of IOSImplementation's offscreen branch (padded edge-replicated backdrop, affine colour material, Gaussian blur, SDF optics: edge refraction + specular rim + AA mask). With it the JavaSE render of TabsMorph_t050 matches the committed Metal golden to ~1 LSB at matched anatomy (bar 240/240/240, bubble interior within 2). Keep the good parts of this PR: the JavaScript port's real per-pixel lens and full glass recipe (replacing the uniform-zoom fallback), re-pinned to master's constants and bit-identical to JavaSE at all seven probe points; the four-backend constant/CRC parity script (regenerated against JavaSEPort.applyLensBuffer); the deterministic JavaSE frame probe; and the opt-in cn1.javase.pixelMilliRatio density override for reference-resolution captures. Co-Authored-By: Claude Fable 5 --- .../src/com/codename1/ui/Graphics.java | 8 +- .../com/codename1/ui/TabSelectionMorph.java | 126 +++------- CodenameOne/src/com/codename1/ui/Tabs.java | 68 +++-- .../com/codename1/impl/javase/JavaSEPort.java | 238 +++++++++++++++--- .../nativeSources/CN1MetalShaders.metal | 37 ++- Ports/iOSPort/nativeSources/METALView.m | 46 ++-- .../iOSPort/nativeSources/iOSModernTheme.res | Bin 156252 -> 155611 bytes Themes/iOSModernTheme.res | Bin 156252 -> 156565 bytes .../codename1/ui/TabSelectionMorphTest.java | 150 +++-------- native-themes/ios-modern/theme.css | 43 ++-- scripts/verify-javascript-lens-parity.mjs | 48 ++-- .../src/javascript/browser_bridge.js | 45 ++-- 12 files changed, 414 insertions(+), 395 deletions(-) diff --git a/CodenameOne/src/com/codename1/ui/Graphics.java b/CodenameOne/src/com/codename1/ui/Graphics.java index b37d540a29a..388a8dd793a 100644 --- a/CodenameOne/src/com/codename1/ui/Graphics.java +++ b/CodenameOne/src/com/codename1/ui/Graphics.java @@ -1526,11 +1526,9 @@ public boolean glassRegion(int x, int y, int width, int height, float radius, fl /// Applies the iOS 26 selection "drop" LENS to the contents already painted into /// the region (the bar + glyphs UNDER it): radial magnification, edge chromatic - /// aberration, and a luminance-keyed accent tint so glyphs read in the accent - /// colour only where the lens covers them. Positive tintStrength keys dark - /// glyphs on a light surface; negative tintStrength keys light glyphs on a dark - /// surface. Unlike glassRegion this is drawn OVER the content. - /// cornerRadius<0 = capsule. tintColor is 0xRRGGBB. + /// aberration, and a luminance-keyed dark->accent tint so dark glyphs read in + /// the accent colour only where the lens covers them. Unlike glassRegion this is + /// drawn OVER the content. cornerRadius<0 = capsule. tintColor is 0xRRGGBB. public boolean lensRegion(int x, int y, int width, int height, float cornerRadius, float magnify, float aberration, int tintColor, float tintStrength) { if (width <= 0 || height <= 0) { return true; diff --git a/CodenameOne/src/com/codename1/ui/TabSelectionMorph.java b/CodenameOne/src/com/codename1/ui/TabSelectionMorph.java index 53f680d3a6c..e48edd97779 100644 --- a/CodenameOne/src/com/codename1/ui/TabSelectionMorph.java +++ b/CodenameOne/src/com/codename1/ui/TabSelectionMorph.java @@ -36,19 +36,6 @@ /// the tokens + geometry from the theme/component and then paints from the returned model. final class TabSelectionMorph { - // At 60fps the 350ms native transition advances about 5% per frame. JavaSE can - // occasionally coalesce repaints and hand the next animation tick a completed - // wall-clock Motion; cap the visible jump so at least the characteristic liquid - // frames are painted instead of snapping directly from 0 to 100. - private static final int MAX_VISIBLE_FRAME_STEP = 14; - - static int paceVisibleFrame(int previous, int wallClockValue) { - int prior = previous < 0 ? 0 : (previous > 100 ? 100 : previous); - int raw = wallClockValue < prior ? prior : (wallClockValue > 100 ? 100 : wallClockValue); - int capped = prior + MAX_VISIBLE_FRAME_STEP; - return raw > capped ? capped : raw; - } - /// The morph's tuning tokens. Themes do not set these individually: a NAMED /// PRESET ({@link #preset}) supplies the full envelope set, and the theme /// exposes only high-level controls -- the preset name (tabsMorphPreset), @@ -71,54 +58,45 @@ static final class Tokens { float peakAb; // mid-flight chromatic aberration (fraction) float tintStrength; // lens accent-tint strength 0..1 int barGrowPct; // whole-bar grow pulse percent (0 = off) - float spring = 1f; // settle-deformation scale (0 = no stop squash/swell) + float spring = 1f; // settle-overshoot scale (1 = preset amount, 0 = none) /// The named motion presets. "ios26" (the default, and any unknown name) - /// is bounded from the recorded iOS 26 UITabBar transition: the travelling - /// drop stays close to one cell, grows only a few percent vertically, and - /// uses barely-visible chromatic separation. "subtle" reduces those cues - /// further for applications that want an almost-flat selection change. + /// is the measured iOS 26 Liquid Glass morph; "subtle" halves the + /// deformation and optics for a calmer selection change. static Tokens preset(String name) { Tokens tk = new Tokens(); if ("subtle".equals(name)) { - tk.stretch = 0f; - tk.squashW = 0.03f; - tk.grow = 0.005f; - tk.squashH = 0.025f; - tk.liftMm = 0.03f; - tk.bubbleWidthPct = 100; - tk.overflowPct = 0; - tk.downBiasMm = 0f; - tk.restMag = 1.01f; - tk.peakMag = 1.05f; - tk.peakAb = 0.00025f; + tk.stretch = 0.16f; + tk.squashW = 0.08f; + tk.grow = 0.07f; + tk.squashH = 0.09f; + tk.liftMm = 0.25f; + tk.bubbleWidthPct = 96; + tk.overflowPct = 12; + tk.downBiasMm = 0.15f; + tk.restMag = 1.04f; + tk.peakMag = 1.09f; + tk.peakAb = 0.01f; tk.tintStrength = 1f; tk.barGrowPct = 0; - tk.spring = 0.20f; return tk; } // "ios26" -- the shipped iOS Modern tuning. - // The recorded native drop keeps essentially one cell's width while - // it travels. Its liquid character comes from refraction across the - // moving edge, not from stretching across the neighbouring glyphs. - // A wider lens duplicated two tabs at once and looked like distortion - // instead of the compact UIKit bubble. - tk.stretch = 0f; - tk.squashW = 0.05f; - tk.grow = 0.01f; - tk.squashH = 0.04f; - tk.liftMm = 0.05f; - tk.bubbleWidthPct = 100; - tk.overflowPct = 0; - tk.downBiasMm = 0f; - tk.restMag = 1.02f; - tk.peakMag = 1.08f; - tk.peakAb = 0.0005f; + tk.stretch = 0.32f; + tk.squashW = 0.16f; + tk.grow = 0.14f; + tk.squashH = 0.18f; + tk.liftMm = 0.5f; + // Wider than the cell: the native iOS 26 selected pill overlaps its + // neighbour cells (276px over a 253px cell on the @3x reference bar). + tk.bubbleWidthPct = 109; + tk.overflowPct = 18; + tk.downBiasMm = 0.3f; + tk.restMag = 1.08f; + tk.peakMag = 1.18f; + tk.peakAb = 0.02f; tk.tintStrength = 1f; tk.barGrowPct = 0; - // Native keeps a restrained stop deformation without positional - // overshoot; scale the width/height settle envelope to 35%. - tk.spring = 0.35f; return tk; } @@ -167,42 +145,24 @@ static float smooth(float a, float b, float x) { return t * t * (3 - 2 * t); } - // Measured centres of the travelling native selection drop, normalized from - // the first visible frame through its 350ms settle. UIKit's curve accelerates - // more quickly than smoothstep, then spends the last third easing into place; - // approximating it with a short 200ms ease made the CN1 drop look like a jump. - private static final float[] NATIVE_POSITION_TIME = { - 0f, 0.049f, 0.100f, 0.151f, 0.200f, 0.251f, 0.294f, - 0.337f, 0.391f, 0.443f, 0.486f, 0.537f, 0.577f, - 0.629f, 0.677f, 0.723f, 0.766f, 0.820f, 0.863f, 0.914f, 1f - }; - private static final float[] NATIVE_POSITION_VALUE = { - 0f, 0.032f, 0.099f, 0.219f, 0.288f, 0.394f, 0.492f, - 0.582f, 0.678f, 0.728f, 0.770f, 0.804f, 0.876f, - 0.907f, 0.932f, 0.951f, 0.966f, 0.981f, 0.996f, 1f, 1f - }; - - /// Position easing sampled from the recorded iOS 26 UITabBar transition. - /// The path is monotonic: the native drop deforms at the stop but does not - /// shoot past the end of the bar and snap back. - static float springEase(float t) { + /// Position easing: an even ease-in-out travel reaching the target ~t=0.78, then a + /// small damped overshoot that settles by t=1 (the "stop" bounce). The + /// springiness scales the overshoot amplitude: 1 = the preset 0.09, 0 = no + /// overshoot (a plain ease-in-out stop), 2 = double the bounce. + static float springEase(float t, float springiness) { if (t <= 0f) { return 0f; } if (t >= 1f) { return 1f; } - for (int i = 1; i < NATIVE_POSITION_TIME.length; i++) { - if (t <= NATIVE_POSITION_TIME[i]) { - float ta = NATIVE_POSITION_TIME[i - 1]; - float tb = NATIVE_POSITION_TIME[i]; - float u = (t - ta) / (tb - ta); - float va = NATIVE_POSITION_VALUE[i - 1]; - float vb = NATIVE_POSITION_VALUE[i]; - return va + (vb - va) * u; - } + float travelEnd = 0.78f; + if (t <= travelEnd) { + float u = t / travelEnd; + return u * u * (3 - 2 * u); } - return 1f; + float u = (t - travelEnd) / (1f - travelEnd); + return 1f + 0.09f * springiness * (float) (Math.sin(u * Math.PI) * (1f - u)); } /// Computes one morph frame. @@ -223,7 +183,7 @@ static TabSelectionMorph compute(float t, int fromX, int fromW, int toX, int toW TabSelectionMorph m = new TabSelectionMorph(); float tp = t < 0 ? 0 : (t > 1 ? 1 : t); - float pos = springEase(tp); + float pos = springEase(tp, tk.spring); int x = fromX + (int) ((toX - fromX) * pos); int w = fromW + (int) ((toW - fromW) * pos); @@ -232,10 +192,6 @@ static TabSelectionMorph compute(float t, int fromX, int fromW, int toX, int toW float moving = smooth(0f, 0.08f, tp) * (1f - smooth(0.88f, 1f, tp)); float sd = (tp - 0.80f) / 0.14f; float squash = (sd > -1f && sd < 1f) ? (1f - sd * sd) * (1f - sd * sd) : 0f; // settle bump - // Springiness controls the stop DEFORMATION, not positional overshoot. - // The native centre path above is monotonic; the liquid cue is its brief - // width compression/height swell as it comes to rest. - squash *= tk.spring; float grow = smooth(0f, 0.10f, tp) * (1f - smooth(0.20f, 0.42f, tp)); // early whole-bar swell // horizontal elongation while moving, then width compression at the stop @@ -250,8 +206,8 @@ static TabSelectionMorph compute(float t, int fromX, int fromW, int toX, int toW capX += (w - bubbleW) / 2; w = bubbleW; - // The drop may be wider than its cell (bubbleWidthPct > 100), but it - // must never leave the bar: + // The drop may be wider than its cell (bubbleWidthPct > 100) and the + // spring overshoots the end cells -- but it must never leave the bar: // the native pill overlaps NEIGHBOUR cells, not the backdrop. if (capX < barLeftX) { w -= barLeftX - capX; diff --git a/CodenameOne/src/com/codename1/ui/Tabs.java b/CodenameOne/src/com/codename1/ui/Tabs.java index 8435bedaa90..21fc3bd9c67 100644 --- a/CodenameOne/src/com/codename1/ui/Tabs.java +++ b/CodenameOne/src/com/codename1/ui/Tabs.java @@ -157,15 +157,12 @@ public class Tabs extends Container { private int animatedIndicatorDurationMs = 200; private int animatedIndicatorThicknessMm = 1; // 1mm-tall underline private Motion indicatorAnimMotion; - // Last progress actually presented. This is paced separately from the - // wall-clock Motion so a coalesced/slow repaint cannot skip the whole morph. - private int indicatorAnimValue; // The tab index the indicator morph is currently travelling TO. Lets a re-entrant // setSelectedIndex (e.g. the content-slide finishing) recognise that a morph to the // same tab is already in flight and NOT restart it mid-travel. private int indicatorTargetIndex = -1; // TEST-ONLY: when >=0, paintSelectionCapsule renders the morph at this fixed - // 0..100 progress instead of the live motion (for the JavaSE capture probe). + // 0..120 progress instead of the live motion (for the JavaSE capture probe). private int morphTestValue = -1; // Tab bounds at the start of the indicator animation. private int indicatorFromX; @@ -401,10 +398,7 @@ public boolean animate() { // slide motion control deregistration; the indicator motion is // cheap enough to run alongside without coordination. if (indicatorAnimMotion != null) { - int wallClockValue = indicatorAnimMotion.getValue(); - indicatorAnimValue = TabSelectionMorph.paceVisibleFrame( - indicatorAnimValue, wallClockValue); - if (indicatorAnimValue >= 100) { + if (indicatorAnimMotion.isFinished()) { indicatorAnimMotion = null; indicatorTargetIndex = -1; // Paint ONE more frame now that the morph is over so the SETTLED capsule @@ -475,7 +469,7 @@ void deregisterAnimatedInternal() { // indicator morph are done. Previously a finished 200ms slide deregistered the // animation while the 550ms morph was still in flight, freezing the drop mid-travel. if ((slideToDestMotion == null || slideToDestMotion.isFinished()) - && indicatorAnimMotion == null) { + && (indicatorAnimMotion == null || indicatorAnimMotion.isFinished())) { Form f = getComponentForm(); if (f != null) { f.deregisterAnimatedInternal(this); @@ -1480,7 +1474,7 @@ private void startIndicatorAnimation(int fromIndex, int toIndex) { // If a motion is already in flight, start from the *current* // interpolated position, not from the previous tab -- otherwise // rapid double-clicks jump back to a stale baseline. - if (indicatorAnimMotion != null && indicatorAnimValue < 100) { + if (indicatorAnimMotion != null && !indicatorAnimMotion.isFinished()) { if (toIndex == indicatorTargetIndex) { // A morph to this SAME tab is already running. The content-slide finishing // re-invokes setSelectedIndex(active) to finalise the selection; restarting @@ -1488,7 +1482,7 @@ private void startIndicatorAnimation(int fromIndex, int toIndex) { // Let the in-flight morph run to completion instead. return; } - int v = indicatorAnimValue; + int v = indicatorAnimMotion.getValue(); indicatorFromX = indicatorFromX + ((indicatorToX - indicatorFromX) * v / 100); indicatorFromW = indicatorFromW + ((indicatorToW - indicatorFromW) * v / 100); } else { @@ -1499,12 +1493,11 @@ private void startIndicatorAnimation(int fromIndex, int toIndex) { indicatorToW = to[1]; indicatorTargetIndex = toIndex; // LINEAR-TIME motion: the value is the morph timeline 0..100 and - // paintSelectionCapsule derives the recorded native position curve AND the - // height/squash envelopes from it -- so the bubble stays tall while travelling - // and compresses at the stop. (The non-glass + // paintSelectionCapsule derives the spring position (springEaseTabs, an + // ease-out-back overshoot) AND the height/squash envelopes from it -- so the + // bubble stays tall while travelling and compresses at the stop. (The non-glass // Material underline path reads the same value as a plain position fraction.) indicatorAnimMotion = Motion.createLinearMotion(0, 100, animatedIndicatorDurationMs); - indicatorAnimValue = 0; indicatorAnimMotion.start(); Form f = getComponentForm(); if (f != null) { @@ -1545,7 +1538,7 @@ void paintBottomDivider(Graphics g) { g.setAlpha(oldAlpha); } - // Position easing + smoothstep now live in the pure + // Position easing (springEaseTabs) + smoothstep (lensSmooth) now live in the pure // TabSelectionMorph model (springEase / smooth) so the morph math is unit-testable. /// The thin frost rim left around the selection capsule (tabSelInsetMm, default a hair). @@ -1597,7 +1590,7 @@ private void capsuleCellBounds(int index, int inset, int[] out) { } /// TEST-ONLY hook: render the selection morph frozen at a fixed progress - /// (`value` 0..100, where 100 is the settled target) + /// (`value` 0..120, where 100 is the target and >100 is the settle overshoot) /// travelling from `fromIndex` to `toIndex`, so a JavaSE probe can capture exact /// frames of the animation without racing the real-time motion. Pass `value` < 0 /// to clear and resume normal behaviour. @@ -1624,20 +1617,18 @@ public void setMorphTestState(int fromIndex, int toIndex, int value) { /// The iOS "selected cell" background: a subtle grey capsule kept at bar height /// (the lens drop, drawn over it, bulges taller). Travels + elongates with the /// drop. systemFill grey so it reads neutral, not blue. Alpha via tabSelPillAlphaInt. - private void drawSelectionPill(Graphics g, int capX, int capY, int w, int capH, - boolean dark) { + private void drawSelectionPill(Graphics g, int capX, int capY, int w, int capH, float bump) { int pillInset = capH * 7 / 100; // pill a hair shorter than the lens int py = capY + pillInset; int ph = capH - 2 * pillInset; if (ph <= 0) { return; } - // The native frame sequence keeps the system-fill body visible for the - // entire trip (#b4 light / #78 dark on the flat reference backdrop). - // Fading 85% of it away in flight left only a thin lens outline, which is - // why the foreground liquid bubble disappeared in JavaSE/JavaScript. + // FADE the grey pill out as the drop travels: the settled "selected cell" + // background is grey, but MID-FLIGHT the bubble is pure transparent glass + // (otherwise the grey shows through the gap between tabs as an empty blob). int baseAlpha = getUIManager().getThemeConstant("tabSelPillAlphaInt", 34); - int alpha = baseAlpha; + int alpha = (int) (baseAlpha * (1f - 0.85f * bump)); if (alpha <= 0) { return; } @@ -1645,9 +1636,7 @@ private void drawSelectionPill(Graphics g, int capX, int capY, int w, int capH, int oldA = g.getAlpha(); boolean aa = g.isAntiAliased(); g.setAntiAliased(true); - g.setColor(getUIManager().getThemeConstant(dark - ? "tabSelPillDarkColorInt" : "tabSelPillColorInt", - dark ? 0xc9c9ce : 0x767680)); + g.setColor(getUIManager().getThemeConstant("tabSelPillColorInt", 0x767680)); g.setAlpha(alpha); g.fillRoundRect(capX, py, w, ph, ph, ph); g.setAntiAliased(aa); @@ -1695,7 +1684,7 @@ void paintSelectionCapsule(Graphics g) { int toW; float t; if (morphTestValue >= 0 || indicatorAnimMotion != null) { - int v = morphTestValue >= 0 ? morphTestValue : indicatorAnimValue; + int v = morphTestValue >= 0 ? morphTestValue : indicatorAnimMotion.getValue(); t = (v < 0 ? 0 : (v > 100 ? 100 : v)) / 100f; fromX = indicatorFromX; fromW = indicatorFromW; @@ -1744,14 +1733,15 @@ void paintSelectionCapsule(Graphics g) { g.lensRegion(m.barGrowX, m.barGrowY, m.barGrowW, m.barGrowH, -1f, m.barGrowMag, 0f, 0x000000, 0f); } - drawSelectionPill(g, m.capX, m.capY, m.capW, m.capH, dark); - // The sign selects the luminance key: positive tints dark glyphs on the - // light bar, negative tints light glyphs on the dark bar. This keeps the - // accent attached to the travelling drop in both appearances instead of - // painting the destination tab blue before the bubble reaches it. + drawSelectionPill(g, m.capX, m.capY, m.capW, m.capH, m.flight); + // Accent supplied by the lens in LIGHT mode only: the keying tints DARK + // pixels toward the accent, which is right over a light frost (the + // deliberately-dark glyphs turn blue) but floods a dark bar solid blue, + // because everything under the drop is dark there. On dark bars the + // glyphs carry the accent directly (theme) and the lens keeps only its + // magnify/aberration optics. int tint = getUIManager().getThemeConstant("tabSelLensTintColorInt", 0x0a84ff); - g.lensRegion(m.lensX, m.lensY, m.lensW, m.lensH, -1f, m.magnify, - m.aberration, tint, dark ? -m.tintStrength : m.tintStrength); + g.lensRegion(m.lensX, m.lensY, m.lensW, m.lensH, -1f, m.magnify, m.aberration, tint, dark ? 0f : m.tintStrength); return; } // Non-glass platforms: a translucent rounded capsule. @@ -1771,8 +1761,8 @@ void paintSelectionCapsule(Graphics g) { /// only (review: fewer, coherent morph knobs): tabsMorphPreset picks a named /// envelope set inside the motion model ("ios26" default / "subtle"), /// tabsMorphLensIntensityPct scales the lens optics around the preset (100 = - /// as authored) and tabsMorphSpringPct scales the preset's settle deformation - /// (100 = preset deformation, 0 = plain stop). Duration remains + /// as authored) and tabsMorphSpringPct scales the settle overshoot (100 = + /// preset bounce, 0 = plain stop). Duration remains /// tabsAnimatedIndicatorDurationInt. The mm lengths carried by the preset /// are converted to px here so the model stays Display-free. private TabSelectionMorph.Tokens morphTokens() { @@ -1780,7 +1770,7 @@ private TabSelectionMorph.Tokens morphTokens() { TabSelectionMorph.Tokens tk = TabSelectionMorph.Tokens.preset( uim.getThemeConstant("tabsMorphPreset", "ios26")); tk.scaleLensIntensity(uim.getThemeConstant("tabsMorphLensIntensityPct", 100) / 100f); - tk.spring *= uim.getThemeConstant("tabsMorphSpringPct", 100) / 100f; + tk.spring = uim.getThemeConstant("tabsMorphSpringPct", 100) / 100f; tk.liftPx = Display.getInstance().convertToPixels(tk.liftMm); tk.downBiasPx = Display.getInstance().convertToPixels(tk.downBiasMm); return tk; @@ -1795,7 +1785,7 @@ void paintAnimatedIndicator(Graphics g) { int x; int w; if (indicatorAnimMotion != null) { - int v = indicatorAnimValue; // paced visible progress, 0..100 + int v = indicatorAnimMotion.getValue(); // 0..100 x = indicatorFromX + ((indicatorToX - indicatorFromX) * v / 100); w = indicatorFromW + ((indicatorToW - indicatorFromW) * v / 100); } else { diff --git a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java index a4c7567d6a6..f64b2378e50 100644 --- a/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java +++ b/Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java @@ -17219,7 +17219,180 @@ public boolean blurRegion(Object graphics, int x, int y, int width, int height, return false; } } - + + /// The full iOS 26 "Liquid Glass" material for backdrop-filter surfaces (the + /// floating tab-bar pill, glass buttons). Mirrors IOSImplementation.glassRegion's + /// offscreen branch step for step: read the backdrop PADDED and edge-replicated, + /// apply the affine colour material (saturation/scale/offset), Gaussian-blur, + /// then apply the rounded-shape OPTICS (edge refraction + specular rim + AA mask) + /// and composite the pill-shaped patch back. Without this the simulator fell + /// back to a plain blur, leaving the bar a flat grey where the device renders a + /// bright frost -- every glass-tab comparison read "murky" purely because of the + /// missing material under the lens. + @Override + public boolean glassRegion(Object graphics, int x, int y, int width, int height, float radius, + float cornerRadius, float sat, float scaleParam, float offset, float refract, float specular) { + if (radius <= 0f || width <= 0 || height <= 0) { + return true; + } + try { + BufferedImage dest; + double scale; + if (graphics instanceof NativeScreenGraphics) { + NativeScreenGraphics ng = (NativeScreenGraphics) graphics; + if (ng.sourceImage != null) { + dest = ng.sourceImage; + scale = 1.0; + } else if (canvas != null && canvas.edtBuffer != null) { + dest = canvas.edtBuffer; + scale = (double) dest.getWidth() / getDisplayWidthImpl(); + } else { + return false; + } + } else if (mutableImageGraphics.containsKey(graphics)) { + dest = mutableImageGraphics.get(graphics); + scale = 1.0; + } else { + return false; + } + int rx = (int) Math.round(x * scale); + int ry = (int) Math.round(y * scale); + int rw = (int) Math.round(width * scale); + int rh = (int) Math.round(height * scale); + int dw = dest.getWidth(), dh = dest.getHeight(); + if (rx < 0) { rw += rx; rx = 0; } + if (ry < 0) { rh += ry; ry = 0; } + if (rx + rw > dw) { rw = dw - rx; } + if (ry + rh > dh) { rh = dh - ry; } + if (rw <= 0 || rh <= 0) { + return true; + } + float scaledRadius = (float) (radius * scale); + float scaledCorner = cornerRadius < 0 ? -1f : (float) (cornerRadius * scale); + // Pad by 3*radius of edge-replicated backdrop so the blur's edge fade is + // fully contained outside the component (see the iOS port commentary). + int pad = (int) Math.ceil(scaledRadius) * 3 + 1; + int bw = rw + 2 * pad, bh = rh + 2 * pad; + int ax0 = Math.max(0, rx - pad), ay0 = Math.max(0, ry - pad); + int ax1 = Math.min(dw, rx + rw + pad), ay1 = Math.min(dh, ry + rh + pad); + int aw = ax1 - ax0, ah = ay1 - ay0; + int[] avail = dest.getRGB(ax0, ay0, aw, ah, null, 0, aw); + int[] prgb = new int[bw * bh]; + for (int by = 0; by < bh; by++) { + int ay = (ry - pad + by) - ay0; + if (ay < 0) ay = 0; else if (ay >= ah) ay = ah - 1; + int arow = ay * aw, brow = by * bw; + for (int bx = 0; bx < bw; bx++) { + int ax = (rx - pad + bx) - ax0; + if (ax < 0) ax = 0; else if (ax >= aw) ax = aw - 1; + prgb[brow + bx] = avail[arow + ax]; + } + } + glassMaterialInPlace(prgb, sat, scaleParam, offset); + BufferedImage padded = new BufferedImage(bw, bh, BufferedImage.TYPE_INT_ARGB); + padded.setRGB(0, 0, bw, bh, prgb, 0, bw); + BufferedImage blurredPadded = new GaussianFilter(scaledRadius).filter(padded, null); + int[] pbargb = blurredPadded.getRGB(0, 0, bw, bh, null, 0, bw); + int[] out = new int[rw * rh]; + applyGlassOptics(pbargb, bw, bh, pad, out, rw, rh, scaledCorner, refract, specular); + BufferedImage patch = new BufferedImage(rw, rh, BufferedImage.TYPE_INT_ARGB); + patch.setRGB(0, 0, rw, rh, out, 0, rw); + Graphics2D dg = dest.createGraphics(); + dg.drawImage(patch, rx, ry, null); + dg.dispose(); + return true; + } catch (Throwable t) { + return false; + } + } + + /// The reverse-engineered iOS Liquid Glass colour material; mirrors + /// IOSImplementation.glassMaterialInPlace (validated <1 LSB against a real + /// UIVisualEffectView): c' = clamp((lum + (c - lum) * sat) * scale + offset). + private static void glassMaterialInPlace(int[] argb, float sat, float scale, float offset) { + for (int i = 0; i < argb.length; i++) { + int p = argb[i]; + int a = p & 0xff000000; + float r = (p >> 16) & 0xff, g = (p >> 8) & 0xff, b = p & 0xff; + float lum = 0.2126f * r + 0.7152f * g + 0.0722f * b; + r = (lum + (r - lum) * sat) * scale + offset; + g = (lum + (g - lum) * sat) * scale + offset; + b = (lum + (b - lum) * sat) * scale + offset; + int ri = r < 0 ? 0 : (r > 255 ? 255 : (int) r); + int gi = g < 0 ? 0 : (g > 255 ? 255 : (int) g); + int bi = b < 0 ? 0 : (b > 255 ? 255 : (int) b); + argb[i] = a | (ri << 16) | (gi << 8) | bi; + } + } + + /// Liquid Glass optics over the blurred material; mirrors + /// IOSImplementation.applyGlassOptics: rounded-rect SDF drives an edge + /// refraction (quarter-circle displacement toward the centre), a specular rim + /// glint (brightest at the top) and the anti-aliased shape mask. + private static void applyGlassOptics(int[] src, int bw, int bh, int pad, int[] out, + int rw, int rh, float cornerRadius, float refract, float specular) { + float hw = rw / 2f, hh = rh / 2f; + float r = cornerRadius < 0f ? Math.min(hw, hh) : Math.min(cornerRadius, Math.min(hw, hh)); + if (r < 0f) r = 0f; + float band = Math.min(hw, hh) * 0.6f; // refraction active in the outer 60% + float rimW = 3.0f; // specular rim width (px) + for (int y = 0; y < rh; y++) { + float py = y + 0.5f; + for (int x = 0; x < rw; x++) { + float px = x + 0.5f; + float dx = Math.abs(px - hw) - (hw - r); + float dy = Math.abs(py - hh) - (hh - r); + float ax = dx > 0 ? dx : 0, ay = dy > 0 ? dy : 0; + float outside = (float) Math.sqrt(ax * ax + ay * ay); + float inside = Math.min(Math.max(dx, dy), 0f); + float sdf = outside + inside - r; + float depth = -sdf; + if (depth <= 0f) { out[y * rw + x] = 0; continue; } + float alpha = depth >= 1f ? 1f : depth; + float sx = x, sy = y; + if (refract > 0f && band > 0f && depth < band) { + float t = 1f - depth / band; + float distortion = 1f - (float) Math.sqrt(Math.max(0f, 1f - t * t)); + sx = x - (px - hw) * distortion * refract; + sy = y - (py - hh) * distortion * refract; + } + int col = glassSampleBilinear(src, bw, bh, sx + pad, sy + pad); + int rr = (col >> 16) & 0xff, gg = (col >> 8) & 0xff, bb = col & 0xff; + if (specular > 0f && depth < rimW) { + float rim = 1f - depth / rimW; + float topBias = 0.55f + 0.45f * (1f - py / rh); + int add = (int) (specular * rim * topBias * 70f); + rr = rr + add > 255 ? 255 : rr + add; + gg = gg + add > 255 ? 255 : gg + add; + bb = bb + add > 255 ? 255 : bb + add; + } + int a = (int) (alpha * 255f); + out[y * rw + x] = (a << 24) | (rr << 16) | (gg << 8) | bb; + } + } + } + + /// Bilinear ARGB sample with edge clamping; mirrors IOSImplementation.sampleBilinear. + private static int glassSampleBilinear(int[] buf, int w, int h, float fx, float fy) { + if (fx < 0f) fx = 0f; else if (fx > w - 1) fx = w - 1; + if (fy < 0f) fy = 0f; else if (fy > h - 1) fy = h - 1; + int x0 = (int) fx, y0 = (int) fy; + int x1 = x0 + 1 < w ? x0 + 1 : x0, y1 = y0 + 1 < h ? y0 + 1 : y0; + float tx = fx - x0, ty = fy - y0; + int p00 = buf[y0 * w + x0], p10 = buf[y0 * w + x1]; + int p01 = buf[y1 * w + x0], p11 = buf[y1 * w + x1]; + int rr = glassBilerp((p00 >> 16) & 0xff, (p10 >> 16) & 0xff, (p01 >> 16) & 0xff, (p11 >> 16) & 0xff, tx, ty); + int gg = glassBilerp((p00 >> 8) & 0xff, (p10 >> 8) & 0xff, (p01 >> 8) & 0xff, (p11 >> 8) & 0xff, tx, ty); + int bb = glassBilerp(p00 & 0xff, p10 & 0xff, p01 & 0xff, p11 & 0xff, tx, ty); + return (rr << 16) | (gg << 8) | bb; + } + + private static int glassBilerp(int c00, int c10, int c01, int c11, float tx, float ty) { + float top = c00 + (c10 - c00) * tx; + float bot = c01 + (c11 - c01) * tx; + return (int) (top + (bot - top) * ty + 0.5f); + } + // iOS-26 tab selection-DROP lens constants (shared shape with METALView.m // glassApplyLens). Uniform magnification in the central MAG_FLAT fraction of the // radius, smooth falloff to 1.0 at the rim; chromatic aberration at the rim only; @@ -17227,33 +17400,29 @@ public boolean blurRegion(Object graphics, int x, int y, int width, int height, private static final double LENS_MAG_FLAT = 0.75; private static final double LENS_TINT_HI = 150; private static final double LENS_TINT_LO = 55; - private static final double LENS_LIGHT_KEY_LO = 150; - private static final double LENS_LIGHT_KEY_HI = 220; // The drop LIFTS the content under it upward (like a magnifying droplet pulling the // glyph up) -- this is the "tabs grow/rise" in the native morph, not just magnify. // Lift is proportional to (magnify-1) so it tracks the travel bump, peaks at centre. - private static final double LENS_LIFT_COEF = 0.10; + private static final double LENS_LIFT_COEF = 0.40; // The drop is a 3D GLASS droplet: a soft specular GLARE (light sheen) near the top, // and a bright EDGE RIM that defines the glass boundary. Without these it reads as a // flat tinted pill, not glass. - private static final double LENS_GLARE = 0.07; // broad moving specular sheen - private static final double LENS_RIM = 0.11; // directional top/left rim light - private static final double LENS_RIM_W = 0.10; // rim band width (fraction of half-height) - private static final double LENS_REFRACT = 0.015; // native barely displaces content at the rim - private static final double LENS_EDGE_SHADOW = 0.07; // directional bottom/right depth - // Magnification falls gently from `magnify` at the centre to 1 at the rim. The - // previous 0.84 rim scale visibly crushed neighbouring glyphs and read as a lens - // distortion rather than the native tab's shallow convex surface. - private static final double LENS_RIM_SCALE = 1.0; - private static final int LENS_GLASS_TINT = 0xbcd8ff; - private static final double LENS_GLASS_TINT_STR = 0.0; // native rim is neutral white, not a blue outline + private static final double LENS_GLARE = 0.09; // specular sheen strength + private static final double LENS_RIM = 0.06; // edge-rim brightness + private static final double LENS_RIM_W = 0.06; // rim band width (fraction of half-height) + private static final double LENS_REFRACT = 0.16; // edge lensing: content bends at the rim + private static final double LENS_EDGE_SHADOW = 0.12; // soft dark band at the inner edge (glass depth) + // The periphery (bar / other tabs seen through the drop) is slightly SHRUNK while the + // central glyph stays enlarged -- mag falls from `magnify` at the centre to RIM_SCALE + // (<1) at the rim. And the glass carries a faint cool TINT inside it. + private static final double LENS_RIM_SCALE = 0.84; + private static final int LENS_GLASS_TINT = 0xbcd8ff; // faint cool-blue glass cast + private static final double LENS_GLASS_TINT_STR = 0.10; // The selected glyph seen through the drop reads MORE vivid/saturated than a flat // tint -- native's selected blue is punchy. Boost chroma around the pixel luminance: // coloured pixels (the blue glyph) push further from grey; neutral greys (the bar) // are barely touched, so only the glyph gets more saturated. - private static final double LENS_SAT_BOOST = 1.12; - private static final double LENS_GLASS_START = 1.025; - private static final double LENS_GLASS_FULL = 1.075; + private static final double LENS_SAT_BOOST = 1.32; @Override public boolean lensRegion(Object graphics, int x, int y, int width, int height, @@ -17318,11 +17487,7 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, // The 3D-glass cues (edge refraction / edge shadow / glare) belong to the MORPH // droplet, not the settled pill -- scale them by how magnified the drop is so a // resting selection stays a flat subtle pill. - // The bounded iOS-26 morph rests at 1.02x and peaks at 1.08x. Keep the - // liquid foreground completely absent at rest, but let its rim/glare/ - // refraction reach full strength during flight. The previous 1.085x - // lower edge sat above the new peak, silently disabling the liquid layer. - double glassAmt = lensSmoothstep(LENS_GLASS_START, LENS_GLASS_FULL, magnify); + double glassAmt = lensSmoothstep(1.085, 1.25, magnify); for (int yy = 0; yy < rh; yy++) { double py = yy + 0.5 - hh; for (int xx = 0; xx < rw; xx++) { @@ -17337,8 +17502,9 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, double alpha = Math.min(depth, 1.0); double rd = Math.min(1.0, Math.sqrt((px * px) / (hw * hw) + (py * py) / (hh * hh))); double edge = lensSmoothstep(LENS_MAG_FLAT, 1.0, rd); - // Centre enlarges gently; the rim returns to 1:1 instead of minifying - // neighbouring content. This keeps the native shallow-lens character. + // Centre ENLARGES (magnify); periphery SHRINKS (mag -> RIM_SCALE < 1) so the + // bar / other tabs seen through the drop read slightly minified, while the + // central glyph stays big. No shrink when settled (scaled by glassAmt). double rimScale = 1.0 + (LENS_RIM_SCALE - 1.0) * glassAmt; double mag = magnify + (rimScale - magnify) * edge; if (mag < 0.2) mag = 0.2; @@ -17356,9 +17522,7 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, int sg = (lensSample(src, rw, rh, hw + (px / mag) * refr, hh + (py / mag) * refr + lift) >> 8) & 0xff; int sb = lensSample(src, rw, rh, hw + (px / magB) * refr, hh + (py / magB) * refr + lift) & 0xff; double lum = 0.2126 * sr + 0.7152 * sg + 0.0722 * sb; - double t = tintStrength < 0 - ? -tintStrength * lensSmoothstep(LENS_LIGHT_KEY_LO, LENS_LIGHT_KEY_HI, lum) - : tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + double t = tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); double fr = sr + (tr - sr) * t; double fg = sg + (tg - sg) * t; double fb = sb + (tb - sb) * t; @@ -17375,17 +17539,15 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, fr = sl + (fr - sl) * LENS_SAT_BOOST; fg = sl + (fg - sl) * LENS_SAT_BOOST; fb = sl + (fb - sl) * LENS_SAT_BOOST; - // 3D GLASS: native depth is a directional highlight/shadow surface, not - // large content displacement. Light the top-left rim and shade the - // bottom-right rim so the travelling capsule reads as a convex volume. + // 3D GLASS: a soft specular GLARE near the top (an elliptical sheen) plus + // a bright EDGE RIM (small `depth` = near the boundary) so the drop reads + // as a raised glass droplet, not a flat tint. Both lift the colour toward + // white. double gx = px / hw, gy = (py + 0.42 * hh) / hh; double glare = LENS_GLARE * glassAmt * Math.exp(-(gx * gx * 1.15 + gy * gy * 2.6) * 2.1); double rimW = Math.max(2.0, LENS_RIM_W * hh); - double rim = depth < rimW ? (1.0 - depth / rimW) * LENS_RIM - * (0.35 + 0.65 * glassAmt) : 0; - double lightDir = Math.max(0.2, Math.min(1.0, - 0.68 - 0.28 * (py / hh) - 0.12 * (px / hw))); - double bright = glare + rim * lightDir; + double rim = depth < rimW ? (1.0 - depth / rimW) * LENS_RIM : 0; + double bright = glare + rim; if (bright > 0) { fr += bright * (255 - fr); fg += bright * (255 - fg); @@ -17395,9 +17557,7 @@ private static void applyLensBuffer(int[] src, int[] out, int rw, int rh, // shadow at its edge) -- gives the droplet depth instead of a flat cutout. double esW = Math.max(2.0, 0.13 * Math.min(hw, hh)); if (depth < esW) { - double shadowDir = Math.max(0.1, Math.min(1.0, - 0.55 + 0.35 * (py / hh) + 0.10 * (px / hw))); - double es = (1.0 - depth / esW) * LENS_EDGE_SHADOW * glassAmt * shadowDir; + double es = (1.0 - depth / esW) * LENS_EDGE_SHADOW * glassAmt; fr *= (1 - es); fg *= (1 - es); fb *= (1 - es); diff --git a/Ports/iOSPort/nativeSources/CN1MetalShaders.metal b/Ports/iOSPort/nativeSources/CN1MetalShaders.metal index d5c332a1a2d..a22e2f92fa6 100644 --- a/Ports/iOSPort/nativeSources/CN1MetalShaders.metal +++ b/Ports/iOSPort/nativeSources/CN1MetalShaders.metal @@ -342,20 +342,16 @@ fragment float4 cn1_fs_multistop_gradient( constant float LENS_MAG_FLAT = 0.75; constant float LENS_TINT_HI = 150.0; constant float LENS_TINT_LO = 55.0; -constant float LENS_LIGHT_KEY_LO = 150.0; -constant float LENS_LIGHT_KEY_HI = 220.0; -constant float LENS_LIFT_COEF = 0.10; -constant float LENS_GLARE = 0.07; -constant float LENS_RIM = 0.11; -constant float LENS_RIM_W = 0.10; -constant float LENS_REFRACT = 0.015; -constant float LENS_EDGE_SHADOW = 0.07; -constant float LENS_RIM_SCALE = 1.0; +constant float LENS_LIFT_COEF = 0.40; +constant float LENS_GLARE = 0.09; +constant float LENS_RIM = 0.06; +constant float LENS_RIM_W = 0.06; +constant float LENS_REFRACT = 0.16; +constant float LENS_EDGE_SHADOW = 0.12; +constant float LENS_RIM_SCALE = 0.84; constant float3 LENS_GLASS_TINT = float3(188.0, 216.0, 255.0); // 0xbcd8ff, 0..255 -constant float LENS_GLASS_TINT_STR = 0.0; -constant float LENS_SAT_BOOST = 1.12; -constant float LENS_GLASS_START = 1.025; -constant float LENS_GLASS_FULL = 1.075; +constant float LENS_GLASS_TINT_STR = 0.10; +constant float LENS_SAT_BOOST = 1.32; static inline float cn1_lens_smoothstep(float a, float b, float x) { float t = clamp((x - a) / (b - a), 0.0, 1.0); @@ -393,7 +389,7 @@ fragment float4 cn1_fs_lens( float rd = min(1.0, sqrt((px * px) / (hw * hw) + (py * py) / (hh * hh))); float liftMax = LENS_LIFT_COEF * (magnify - 1.0) * hh; - float glassAmt = cn1_lens_smoothstep(LENS_GLASS_START, LENS_GLASS_FULL, magnify); + float glassAmt = cn1_lens_smoothstep(1.085, 1.25, magnify); float edge = cn1_lens_smoothstep(LENS_MAG_FLAT, 1.0, rd); float rimScale = 1.0 + (LENS_RIM_SCALE - 1.0) * glassAmt; @@ -412,9 +408,7 @@ fragment float4 cn1_fs_lens( float sb = src.sample(smp, cB).b * 255.0; float lum = 0.2126 * sr + 0.7152 * sg + 0.0722 * sb; - float t = tintStrength < 0.0 - ? -tintStrength * cn1_lens_smoothstep(LENS_LIGHT_KEY_LO, LENS_LIGHT_KEY_HI, lum) - : tintStrength * cn1_lens_smoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + float t = tintStrength * cn1_lens_smoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); float fr = sr + (tintc.r - sr) * t; float fg = sg + (tintc.g - sg) * t; float fb = sb + (tintc.b - sb) * t; @@ -432,18 +426,15 @@ fragment float4 cn1_fs_lens( float gx = px / hw, gy = (py + 0.42 * hh) / hh; float glare = LENS_GLARE * glassAmt * exp(-(gx * gx * 1.15 + gy * gy * 2.6) * 2.1); float rimW = max(2.0, LENS_RIM_W * hh); - float rim = depth < rimW ? (1.0 - depth / rimW) * LENS_RIM - * (0.35 + 0.65 * glassAmt) : 0.0; - float lightDir = clamp(0.68 - 0.28 * (py / hh) - 0.12 * (px / hw), 0.2, 1.0); - float bright = glare + rim * lightDir; + float rim = depth < rimW ? (1.0 - depth / rimW) * LENS_RIM : 0.0; + float bright = glare + rim; fr += bright * (255.0 - fr); fg += bright * (255.0 - fg); fb += bright * (255.0 - fb); float esW = max(2.0, 0.13 * min(hw, hh)); if (depth < esW) { - float shadowDir = clamp(0.55 + 0.35 * (py / hh) + 0.10 * (px / hw), 0.1, 1.0); - float es = (1.0 - depth / esW) * LENS_EDGE_SHADOW * glassAmt * shadowDir; + float es = (1.0 - depth / esW) * LENS_EDGE_SHADOW * glassAmt; fr *= (1.0 - es); fg *= (1.0 - es); fb *= (1.0 - es); } fr = clamp(fr, 0.0, 255.0); fg = clamp(fg, 0.0, 255.0); fb = clamp(fb, 0.0, 255.0); diff --git a/Ports/iOSPort/nativeSources/METALView.m b/Ports/iOSPort/nativeSources/METALView.m index ea6f9bfce59..6fdbe77259e 100644 --- a/Ports/iOSPort/nativeSources/METALView.m +++ b/Ports/iOSPort/nativeSources/METALView.m @@ -214,20 +214,16 @@ static inline float glassSmoothstep(float a, float b, float x) { #define LENS_MAG_FLAT 0.75f // uniform-magnify fraction of the (elliptical) radius #define LENS_TINT_HI 150.0f // luminance >= HI: no tint #define LENS_TINT_LO 55.0f // luminance <= LO: full dark->accent tint -#define LENS_LIGHT_KEY_LO 150.0f // dark bar/pill remain neutral -#define LENS_LIGHT_KEY_HI 220.0f // light glyph becomes full accent -#define LENS_LIFT_COEF 0.10f // restrained upward pull under the drop -#define LENS_GLARE 0.07f // broad moving specular sheen -#define LENS_RIM 0.11f // directional top/left rim light -#define LENS_RIM_W 0.10f // rim band width (fraction of half-height) -#define LENS_REFRACT 0.015f // native barely displaces content at the rim -#define LENS_EDGE_SHADOW 0.07f // directional bottom/right depth -#define LENS_RIM_SCALE 1.0f // no funhouse minification at the periphery +#define LENS_LIFT_COEF 0.40f // upward pull of the content under the drop +#define LENS_GLARE 0.09f // specular sheen strength +#define LENS_RIM 0.06f // edge-rim brightness +#define LENS_RIM_W 0.06f // rim band width (fraction of half-height) +#define LENS_REFRACT 0.16f // edge lensing: content bends at the rim +#define LENS_EDGE_SHADOW 0.12f // soft dark band just inside the rim +#define LENS_RIM_SCALE 0.84f // periphery shrinks (< 1) while the centre enlarges #define LENS_GLASS_TINT 0xbcd8ff /* faint cool-blue cast through the whole glass */ -#define LENS_GLASS_TINT_STR 0.0f -#define LENS_SAT_BOOST 1.12f // restrained blue saturation -#define LENS_GLASS_START 1.025f -#define LENS_GLASS_FULL 1.075f +#define LENS_GLASS_TINT_STR 0.10f +#define LENS_SAT_BOOST 1.32f // push the tinted blue glyph more vivid __attribute__((unused)) static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, @@ -244,9 +240,7 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, float liftMax = LENS_LIFT_COEF * (magnify - 1.0f) * hh; // The 3D-glass cues belong to the morph droplet, not the settled pill -- fade them // out by how magnified the drop is, so a resting selection is a flat subtle pill. - // The bounded tab morph rests at 1.02x and peaks at 1.08x. Fade the - // foreground optics in above rest and reach full liquid character in flight. - float glassAmt = glassSmoothstep(LENS_GLASS_START, LENS_GLASS_FULL, magnify); + float glassAmt = glassSmoothstep(1.085f, 1.25f, magnify); for (int y = 0; y < rh; y++) { float py = (y + 0.5f) - hh; for (int x = 0; x < rw; x++) { @@ -262,8 +256,9 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, float alpha = depth >= 1.0f ? 1.0f : depth; float rd = sqrtf((px * px) / (hw * hw) + (py * py) / (hh * hh)); // elliptical 0..1 if (rd > 1.0f) rd = 1.0f; - // Centre enlarges gently; the rim returns to 1:1 instead of minifying - // neighbouring content. This keeps the native shallow-lens character. + // Centre ENLARGES (magnify); periphery SHRINKS toward RIM_SCALE (< 1) so the + // bar/other tabs seen through the drop read minified while the central glyph + // stays big. No shrink when settled (rimScale -> 1 as glassAmt -> 0). float edge = glassSmoothstep(LENS_MAG_FLAT, 1.0f, rd); float rimScale = 1.0f + (LENS_RIM_SCALE - 1.0f) * glassAmt; float mag = magnify + (rimScale - magnify) * edge; @@ -278,9 +273,7 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, int sg = (glassSampleBilinear(src, rw, rh, hw + (px / mag) * refr, hh + (py / mag) * refr + lift) >> 8) & 0xff; int sb = glassSampleBilinear(src, rw, rh, hw + (px / magB) * refr, hh + (py / magB) * refr + lift) & 0xff; float lum = 0.2126f * sr + 0.7152f * sg + 0.0722f * sb; - float t = tintStrength < 0.0f - ? -tintStrength * glassSmoothstep(LENS_LIGHT_KEY_LO, LENS_LIGHT_KEY_HI, lum) - : tintStrength * glassSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + float t = tintStrength * glassSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); float fr = sr + (tr - sr) * t; float fg = sg + (tg - sg) * t; float fb = sb + (tb - sb) * t; @@ -298,11 +291,8 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, float gx = px / hw, gy = (py + 0.42f * hh) / hh; float glare = LENS_GLARE * glassAmt * expf(-(gx * gx * 1.15f + gy * gy * 2.6f) * 2.1f); float rimW = LENS_RIM_W * hh; if (rimW < 2.0f) rimW = 2.0f; - float rim = depth < rimW ? (1.0f - depth / rimW) * LENS_RIM - * (0.35f + 0.65f * glassAmt) : 0.0f; - float lightDir = fmaxf(0.2f, fminf(1.0f, - 0.68f - 0.28f * (py / hh) - 0.12f * (px / hw))); - float bright = glare + rim * lightDir; + float rim = depth < rimW ? (1.0f - depth / rimW) * LENS_RIM : 0.0f; + float bright = glare + rim; if (bright > 0.0f) { fr += bright * (255.0f - fr); fg += bright * (255.0f - fg); @@ -311,9 +301,7 @@ static void glassApplyLens(uint32_t *src, int rw, int rh, uint32_t *out, // soft dark band just inside the rim (glass depth), morph-only float esW = 0.13f * minhh; if (esW < 2.0f) esW = 2.0f; if (depth < esW) { - float shadowDir = fmaxf(0.1f, fminf(1.0f, - 0.55f + 0.35f * (py / hh) + 0.10f * (px / hw))); - float es = (1.0f - depth / esW) * LENS_EDGE_SHADOW * glassAmt * shadowDir; + float es = (1.0f - depth / esW) * LENS_EDGE_SHADOW * glassAmt; fr *= (1.0f - es); fg *= (1.0f - es); fb *= (1.0f - es); diff --git a/Ports/iOSPort/nativeSources/iOSModernTheme.res b/Ports/iOSPort/nativeSources/iOSModernTheme.res index 98a82ad9bcee356ca6517cacde6c6dcd7d67f5a6..4909edc72674610ee440f9537426b3765e22daca 100644 GIT binary patch delta 1441 zcmZXTZBUd|6vy}6UEl%MW!YU>SeD&o(J9geTnN{f3Y00d)Ez~ovJ{o3WLZ`~(=Y^Q z8G6$J4su6%iy3LkG?^}Wofd^utgM_E#wA7w7$s{=)6y?CMRyna(DdOx_nhDV{LkFE z&pjg-!>{#*>t$q0CX`OgJ46V5%26(vW1DSgHU2zWQwEZ+75voCsM5R z#6pqkQZC?l%Kd5dY1%_TxxF18ZODk7*4>&=9I>~fH~rfHHPW*=ph9O&D0?SE(qVIM zEt|LE0H9^1p0*8x5{Fm*jn?O8Q}s@e*k8{MMZ#Q!=e6{5HRw^w_p-zJ=>)gG7{hM9 zPziWAU(G5v*;K51$1XM6D-MdK$FYj*V8(mNs>)cgz%NY3of0WAhJ;MhA(qu_3Utj>asA5P{4*C#Rsah_=lk zMht6m1g~p((|QHia3UcI`;RuyME8%zVrO%j+$J;N&K51+YCbisfJNkSIMHGkT5?cJ zZmpeh<+R4)gVr>Wm9)&pC2dNuvD~)uNbLXUpjdhoFAANYV3o%|Cd__v2n5yQ4zC(3 zk84@(si&f_=(IX=U7@RRTY2I3yn?N`rq_bIeE(&2zWJ!D*3d(niG)Xd+JFhKR;#J; zrg&niJ`;X*PK{60O894w7SEpBZLnl*b1$%Mea*dnaqtR6=M+zAVOd!c*8AeMrvJ+b zeHLL=3h zZv;JiFsvg?GqM+0rPAc7wVwO#!_Dt{w zGd__CPC`2u!5sebB0vaGQ$w198<)U$3c4!?bhy-}q?#=71T)^A1wR3uTm}Xz97^83 z46-30#Zuu>a%&D8d?Y0LX;(oxW{<_t!6K;RE*BsP(FY*iYz8Y&ErCwpqaL^dw7V3V z0v`h}E`uKk6_abku2o9DdpCR;lw;`kdqBar?18o6)K?A7eDeYL8MxC6TY>v(Ak09s z+d<2JY=?e5J;6o8KhB8D*mMS}2%r2M&I9l2fyd(L^{en1UF#P=VV_GFF8QH>@Pc8u z0etl}Sgod=x1fv4ZwqtDuh2+n#~o2-{tfzp-@Xgdli&!<1GT1-+L_RwN(R8eKTIPj zFoWcFG71hlVJC0RkfwApAa{t7v9%dQaZLuPGSS29h=y9$5f>LxB>@8#d=t+d8_3&0 RGxCWB-Cl{`*huz7`~#`59PI!A delta 2172 zcmZWrdr*{B6yI}Q_W7~Ac3D{34Nw!4hd_kmOgwBRguw=Uq-ZEK=_rDVp@lD2qbX-p z#DjBPP~4`bFio`2`tlL0ljAcL#m47=nKq&bSW}}-YIk?VmMRTaD{<%l~gu;zXN(p;aoJ7aYc1mH50zz%%hWu}B~4E3ZR{MZ*aV zK3}F28-i8p@PM%oJs~27QBW3Yc!DGF@kdc@(-G~q0YUa%Ru zYakL^9Y!2%nK2Y%V;*K&n92ff*q*?wp)DpFlSTwjvfLdJV&C>TdjD+Z?lq#bEr_1) z1vOT*EgEW3iUVyD@w9rxo(R#ky8{AUqQs`JXNO?7OOGWTp*Z*vTcaP=i-R4b!p#vm zHRbP2nOj-3pt3Tza>@Mi{6#eil8L5^`6N9brkXG2>roZWQ;6)Z@VJy@e z>4@<#3d@o_J1$Ub2H1Tn$&~>`gig+cBwCUSf|NZDJ_fgW4DFZz0zI1nHYt2Oe5Ry> z6Ts@@iVp&gEQUbOOaP^3-%WLRh7evyf6|kA#`B?=}-d(A&3Oe}{Nu`V75|`<*YOvA#YFNcF?EIIXS?T^| zta3L|NIf-hG5}=3vPnwm=yI5><;^zOVJ8o=%g&yK%Z=+`o-F_IA?Pifx#&PETQbJl zo(Ynyt#HuHBVGCynms&ALnQVTtG@%ge2pjH!wEUs;X{xd%^`QY;2=-fd>IbQ?!{N& zo_{_bU1g1C^P#3c^|Kqzz6PT?_t6h~;Q+^{--lf?{%Rka46fhDFj?Smvk~3+3Nh3E zL{iJo@1?VIY%&R=tx2Q<98zgANd~XcKgbtSNF+U;LQa8$ZX8QK2?V#NGB9Hm2B!Kn z@&b#IkVfn_|GGtCaa%OKJC%gv>^2p>UPKB5jooe0Qcf{>S1#Q-h4}sq(0$X0Nt!i{ H*meH`^X5g> diff --git a/Themes/iOSModernTheme.res b/Themes/iOSModernTheme.res index 98a82ad9bcee356ca6517cacde6c6dcd7d67f5a6..0b2d8c17a2df4cfeb96bb180f993ba27cf27e45f 100644 GIT binary patch delta 443 zcmca}gmda~&Iw|Sd>h538<@FT{!ji`FFrjlmWgL_L_;=M;8;Tsm^N&boSfIV5UNOc z`rPM??3;C(?h1lMc&1DO(VG`eabV;EG8p7lToQ}2r_WPil9|jt&0=yw70YJlX`w8P zx|1Ku>P$X5Yc`OTIJ<;dmqBWB;T++~uV=3Z%Nb2RJI4(ustFd=oZAi*-8i=dNaoKG zooqdihf#O4$2@z5$=TZkxPw!3Qj<$kQ$iAxCj0MKm|VIWRcyj;oyj7NLeu-=nK&lT z+${{!H2G?yz;v1Cj2x5s4#-VDu}@&KZ9B*0-+P27@7-4>rQnd9XQ-Q$nU`V(_5sjj zy@I0D;$r2=6L(7s%VA1SW;~z()G9E!en0Q#fc;!TlZ!99GnrU4@42{r&qYSdSVjww zm!~V~GqppRH%*!Nra#eVI?QOXeT4y&9wShk@u}`~79*xRj26=$8ZmVst6XBtbeP!! R7*4wLq<}hFO_;9h0sx+hsNMho delta 423 zcmbPwob%2R&Iw{1oD32wE{R3ilP_KtS!mS2{QC9R$sg;*C;K#%a|78>+2H9L;+e!J zKW)gJY`=+XvPGlNkg{wAsDskl^HtlSL-WPH6Be$-9$zCjaYXo18ySX!<)#CZ5Tg=Zj2#X~iV8`TD#K@{ z*wkS1xl0nwe3!TLU1n5>Wt<7}#B?1CCcf#J227PuKEEN;MaG%i9~v_0F+zoGjG68- i&YUh~!qfp3Id1||G<~5dlMIj&G-ndoe#w;Sur2_h)~L7u diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/TabSelectionMorphTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/TabSelectionMorphTest.java index 769f445a783..16113d2c319 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/TabSelectionMorphTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/TabSelectionMorphTest.java @@ -34,22 +34,6 @@ */ class TabSelectionMorphTest { - @Test - void visibleFramePacingPreventsAOneTickJump() { - assertEquals(8, TabSelectionMorph.paceVisibleFrame(0, 8), - "normal 60fps progress remains unchanged"); - assertEquals(14, TabSelectionMorph.paceVisibleFrame(0, 100), - "a completed wall-clock motion still presents an intermediate frame"); - int visible = 0; - int frames = 0; - while (visible < 100) { - visible = TabSelectionMorph.paceVisibleFrame(visible, 100); - frames++; - } - assertEquals(8, frames, "a coalesced transition still paints a short frame sequence"); - assertEquals(100, visible); - } - // A cell that travels from x=0..w=100 to x=200..w=100, bar inner-x 10, pill y=50 h=40. private static final int FROM_X = 0, FROM_W = 100, TO_X = 200, TO_W = 100; private static final int INNER_X = 10, CAP_Y = 50, CAP_H = 40; @@ -136,7 +120,8 @@ void barGrowFiresEarlyThenStops() { * (0, 10, 25, 50, 75, 90, 100): pill rect, lens rect, magnify, aberration, * flight envelope and the bar-grow flag are pinned EXACTLY per frame. This is * the numeric ground truth the fidelity animation-frame captures validate - * against -- any change to the measured monotonic motion path, lens size or tint + * against -- any change to the motion path, overshoot (see t=0.90: the pill + * passes the 220px target to 230px before settling), lens size or tint * timing shows up here as a frame-value diff, not as a vague "feels wrong". */ @Test @@ -144,11 +129,15 @@ void frameTableAtFixedProgressPoints() { // {t, capX, capW, lensX, lensY, lensW, lensH, magnify, aberration, flight, barGrow} Object[][] frames = { {0.00f, 20, 80, 20, 50, 80, 40, 1.0800f, 0.0000f, 0.0000f, false}, - {0.10f, 42, 105, 42, 39, 105, 54, 1.1726f, 0.0185f, 0.9259f, true}, - {0.25f, 101, 105, 101, 37, 105, 56, 1.1800f, 0.0200f, 1.0000f, true}, - {0.50f, 178, 105, 178, 37, 105, 56, 1.1800f, 0.0200f, 1.0000f, false}, - {0.75f, 213, 90, 213, 41, 90, 55, 1.1300f, 0.0100f, 0.5000f, false}, - {0.90f, 221, 89, 221, 49, 89, 42, 1.0800f, 0.0000f, 0.0000f, false}, + {0.10f, 32, 105, 32, 39, 105, 54, 1.1726f, 0.0185f, 0.9259f, true}, + {0.25f, 71, 105, 71, 37, 105, 56, 1.1800f, 0.0200f, 1.0000f, true}, + {0.50f, 164, 105, 164, 37, 105, 56, 1.1800f, 0.0200f, 1.0000f, false}, + {0.75f, 220, 90, 220, 41, 90, 55, 1.1300f, 0.0100f, 0.5000f, false}, + // t=0.9: the spring overshoots the target cell; the drop's right + // edge (230+98=328) would leave the bar (right edge 310), so the + // bar-bounds clamp trims the width -- the drop compresses against + // the bar end instead of painting over the backdrop. + {0.90f, 230, 80, 230, 49, 80, 42, 1.0800f, 0.0000f, 0.0000f, false}, {1.00f, 220, 80, 220, 50, 80, 40, 1.0800f, 0.0000f, 0.0000f, false}, }; for (Object[] f : frames) { @@ -177,24 +166,25 @@ void frameTableAtFixedProgressPoints() { @Test void presetIos26MatchesShippedTuning() { TabSelectionMorph.Tokens tk = TabSelectionMorph.Tokens.preset("ios26"); - assertEquals(0f, tk.stretch, 1e-6); - assertEquals(0.05f, tk.squashW, 1e-6); - assertEquals(0.01f, tk.grow, 1e-6); - assertEquals(0.04f, tk.squashH, 1e-6); - assertEquals(0.05f, tk.liftMm, 1e-6); - assertEquals(100, tk.bubbleWidthPct); - assertEquals(0, tk.overflowPct); - assertEquals(0f, tk.downBiasMm, 1e-6); - assertEquals(1.02f, tk.restMag, 1e-6); - assertEquals(1.08f, tk.peakMag, 1e-6); - assertEquals(0.0005f, tk.peakAb, 1e-6); + assertEquals(0.32f, tk.stretch, 1e-6); + assertEquals(0.16f, tk.squashW, 1e-6); + assertEquals(0.14f, tk.grow, 1e-6); + assertEquals(0.18f, tk.squashH, 1e-6); + assertEquals(0.5f, tk.liftMm, 1e-6); + // 109: the native selected pill is wider than its cell (overlaps the + // neighbours) -- measured 276px over a 253px cell on the @3x reference bar. + assertEquals(109, tk.bubbleWidthPct); + assertEquals(18, tk.overflowPct); + assertEquals(0.3f, tk.downBiasMm, 1e-6); + assertEquals(1.08f, tk.restMag, 1e-6); + assertEquals(1.18f, tk.peakMag, 1e-6); + assertEquals(0.02f, tk.peakAb, 1e-6); assertEquals(1f, tk.tintStrength, 1e-6); assertEquals(0, tk.barGrowPct); - assertEquals(0.35f, tk.spring, 1e-6, "native settle deformation is restrained"); + assertEquals(1f, tk.spring, 1e-6, "preset springiness is the reference bounce"); // unknown names fall back to ios26, "subtle" is a calmer variant assertEquals(tk.stretch, TabSelectionMorph.Tokens.preset("nonsense").stretch, 1e-6); - assertEquals(0f, TabSelectionMorph.Tokens.preset("subtle").stretch, 1e-6, - "both native presets keep the travelling drop to one cell"); + assertTrue(TabSelectionMorph.Tokens.preset("subtle").stretch < tk.stretch); } @Test @@ -205,91 +195,33 @@ void lensIntensityScalesOpticsNotGeometry() { assertEquals(1f, tk.peakMag, 1e-6); assertEquals(0f, tk.peakAb, 1e-6); assertEquals(0f, tk.tintStrength, 1e-6); - assertEquals(0f, tk.stretch, 1e-6, "geometry untouched by lens intensity"); + assertEquals(0.32f, tk.stretch, 1e-6, "geometry untouched by lens intensity"); TabSelectionMorph.Tokens twice = TabSelectionMorph.Tokens.preset("ios26"); twice.scaleLensIntensity(2f); - assertEquals(1.16f, twice.peakMag, 1e-5, "intensity 2 doubles the magnify delta"); + assertEquals(1.36f, twice.peakMag, 1e-5, "intensity 2 doubles the magnify delta"); assertEquals(1f, twice.tintStrength, 1e-6, "tint strength clamps at 1"); } @Test - void ios26PresetStaysWithinRecordedNativeDeformationBounds() { - TabSelectionMorph.Tokens tk = TabSelectionMorph.Tokens.preset("ios26"); - tk.liftPx = 1; - tk.downBiasPx = 0; - int maxWidth = 0; - int maxHeight = 0; - float maxAberration = 0f; - for (int frame = 0; frame <= 100; frame++) { - TabSelectionMorph m = TabSelectionMorph.compute(frame / 100f, - FROM_X, FROM_W, TO_X, TO_W, INNER_X, CAP_Y, CAP_H, - BAR_LEFT, BAR_RIGHT, tk); - maxWidth = Math.max(maxWidth, m.lensW); - maxHeight = Math.max(maxHeight, m.lensH); - maxAberration = Math.max(maxAberration, m.aberration); - } - assertTrue(maxWidth <= 100, - "native drop stays at one cell; it must not engulf two tab glyphs"); - assertTrue(maxHeight <= 44, - "native drop only subtly bulges beyond the 40px bar"); - assertTrue(maxAberration <= 0.00051f, - "native chromatic separation is a rim detail, not a visible double image"); - } - - @Test - void ios26PresetFrameTableMatchesRecordedTuning() { - TabSelectionMorph.Tokens tk = TabSelectionMorph.Tokens.preset("ios26"); - tk.liftPx = 1; - tk.downBiasPx = 0; - // {time, capX, capW, lensY, lensW, lensH, magnify, aberration, flight} - Object[][] frames = { - {0.00f, 10, 100, 50, 100, 40, 1.0200000f, 0.0000000f, 0.0000000f}, - {0.10f, 29, 100, 50, 100, 40, 1.0755556f, 0.0004630f, 0.9259260f}, - {0.25f, 88, 100, 49, 100, 40, 1.0800000f, 0.0005000f, 1.0000000f}, - {0.50f, 165, 100, 49, 100, 40, 1.0800000f, 0.0005000f, 1.0000000f}, - {0.75f, 202, 98, 50, 98, 40, 1.0500000f, 0.0002500f, 0.5000000f}, - {0.90f, 209, 99, 50, 99, 40, 1.0200000f, 0.0000000f, 0.0000000f}, - {1.00f, 210, 100, 50, 100, 40, 1.0200000f, 0.0000000f, 0.0000000f} - }; - for (Object[] frame : frames) { - float time = (Float) frame[0]; - TabSelectionMorph m = TabSelectionMorph.compute(time, - FROM_X, FROM_W, TO_X, TO_W, INNER_X, CAP_Y, CAP_H, - BAR_LEFT, BAR_RIGHT, tk); - String at = "t=" + time + " "; - assertEquals(((Integer) frame[1]).intValue(), m.capX, at + "capX"); - assertEquals(((Integer) frame[2]).intValue(), m.capW, at + "capW"); - assertEquals(((Integer) frame[3]).intValue(), m.lensY, at + "lensY"); - assertEquals(((Integer) frame[4]).intValue(), m.lensW, at + "lensW"); - assertEquals(((Integer) frame[5]).intValue(), m.lensH, at + "lensH"); - assertEquals((Float) frame[6], m.magnify, 5e-7f, at + "magnify"); - assertEquals((Float) frame[7], m.aberration, 5e-7f, at + "aberration"); - assertEquals((Float) frame[8], m.flight, 5e-7f, at + "flight"); - } - } - - @Test - void springinessControlsStopDeformationWithoutPositionOvershoot() { - // Sample the stop envelope at three springiness values. The native - // centre path remains monotonic; spring changes only the liquid - // width-compression/height-swell at the stop. + void springinessControlsOvershoot() { + // Sample mid-settle (t=0.9, inside the overshoot window) at three + // springiness values: no spring stops dead on an ease-out, more spring + // overshoots further. TabSelectionMorph.Tokens none = tokens(); none.spring = 0f; TabSelectionMorph.Tokens strong = tokens(); strong.spring = 2f; - TabSelectionMorph flat = TabSelectionMorph.compute(0.8f, FROM_X, FROM_W, TO_X, TO_W, - INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, none); - TabSelectionMorph reference = at(0.8f); - TabSelectionMorph elastic = TabSelectionMorph.compute(0.8f, FROM_X, FROM_W, TO_X, TO_W, - INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, strong); - assertTrue(flat.lensW > reference.lensW && reference.lensW > elastic.lensW, - "stronger spring compresses the stop width further"); - assertTrue(flat.lensH < reference.lensH && reference.lensH < elastic.lensH, - "stronger spring swells the stop height further"); + int xNone = TabSelectionMorph.compute(0.9f, FROM_X, FROM_W, TO_X, TO_W, + INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, none).capX; + int xRef = at(0.9f).capX; + int xStrong = TabSelectionMorph.compute(0.9f, FROM_X, FROM_W, TO_X, TO_W, + INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, strong).capX; + assertTrue(xNone < xRef, "reference bounce overshoots past the springless stop"); + assertTrue(xRef < xStrong, "stronger springiness overshoots further"); assertEquals(at(1f).capX, TabSelectionMorph.compute(1f, FROM_X, FROM_W, TO_X, TO_W, INNER_X, CAP_Y, CAP_H, BAR_LEFT, BAR_RIGHT, strong).capX, - "every springiness settles on the same target without overshoot"); + "every springiness settles on the same target"); } @Test @@ -305,7 +237,7 @@ void isDeterministic() { /** * A drop wider than its cell (bubbleWidthPct > 100, the shipped ios26 - * geometry) travelling to an END cell must never + * geometry) travelling to an END cell, with spring overshoot, must never * paint outside the bar: the native pill overlaps neighbour cells, not * the backdrop past the bar's rounded ends. */ diff --git a/native-themes/ios-modern/theme.css b/native-themes/ios-modern/theme.css index 582b2ce3ba9..98034faef99 100644 --- a/native-themes/ios-modern/theme.css +++ b/native-themes/ios-modern/theme.css @@ -96,7 +96,7 @@ controls: tabsMorphPreset "ios26" (default) | "subtle" tabsMorphLensIntensityPct scales the lens optics around 100 - tabsMorphSpringPct scales the settle deformation around 100 + tabsMorphSpringPct scales the settle overshoot around 100 tabsAnimatedIndicatorDurationInt the morph duration (ms), below */ tabsMorphPreset: "ios26"; /* Pressed = the SUSTAINED held-down look: iOS 26 dims a Liquid Glass button @@ -161,18 +161,11 @@ underline that tweens between tabs when the user changes selection. Off by default in the framework (so existing apps don't suddenly animate); on by default in the modern native - theme. */ + theme. Duration 200ms matches Material 3's reference. */ /* iOS 26 tab bars use a sliding CAPSULE selection indicator, not the Material 3 underline -- so the underline is off and the capsule is on. */ tabsAnimatedIndicatorBool: false; - /* The native last-to-first travel reaches its final resting x at ~350ms. - 200ms made JavaSE/JavaScript burn through the localized bubble motion so - quickly that it read as a jump. */ - tabsAnimatedIndicatorDurationInt: 350; - /* Native flat-backdrop samples: outer bar #cd, settled selection #b4 over - a #808080 backdrop. 72 alpha with the system-fill selection colour lands - on that measured contrast instead of an almost invisible inner pill. */ - tabSelPillAlphaInt: 72; + tabsAnimatedIndicatorDurationInt: 480; tabsAnimatedIndicatorThicknessMm: 1; /* iOS 26 sliding selection capsule: a single Liquid Glass blob behind the selected tab that tweens between tabs on selection change. Replaces the @@ -698,10 +691,10 @@ TabsContainer { /* Floating WHITE glass capsule (native iOS 26 bar): a translucent white fill over the blurred backdrop so it reads as a distinct white pill even over light-grey content, not a near-invisible frost. The blur still lets colour bleed through. */ - /* Sampled against the animation's flat #808080 stage: the native pill is - #cd, which is a 60% white composite. Keep the blur local so textured - backdrops retain their colour instead of cross-mixing adjacent regions. */ - background-color: rgba(255,255,255,0.60); + /* Sampled against the golden over the shared backdrop: the native pill is + only ~22% white over a LIGHTLY blurred local backdrop (yellow stays + yellow); 0.60/blur40 washed and cross-mixed the colours. */ + background-color: rgba(255,255,255,0.22); color: #3c3c43; /* Tight local blur: 24px still dragged neighbouring backdrop colour across the pill (blue bleeding into the warm left gap); native frost stays local. */ @@ -1153,27 +1146,29 @@ PopupContent { Tabs { background-color: transparent; } TabbedPane { background-color: transparent; } - /* Dark flat-backdrop sample is #58 over #808080, matching a roughly 38% - composite of the #16161a bar colour. */ - TabsContainer { background-color: rgba(22,22,26,0.38); color: #ebebf5; cn1-background-type: cn1-pill-border; backdrop-filter: blur(40px); border: none; } + /* Dark glass tab pill: translucent so the backdrop reads through like the + native Liquid Glass bar (0.55 was too opaque -> flat grey vs native's tint; + fully transparent left NO visible pill on plain-blur ports and even on + Metal read lighter than the native bar's slight dark wash). */ + TabsContainer { background-color: rgba(22,22,26,0.18); color: #ebebf5; cn1-background-type: cn1-pill-border; backdrop-filter: blur(40px); border: none; } Tab { color: #ebebf5; background-color: transparent; } /* Dark bottom tab bar: the selected item sits on a lighter frosted sub-capsule over the dark pill (layered glass), matching the native UITabBar. */ /* No per-tab background -- the sliding glass capsule is the only selection visual (a cn1-pill-border here double-painted a brighter inner pill / dark ring). */ - Tab.selected { color: #ebebf5; background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } - Tab.pressed { color: #ebebf5; background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } - SelectedTab { color: #ebebf5; background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } + Tab.selected { color: var(--accent-color-dark, #0a84ff); background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } + Tab.pressed { color: var(--accent-color-dark, #0a84ff); background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } + SelectedTab { color: var(--accent-color-dark, #0a84ff); background-color: transparent; margin: 0.18mm 0.4mm 0.18mm 0.4mm; } UnselectedTab { color: #ebebf5; } /* Dark overrides for the icon-only UIID. Background stays transparent so the pill shows through; only the glyph (fgColor) is forced white. */ TabIcon { color: #ffffff; background-color: transparent; } - /* The lens light-luminance key colours these white glyphs blue only while - the travelling drop covers them; the destination does not turn blue early. */ - TabIcon.selected { color: #ffffff; background-color: transparent; } - TabIcon.pressed { color: #ffffff; background-color: transparent; } + /* With the dark lens tint disabled (it flooded the dark bar solid blue) + the selected glyph carries the accent itself, like the native dark bar. */ + TabIcon.selected { color: #0a84ff; background-color: transparent; } + TabIcon.pressed { color: #0a84ff; background-color: transparent; } /* Dark glass panel: a translucent dark tint so the backdrop reads through as Liquid Glass (matches the dark UIVisualEffectView in the isolation tests). */ diff --git a/scripts/verify-javascript-lens-parity.mjs b/scripts/verify-javascript-lens-parity.mjs index 890df149fa7..e444315a279 100644 --- a/scripts/verify-javascript-lens-parity.mjs +++ b/scripts/verify-javascript-lens-parity.mjs @@ -28,11 +28,10 @@ vm.runInNewContext(bridge.substring(start, end) // The same foreground lens is implemented four times because each backend has // a different pixel API. Fail before the CRC probe if a tuning constant drifts. const lensConstants = [ - 'LENS_MAG_FLAT', 'LENS_TINT_HI', 'LENS_TINT_LO', - 'LENS_LIGHT_KEY_LO', 'LENS_LIGHT_KEY_HI', 'LENS_LIFT_COEF', + 'LENS_MAG_FLAT', 'LENS_TINT_HI', 'LENS_TINT_LO', 'LENS_LIFT_COEF', 'LENS_GLARE', 'LENS_RIM', 'LENS_RIM_W', 'LENS_REFRACT', 'LENS_EDGE_SHADOW', 'LENS_RIM_SCALE', 'LENS_GLASS_TINT_STR', - 'LENS_SAT_BOOST', 'LENS_GLASS_START', 'LENS_GLASS_FULL' + 'LENS_SAT_BOOST' ]; function constant(source, pattern, name, backend) { @@ -57,7 +56,25 @@ for (const name of lensConstants) { throw new Error(`Lens constant drift for ${name}: ${JSON.stringify(values)}`); } } -console.log(`lens constant parity PASS backends=4 constants=${lensConstants.length}`); + +// The liquid-layer gate (glassAmt = smoothstep(1.085, 1.25, magnify)) is a +// named pair only in the JavaScript port; the other backends inline the +// literals. Pin all four so the gate cannot drift silently either. +const gateStart = constant(bridge, `var\\s+%s\\s*=\\s*([0-9.]+)\\s*;`, 'LENS_GLASS_START', 'JavaScript'); +const gateFull = constant(bridge, `var\\s+%s\\s*=\\s*([0-9.]+)\\s*;`, 'LENS_GLASS_FULL', 'JavaScript'); +if (gateStart !== 1.085 || gateFull !== 1.25) { + throw new Error(`JavaScript liquid gate drift: ${gateStart}..${gateFull}, expected 1.085..1.25`); +} +for (const [backend, source, pattern] of [ + ['JavaSE', javaSource, /lensSmoothstep\(1\.085,\s*1\.25,\s*magnify\)/], + ['Metal CPU reference', metalReference, /glassSmoothstep\(1\.085f,\s*1\.25f,\s*magnify\)/], + ['Metal shader', metalShader, /cn1_lens_smoothstep\(1\.085,\s*1\.25,\s*magnify\)/] +]) { + if (!pattern.test(source)) { + throw new Error(`Liquid gate smoothstep(1.085, 1.25) not found in ${backend}`); + } +} +console.log(`lens constant parity PASS backends=4 constants=${lensConstants.length + 2}`); function crc32(bytes) { let crc = 0xffffffff; @@ -95,16 +112,18 @@ function glassPattern(width, height) { return source; } +// magnify/aberration sampled from the shipped "ios26" preset (rest 1.08x, +// peak 1.18x, peak aberration 0.02) at representative flight envelopes. +// Expected CRCs are produced by JavaSEPort.applyLensBuffer over the same +// synthetic source (see the LensParityDump probe in the PR description). const frames = [ - { progress: 0, width: 100, height: 40, magnify: 1.02, aberration: 0, expected: 0x54b0a28a }, - { progress: 10, width: 100, height: 40, magnify: 1.0755556, aberration: 0.000463, expected: 0x0cb3f189 }, - { progress: 25, width: 100, height: 40, magnify: 1.08, aberration: 0.0005, expected: 0xbb9b8ae3 }, - { progress: 50, width: 100, height: 40, magnify: 1.08, aberration: 0.0005, expected: 0xbb9b8ae3 }, - { progress: 75, width: 98, height: 40, magnify: 1.05, aberration: 0.00025, expected: 0x3a6519ac }, - { progress: 90, width: 99, height: 40, magnify: 1.02, aberration: 0, expected: 0x70153f60 }, - { progress: 100, width: 100, height: 40, magnify: 1.02, aberration: 0, expected: 0x54b0a28a }, - { progress: 'dark-25', width: 100, height: 40, magnify: 1.08, - aberration: 0.0005, tintStrength: -1, expected: 0x045fe638 } + { progress: 0, width: 100, height: 40, magnify: 1.08, aberration: 0, expected: 0xda50e7bd }, + { progress: 10, width: 100, height: 40, magnify: 1.1726, aberration: 0.01852, expected: 0xfb9082d1 }, + { progress: 25, width: 100, height: 40, magnify: 1.18, aberration: 0.02, expected: 0x766a3feb }, + { progress: 50, width: 100, height: 40, magnify: 1.18, aberration: 0.02, expected: 0x766a3feb }, + { progress: 75, width: 98, height: 40, magnify: 1.13, aberration: 0.01, expected: 0x4fe082c7 }, + { progress: 90, width: 99, height: 40, magnify: 1.08, aberration: 0, expected: 0xd0b8b264 }, + { progress: 100, width: 100, height: 40, magnify: 1.08, aberration: 0, expected: 0xda50e7bd } ]; for (const frame of frames) { @@ -128,8 +147,7 @@ for (const frame of frames) { putImageData(image) { output = image.data; } }; sandbox.applyLens(context, 0, 0, width, height, -1, - frame.magnify, frame.aberration, 0x0a84ff, - frame.tintStrength === undefined ? 1 : frame.tintStrength); + frame.magnify, frame.aberration, 0x0a84ff, 1); // JavaSE's reference buffer is packed ARGB; Canvas ImageData is RGBA. const actual = crc32(rgbaToArgb(output)); diff --git a/vm/ByteCodeTranslator/src/javascript/browser_bridge.js b/vm/ByteCodeTranslator/src/javascript/browser_bridge.js index 9828c9c1327..769d8e79725 100644 --- a/vm/ByteCodeTranslator/src/javascript/browser_bridge.js +++ b/vm/ByteCodeTranslator/src/javascript/browser_bridge.js @@ -1008,20 +1008,18 @@ var LENS_MAG_FLAT = 0.75; var LENS_TINT_HI = 150; var LENS_TINT_LO = 55; - var LENS_LIGHT_KEY_LO = 150; - var LENS_LIGHT_KEY_HI = 220; - var LENS_LIFT_COEF = 0.10; - var LENS_GLARE = 0.07; - var LENS_RIM = 0.11; - var LENS_RIM_W = 0.10; - var LENS_REFRACT = 0.015; - var LENS_EDGE_SHADOW = 0.07; - var LENS_RIM_SCALE = 1.0; + var LENS_LIFT_COEF = 0.40; + var LENS_GLARE = 0.09; + var LENS_RIM = 0.06; + var LENS_RIM_W = 0.06; + var LENS_REFRACT = 0.16; + var LENS_EDGE_SHADOW = 0.12; + var LENS_RIM_SCALE = 0.84; var LENS_GLASS_TINT = 0xbcd8ff; - var LENS_GLASS_TINT_STR = 0.0; - var LENS_SAT_BOOST = 1.12; - var LENS_GLASS_START = 1.025; - var LENS_GLASS_FULL = 1.075; + var LENS_GLASS_TINT_STR = 0.10; + var LENS_SAT_BOOST = 1.32; + var LENS_GLASS_START = 1.085; + var LENS_GLASS_FULL = 1.25; function lensSmoothstep(a, b, x) { var t = (x - a) / (b - a); @@ -1103,8 +1101,9 @@ var tg = (tintColor >> 8) & 0xff; var tb = tintColor & 0xff; var liftMax = LENS_LIFT_COEF * (magnify - 1.0) * hh; - // The bounded tab morph rests at 1.02x and peaks at 1.08x. Fade the - // foreground optics in above rest and reach full liquid character in flight. + // The 3D-glass cues (edge refraction / edge shadow / glare) belong to the + // MORPH droplet, not the settled pill (rest 1.08x, peak 1.18x): they fade + // in above rest so a resting selection stays a flat subtle pill. var glassAmount = lensSmoothstep(LENS_GLASS_START, LENS_GLASS_FULL, magnify); for (var yy = 0; yy < rh; yy++) { @@ -1145,9 +1144,7 @@ var sg = lensSample(src, rw, rh, hw + (px / mag) * refract, sampleYG, 1); var sb = lensSample(src, rw, rh, hw + (px / magB) * refract, sampleYB, 2); var lum = 0.2126 * sr + 0.7152 * sg + 0.0722 * sb; - var tint = tintStrength < 0 - ? -tintStrength * lensSmoothstep(LENS_LIGHT_KEY_LO, LENS_LIGHT_KEY_HI, lum) - : tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); + var tint = tintStrength * lensSmoothstep(LENS_TINT_HI, LENS_TINT_LO, lum); var fr = sr + (tr - sr) * tint; var fg = sg + (tg - sg) * tint; var fb = sb + (tb - sb) * tint; @@ -1162,11 +1159,8 @@ var gx = px / hw, gy = (py + 0.42 * hh) / hh; var glare = LENS_GLARE * glassAmount * Math.exp(-(gx * gx * 1.15 + gy * gy * 2.6) * 2.1); var rimWidth = Math.max(2.0, LENS_RIM_W * hh); - var rim = depth < rimWidth ? (1.0 - depth / rimWidth) * LENS_RIM - * (0.35 + 0.65 * glassAmount) : 0; - var lightDirection = Math.max(0.2, Math.min(1.0, - 0.68 - 0.28 * (py / hh) - 0.12 * (px / hw))); - var bright = glare + rim * lightDirection; + var rim = depth < rimWidth ? (1.0 - depth / rimWidth) * LENS_RIM : 0; + var bright = glare + rim; if (bright > 0) { fr += bright * (255 - fr); fg += bright * (255 - fg); @@ -1174,10 +1168,7 @@ } var edgeShadowWidth = Math.max(2.0, 0.13 * Math.min(hw, hh)); if (depth < edgeShadowWidth) { - var shadowDirection = Math.max(0.1, Math.min(1.0, - 0.55 + 0.35 * (py / hh) + 0.10 * (px / hw))); - var edgeShadow = (1.0 - depth / edgeShadowWidth) * LENS_EDGE_SHADOW - * glassAmount * shadowDirection; + var edgeShadow = (1.0 - depth / edgeShadowWidth) * LENS_EDGE_SHADOW * glassAmount; fr *= 1 - edgeShadow; fg *= 1 - edgeShadow; fb *= 1 - edgeShadow; From 0e672affed09017168dc3c14ecc2ba2bf1e60111 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 16 Jul 2026 12:13:44 +0300 Subject: [PATCH 05/13] Refresh tab-morph motion references; add the CN1 motion baseline videos Re-record native-tabs-{light,dark}.mov from the live UITabBar on the current iOS 26.3 simulator runtime. The fresh recordings confirm the old video was not stale: Apple toned the Liquid Glass tab drop down in later 26.x updates, so current UIKit shows a flat compact drop -- the frosted refracting bubble from the original iOS 26 design (the developer-guide fidelity image and the committed TabsMorph frame goldens) can no longer be captured natively. Codename One deliberately keeps the original frosted-bubble design, so add cn1-tabs-{light,dark}.mov: the shipped morph in motion, rendered deterministically from the JavaSE simulator at the reference density (60fps, the theme's 480ms timeline, 1088x290 tile) with the restored model and the new glassRegion material. A README in the anim goldens dir now states which video is authoritative for what: natives for timing/travel, the CN1 baseline and the frame goldens for material -- never tune against the flat native material. Also note in verify-javascript-lens-parity.mjs that the glass material/optics CRC pins are shared by three CPU implementations (iOS, JavaSE, browser bridge); JavaSE's new glassMaterialInPlace/applyGlassOptics reproduce them bit-exactly. Co-Authored-By: Claude Fable 5 --- .../goldens/ios-26-metal-anim/README.md | 28 ++++++++++++++++++ .../ios-26-metal-anim/cn1-tabs-dark.mov | Bin 0 -> 92721 bytes .../ios-26-metal-anim/cn1-tabs-light.mov | Bin 0 -> 110285 bytes .../ios-26-metal-anim/native-tabs-dark.mov | Bin 175723 -> 155599 bytes .../ios-26-metal-anim/native-tabs-light.mov | Bin 186285 -> 183086 bytes scripts/verify-javascript-lens-parity.mjs | 8 +++++ 6 files changed, 36 insertions(+) create mode 100644 scripts/fidelity-app/goldens/ios-26-metal-anim/README.md create mode 100644 scripts/fidelity-app/goldens/ios-26-metal-anim/cn1-tabs-dark.mov create mode 100644 scripts/fidelity-app/goldens/ios-26-metal-anim/cn1-tabs-light.mov diff --git a/scripts/fidelity-app/goldens/ios-26-metal-anim/README.md b/scripts/fidelity-app/goldens/ios-26-metal-anim/README.md new file mode 100644 index 00000000000..2791435152a --- /dev/null +++ b/scripts/fidelity-app/goldens/ios-26-metal-anim/README.md @@ -0,0 +1,28 @@ +# Animation references — which video is authoritative for what + +Two families of motion references live here, and they intentionally disagree +about the tab morph's material: + +- **`native-tabs-*.mov`** — the REAL `UITabBar` transition recorded from the + NativeRef app on the iOS 26 simulator runtime named by this golden set + (re-record with `scripts/record-ios-native-anim.sh tabs `). On + the original iOS 26 runtime the drop was a pronounced frosted, refracting + bubble (see the developer-guide fidelity image); Apple toned the effect down + in later 26.x updates, so a fresh recording shows a flat, compact drop. + These recordings answer "what does current UIKit do" — they are the honest + native baseline for TIMING and TRAVEL, **not** the material target. + +- **`cn1-tabs-*.mov`** — the Codename One morph in motion, rendered + deterministically from the JavaSE simulator at the reference density + (60 fps, the theme's 480 ms `tabsAnimatedIndicatorDurationInt` timeline, + 1088x290 tile). This is the SHIPPED look — the frosted-bubble material + pinned by `../ios-26-metal-frames/TabsMorph_*` and the developer-guide + fidelity image — kept deliberately at the original iOS 26 design. + **Tune the morph model, lens constants, and theme against THIS and the + frame goldens, never against the flat `native-tabs-*.mov` material.** + +- **`native-switch-*.mov`** — the real `UISwitch` toggle; unaffected by the + above (the switch motion did not change between 26.x updates). + +The four lens/glass implementations (Metal shader, iOS CPU reference, JavaSE, +JavaScript) are held together by `scripts/verify-javascript-lens-parity.mjs`. diff --git a/scripts/fidelity-app/goldens/ios-26-metal-anim/cn1-tabs-dark.mov b/scripts/fidelity-app/goldens/ios-26-metal-anim/cn1-tabs-dark.mov new file mode 100644 index 0000000000000000000000000000000000000000..bfcc65a1989eb9a4fee17f9b5d4628d630d2b360 GIT binary patch literal 92721 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 Z$QkloJzb!~26bTLU=217qTnSW_8-T@Fkt`y literal 0 HcmV?d00001 diff --git a/scripts/fidelity-app/goldens/ios-26-metal-anim/cn1-tabs-light.mov b/scripts/fidelity-app/goldens/ios-26-metal-anim/cn1-tabs-light.mov new file mode 100644 index 0000000000000000000000000000000000000000..8a0059df154f56ac443d3edf7ee83a613310788f GIT binary patch literal 110285 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%0ALZAt@>9_334FnepVwpR?wjlkkl|b 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