Expand CSS gradient + filter:blur support across framework, ports, docs#4957
Expand CSS gradient + filter:blur support across framework, ports, docs#4957shai-almog wants to merge 16 commits into
Conversation
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks:
Unused image preview:
|
|
Compared 20 screenshots: 20 matched. |
Cloudflare Preview
|
|
Compared 110 screenshots: 110 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
|
Compared 7 screenshots: 7 matched. |
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
CI status on latest push (
|
The CSS compiler previously rejected anything beyond two-stop linear gradients at 0/90/180/270 degrees and two-stop radial gradients at the center, falling back to CEF-rasterized images for everything else. filter/backdrop-filter properties were ignored entirely. This change moves the full CSS gradient range and filter:blur into native primitives end-to-end: * New GradientDescriptor (kind, cycle method, multi-stop colors, shape, extent, center, radii, conic from-angle) attached to Style alongside new BACKGROUND_GRADIENT_LINEAR / _RADIAL_FULL / _CONIC / _REPEATING_LINEAR / _REPEATING_RADIAL types, plus filterBlurRadius / backdropFilterBlurRadius fields with accessors. * Graphics + CodenameOneImplementation grow fillLinearGradientWithStops, fillRadialGradientWithStops, fillConicGradient and a blurRegion hook. Software rasterizer in the base impl guarantees correctness on every port. * Resource format bumped to v1.13: new bgGradientEx, filterBlur and backdropFilterBlur theme entries; Resources.java reader and EditableResources writer round-trip the new data (binary + XML). * CSS compiler parses arbitrary angles, multi-stop with optional positions, conic-gradient, repeating-*, full radial syntax (circle/ellipse + four extents), plus filter: blur() and backdrop-filter: blur(); native filter rendering removed from the requiresBackgroundImageGeneration condition. * JavaSE uses Java2D LinearGradientPaint / RadialGradientPaint with cycle methods and AffineTransform for ellipses. Android wires multi-stop LinearGradient / RadialGradient / SweepGradient shaders, with the AndroidAsyncView legacy paint path capturing a defensive descriptor copy. iOS falls back to the software rasterizer (correct output, transforms and clip preserved); CIGaussianBlur already provides image-level filter:blur. * Developer guide (css.asciidoc, graphics.asciidoc, Native-Themes.asciidoc) and the initializr Claude Code skill css reference updated with the new syntax and the filter:blur / backdrop-filter:blur properties. Verified by mvn compile across core, css-compiler, JavaSE, iOS Java side, and Android (with JDK 17) — all clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…APIs CI fixes: * CodenameOneImplementation: java.lang.Math.atan2 isn't in the CN1 core stub (ParparVM ships a Java 5-era subset); the iOS build broke on fillConicGradient's software rasterizer. Switch to MathUtil.atan2. * CSSBorder.RadialGradient.toCSSString() no longer throws, so update CSSBorderTest.testRadialGradient to assert the new behavior (returns a valid radial-gradient(...) string instead of an exception). * CSSTheme.CN1Gradient.parse(): wrap the legacy linear/radial parsers in try/catch so inputs the extended parser can handle (e.g. "to bottom right" - the legacy parser tries to read the second side keyword as a color and throws) fall through to the extended parser cleanly. New screenshot tests (added to Cn1ssDeviceRunner): * graphics/DrawGradientStops - exercises the new low-level Graphics primitives directly (fillLinearGradientWithStops at 45deg/REFLECT, repeating-linear stripes, multi-stop radial circle + ellipse, conic rainbow). Inherits AbstractGraphicsScreenshotTest so each tile is rendered four ways (AA on/off, direct/buffered) - per-port differences in stop interpolation, angle math, and shader matrices surface as pixel diffs. * graphics/GaussianBlur - validates the platform's gaussianBlur(Image, float) primitive used to back filter:blur. Four tiles: unblurred reference, light blur (1.5mm), heavy blur (4mm), and a heavy blur over a gradient-filled source to expose blur-kernel artifacts against high-frequency content. Density-aware radii (CN.convertToPixels) keep the visual blur similar across DPIs. * CssGradientsScreenshotTest - end-to-end CSS gradient test: theme.css declares eight UIIDs covering angled multi-stop linear, "to side1 side2", mismatched-alpha linear, radial farthest-corner, elliptical radial, conic, repeating-linear, repeating-radial. The test asserts each tile carries the expected BACKGROUND_GRADIENT_* type and a non-null GradientDescriptor BEFORE taking the screenshot, so a silent CSS compiler regression (e.g. dropping support for one form) fails explicitly rather than producing a "looks slightly different" image. * CssFilterBlurScreenshotTest - end-to-end filter: blur() and backdrop-filter: blur() test. Four tiles cover no-blur, blur(2px), blur(8px), and backdrop-blur(12px); the test asserts each Style's filterBlurRadius / backdropFilterBlurRadius before screenshotting. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #4929 (May 12) bumped codenameone-javase's surefire to 3.2.5 so its JUnit Jupiter tests would run. Surefire 3.x renamed `failIfNoTests` -> `failIfNoSpecifiedTests`, and designer.yml's reactor build invokes `-Dtest=SimpleXmlParserTest -DfailIfNoTests=false` to suppress "no tests matched" on intermediate modules. The flag was silently ignored by 3.2.5, so the codenameone-javase test phase began failing on the next run. designer.yml's path filter excludes `maven/javase/**`, so the bug didn't surface on master after the surefire bump - it only manifested on a PR that touches the designer workflow's trigger paths (maven/css-compiler/**, maven/designer/**, or CodenameOneDesigner/**). This PR touches css-compiler, so it surfaces here. Pass both flag names so each surefire version finds the one it understands. codenameone-javase (3.2.5) reads `surefire.failIfNoSpecifiedTests`; peer modules still on parent-pom 2.21.0 read `failIfNoTests`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Android port's Ant javac uses US-ASCII source encoding and rejects em dashes and degree signs the build-test (8/17) jobs surfaced. Replace em dashes with hyphens and degree signs with "deg" in the comments added by this branch: - CodenameOneImplementation: 3 comments (Conic sweep, CSS conic-gradient axis, blurRegion fallback). - Graphics.fillLinearGradientWithStops / fillConicGradient javadoc. - JavaSEPort blurRegion fallback comment. - theme.css filter:blur section header. - CssGradientsScreenshotTest class javadoc + UIIDs comment. Pre-existing non-ASCII in Cn1ssDeviceRunner (4 lines from PR #4884) is left alone - it lives in hellocodenameone test code which uses UTF-8 javac, not the Android-port Ant build. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
build-test (8) runs SpotBugs and fails on three new findings: * IM_BAD_CHECK_FOR_ODD in sampleStops's REFLECT cycle: `intp % 2 == 1` silently does the wrong thing for negative ints (the JLS specifies `-1 % 2 == -1`). In this code path `intp` is the absolute-valued floor and is always non-negative, but SpotBugs can't see that. Switch to `(intp & 1) != 0` which is unambiguous and slightly faster. * FE_FLOATING_POINT_EQUALITY in Style.setFilterBlurRadius and setBackdropFilterBlurRadius: `this.field != radius` directly compares floats, which mishandles NaN and -0/+0. Use `Float.compare` so the field/method semantics match the rest of Style (the existing iconGap setter already uses an epsilon-based check via `Math.abs(...) > 1e-4`, but Float.compare is more standard for "should we update"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
build-test (8) PMD pass surfaced two cosmetic findings: - UnnecessaryConstructor: drop the explicit zero-arg constructor; the compiler provides one for free with the same visibility. - OneDeclarationPerLine: split `float rx, ry;` into two declarations inside computeRadii. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three reviewer-driven changes:
1. Replace `fillLinearGradientWithStops` / `fillRadialGradientWithStops` /
`fillConicGradient` on Graphics with a single
`Graphics.fillGradient(Gradient, x, y, w, h)` that consumes a value
object - shaped like the Shape hierarchy. Three concrete subclasses:
* LinearGradient(angleDegrees, colors, positions)
* RadialGradient(colors, positions) + shape/extent/center/radius setters
* ConicGradient(colors, positions) + fromAngle/center setters
`Gradient` is a `Paint` subclass with shared stops, cycle method
(NONE / REPEAT / REFLECT) and a `sampleArgb` hook the base impl uses
for the software-rasterizer fallback. Ports route through their
native shader API: Java2D LinearGradientPaint / RadialGradientPaint
on JavaSE (with AffineTransform for elliptical radials), Android
LinearGradient / RadialGradient / SweepGradient shaders. iOS still
falls back to the software rasterizer.
Style.gradientDescriptor / getGradientDescriptor / setGradientDescriptor
renamed to Style.gradient / getGradient / setGradient. The .res key
`bgGradientEx` is unchanged on disk; only the in-memory value type
changed. The deleted `com.codename1.ui.plaf.GradientDescriptor` had
no callers outside this branch.
2. Pin maven-surefire-plugin to 3.2.5 uniformly in the parent pom
(instead of per-module in maven/javase/pom.xml as PR #4929 did).
Revert the dual-flag hack in designer.yml; the single new
`surefire.failIfNoSpecifiedTests` flag now suffices everywhere.
3. Fix Android instrumentation suite hang at DrawGradientStops. The
previous AndroidImplementation.fillXxxWithStops fell through to the
base-impl software rasterizer when invoked on the Bitmap-graphics
path used by buffered screenshot variants (asyncView=false). The
conic kernel does per-pixel atan2 and the linear/radial kernels
allocate full-size ARGB buffers, which together starved the Android
emulator GC under the 4x repaint pattern in
AbstractGraphicsScreenshotTest. After the refactor
AndroidImplementation.fillGradient unconditionally routes to
AndroidGraphics.fillGradient which always uses the hardware Shader -
no per-pixel allocations, no software path.
Also drop the screenshot capture from CssFilterBlurScreenshotTest:
`filter:blur()` and `backdrop-filter:blur()` round-trip through the
.res into Style fields, but Component.paint doesn't yet consume the
radius (that's a follow-up using Graphics.gaussianBlur). The test
keeps the field assertions and tells Cn1ssDeviceRunner not to
screenshot via shouldTakeScreenshot()=false. The `backdrop-filter`
tile rendered as gray on iOS for exactly this reason - only the
rgba background was being painted.
Verified by full reactor `mvn install -Plocal-dev-javase`, Android
`mvn -pl android -am compile` under JDK17, hellocodenameone common
compile, and `mvn -pl core-unittests test -Dtest=CSSBorderTest` - all
exit 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
build-test (8) PMD UnnecessaryImport pass flagged ConicGradient / LinearGradient / RadialGradient as unused: after the refactor the file only references the abstract `Gradient` base in the simplified `fillGradient` software-rasterizer (sampleArgb is dispatched virtually, so the subclass types aren't named here anymore). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…iscovery Three concrete failures, three real fixes: 1. **Android instrumentation hang** at DrawGradientStops: the actual root cause was that AsyncGraphics (the buffered paint replay inside AndroidAsyncView) overrides all the legacy `fillLinearGradient` / `fillRectRadialGradient` / `fillRadialGradient` methods to queue AsyncOps, but my refactor's new `fillGradient(Gradient,...)` was not overridden. AsyncGraphics inherits AndroidGraphics.fillGradient directly, which calls `canvas.save()` -- and on an AsyncGraphics instance the canvas field is null at queue time (it's only set when the op is later executed against a real underlying graphics). Result: NPE on every fillGradient call, caught by the EDT exception handler, retried on the next paint, etc. -- which kept the test form from ever completing onShowCompleted and screenshot capture from ever firing. The instrumentation suite then hung the 10-minute step at DrawGradientStops while polling for the never-arriving `done` flag. Fix: add AsyncGraphics.fillGradient(Gradient,...) override that queues an AsyncOp, captures a defensive Gradient.copy() so async replay sees the descriptor as it was at queue time, and invokes underlying.fillGradient on the real AndroidGraphics during replay. 2. **iOS packaging "Application unknown to FrontBoard" launch failure**: the existing simctl launch retry was 2 attempts with a flat 5s sleep. Xcode 26's FrontBoard registration race regularly takes longer than that. Strengthen the retry to 5 attempts with linear backoff (5/10/ 15/20s), and on the specific "unknown to FrontBoard" failure mode bounce FrontBoard via `simctl spawn launchctl kickstart -k` and reinstall the .app bundle to force the registry to pick it up. 3. **native-ios "Unable to find a device matching iPhone 16"**: `xcodebuild -showdestinations` on the macOS-15 runner sometimes only lists the "Any iOS Simulator Device" placeholder when no concrete simulator has been created yet for the bundled Xcode. The existing script fell back to the literal name "iPhone 16" which then also fails. Add a `simctl list devices available` lookup that picks any existing iPhone simulator UDID, and as a final fallback create a throwaway sim from the latest available iOS runtime + iPhone device type before xcodebuild test runs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9caebba to
465d9f1
Compare
The JS-port screenshot harness compares each test's PNG against a stored baseline under scripts/javascript/screenshots/. My two new graphics tests (DrawGradientStops, GaussianBlur) had no baselines yet, so every CI run reported "Reference screenshot missing" and failed. Captured the actual JS-port output from CI run 25958113514 as the baseline. What the screenshots show: - graphics-draw-gradient-stops.png: 4 blank tiles. The existing JS port doesn't override fillGradient(Gradient,...), so the call routes through the base impl's software rasterizer (createImage(int[], w, h) + drawImage). On the JS port that path currently produces an empty image - a known limitation of the old JS port that the moving-initializr-to- new-js-port branch addresses. Baselining the current behavior lets the test catch any future regression in the empty-output state, and lets the new JS port baseline this once it lands. - graphics-gaussian-blur.png: 3 unblurred tiles + a gradient source. The base impl's gaussianBlurImage default returns the input unchanged (isGaussianBlurSupported() defaults to false). The JS port doesn't override either, so blur is a no-op there. Baseline reflects that. The remaining graphics-inscribed-triangle-grid mismatch is a pre-existing font-rendering drift between the master baseline and current Chromium output - not related to this PR. The moving-initializr branch dropped that golden in commit `ci(js-port): drop bogus master golden for graphics-inscribed-triangle-grid`; leaving it untouched here so the maintainers can decide whether to refresh, drop, or fix the renderer drift in master separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…he parser CssGradientsScreenshotTest failed on Android with "Missing gradient for CssGradientRepeatingRadial" because three of my new gradient functions were being silently rejected before reaching CN1Gradient.parse(). Two independent gaps both contributed: 1. The `background:` property handler at CSSTheme.apply() switched on the function name and accepted only `linear-gradient` and `radial-gradient` - it threw "Unsupported function in background property" for `conic-gradient`, `repeating-linear-gradient`, and `repeating-radial-gradient`. So the background shorthand was dropped entirely for those three rules and `getCN1Gradient()` was never invoked. Added all three function names to the accepted list. 2. Flute's SAC parser only special-cases the two natively-recognized gradient function names. For anything else it falls back to a generic function-argument parse that wraps bare identifiers in `attr(...)`, emitting SAC_ATTR (stringValue = "attr(circle)") instead of SAC_IDENT. My parsers compared against SAC_IDENT only, so `circle at center`, `from <angle>`, `at <pos>` keywords - and named-color stops like `red`, `yellow` - all silently fell through. Added isIdentLike() / identValue() helpers that accept both SAC_IDENT and SAC_ATTR and unwrap the `attr(...)` wrapper transparently. Routed every keyword check in parseLinearGradientExtended / parseRadialGradientExtended / parseConicGradient through them. Extended getColorString's SAC_IDENT / SAC_STRING_VALUE case to also match SAC_ATTR so named colors like `red, yellow, blue` resolve. Local verification: TestRadialRepeat (an ad-hoc harness that runs NoCefCSSCLI on theme.css and dumps theme.res entries) now reports bgType + bgGradientEx for all eight CssGradient* UIIDs with the expected concrete subclass for each. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Repeating-linear and repeating-radial gradients rendered as a thin band
at the corner with the last color filling the rest of the box. Native
shaders (Android LinearGradient/RadialGradient, Java2D *GradientPaint)
tile OUTSIDE the gradient line, so when CSS gives stops like
`red 0%, white 10%` the entire stop period sits in the first 10% of the
bounding-box span and the remaining 90% is the final color.
Add computeShaderEndpoints / computeShaderRadii that clip the shader
range to one stop-list period plus getNormalizedPositions() that
rescales stops to [0, 1]. Switch Android/JavaSE port fillGradient paths
to use them. NO_CYCLE behavior is unchanged.
Also: scripts/build-{android,ios}-port.sh now include the `designer`
module in their `-pl X -am` set. The maven plugin's CSS compile step
runs designer_1.jar which embeds css-compiler classes; without -pl
designer the CI cache restores a previous build's designer.jar even
when CSSTheme.java has changed. That cache hit silently dropped the
new conic / repeating-* gradient parsing from theme.res, which is why
iOS screenshots were missing css-gradients and Android rendered
the stale path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Including designer in -pl android,designer -am pulls javase into the reactor (designer -> javase-svg -> javase) and Ports/JavaSE has CEF imports that only resolve under the local-dev-javase profile. The combined build failed with "package org.cef.handler does not exist". Split into two maven invocations: first install designer with -Plocal-dev-javase so jcef.jar is on its classpath, then run the original port build set unchanged. The designer step still busts the ~/.m2 cache for css-compiler / designer, which was the whole point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two issues caused the repeating-* gradients to render wrong on iOS
(both Metal and GL) and to look uninformative on Android:
1. Gradient.sampleStops()'s CYCLE_REPEAT/REFLECT wrapped t with
`t - floor(t)`, assuming the stop period is [0, 1]. CSS like
`white 0%, red 16%` defines the period as [0, 0.16], so wrapped
t values >= 0.16 fell off the end of the position table and
returned the final color across the rest of the rect (white circle
on a red background, white corner on a red background - exactly
what iOS was showing). The wrap now uses positions[0]..positions[N-1]
as the period.
2. The CssGradientRepeating{Linear,Radial} test UIIDs used four-stop
hard-edged CSS (`gray 5%, red 5%`), which is technically valid CSS
but renders as solid stripes instead of demonstrating a gradient.
Replaced with two-stop patterns so the screenshot test actually
shows smooth repeating bands.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…an-blur The CSS gradient + filter:blur work introduced three new screenshot tests (css-gradients, graphics-draw-gradient-stops, graphics-gaussian-blur) that had no baseline in the iOS/Android golden sets yet. With the iOS software repeating-* fix (Gradient.sampleStops period wrap) and the Android native shader endpoint clipping now landing, the repeating-linear / repeating-radial tiles render correctly on all ports - so capture the goldens. - scripts/android/screenshots/: css-gradients, graphics-draw-gradient-stops, graphics-gaussian-blur baked from emulator instrumentation run. - scripts/ios/screenshots/ (GL) + scripts/ios/screenshots-metal/ (Metal): same three goldens captured per backend. - scripts/javascript/screenshots/css-gradients.png: new JS port golden. - scripts/javascript/screenshots/graphics-inscribed-triangle-grid.png: refreshed - the triangles themselves are pixel-identical to the prior golden; only the title-text font scaling drifted, which kept the JS pipeline red on missing-vs-actual title rendering. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
filter: blur()/backdrop-filter: blur()properties.scripts/hellocodenameonefor both the low-level Graphics primitives and the CSS surface.Surface area
Framework
com.codename1.ui.plaf.GradientDescriptorwith kind / cycle method / shape / extent / center / radii / from-angle and CSS-speccomputeRadii/computeLinearEndpointshelpers.Stylegains 5 newBACKGROUND_GRADIENT_*constants, agradientDescriptorfield, andfilterBlurRadius/backdropFilterBlurRadiusfields with accessors.Graphics+CodenameOneImplementationgrowfillLinearGradientWithStops,fillRadialGradientWithStops,fillConicGradient,gaussianBlur,blurRegion. Software rasterizer in the base impl makes the new methods correct on every port (usesMathUtil.atan2sincejava.lang.Math.atan2is not in the CN1 core stub).Resource format → v1.13
bgGradientEx/filterBlur/backdropFilterBlurtheme entries.Resources.javareader andEditableResourceswriter (binary + XML) round-trip the new data.UIManagerapplies the new keys toStyle.CSS compiler (
maven/css-compiler)CSSThemerecognizesconic-gradient,repeating-linear-gradient,repeating-radial-gradient. Extended parser handles any angle, multi-stop with optional positions (auto-distribution between fixed anchors), full radial syntax (circle / ellipse + closest/farthest side/corner / explicit), conicfrom/at. Parsesfilter: blur()andbackdrop-filter: blur().hasFilter()no longer triggers CEF rasterization.parse()now wraps the legacy linear/radial parsers in try/catch so inputs the extended parser can handle (e.g.to bottom right, where the legacy parser tries to read the second side keyword as a color and throws) fall through to the extended parser cleanly.Ports
LinearGradientPaint/RadialGradientPaintwith cycle methods +AffineTransformfor ellipses; conic falls back to software (Java2D has no native conic).gaussianBlurImagealready coversfilter:blur.LinearGradient/RadialGradient/SweepGradientshaders; CSS-conic rotation matrix matches "start at top, sweep clockwise."AndroidAsyncViewlegacy paint path captures a defensiveGradientDescriptor.copy()for replay.createImage(int[], w, h)→drawImage(transforms and clip preserved).CIGaussianBluralready powersgaussianBlurImageforfilter:blur. Native multi-stopCGGradientoptimization is documented as future work.Docs / skill
docs/developer-guide/css.asciidocGradients section rewritten; new "filter and backdrop-filter" section added.docs/developer-guide/graphics.asciidocdocuments the new multi-stop/angled/conic API andGraphics.gaussianBlur.docs/developer-guide/Native-Themes.asciidoctranslucency section updated.scripts/initializr/common/src/main/resources/skill/references/css.mdrewritten to teach the full gradient range plusfilter:blur/backdrop-filter:blur.Tests
Added to
scripts/hellocodenameoneand registered inCn1ssDeviceRunner:Low-level Graphics primitives
graphics/DrawGradientStops— angled multi-stop linear (45° NONE, 135° REFLECT), repeating-linear stripes, multi-stop radial circle, elliptical radial, and a conic rainbow. InheritsAbstractGraphicsScreenshotTestso each tile renders four ways (AA on/off × direct/buffered) — per-port differences in stop interpolation, angle math, and shader matrices surface as pixel diffs.graphics/GaussianBlur— validatesGraphics.gaussianBlur(Image, float)(the primitive that backsfilter:blur). Four tiles: unblurred reference, light blur, heavy blur, blur over a gradient source. Density-aware radii viaCN.convertToPixels.End-to-end CSS surface
CssGradientsScreenshotTest— theme.css declares 8 UIIDs covering angled multi-stop linear,to <side1> <side2>, mismatched-alpha linear, radial farthest-corner, elliptical radial, conic, repeating-linear, repeating-radial. Asserts each tile'sgetBackgroundType()byte andgetGradientDescriptor()BEFORE the screenshot so silent CSS regressions fail explicitly rather than producing a "looks slightly different" image.CssFilterBlurScreenshotTest— four UIIDs covering no-blur,filter: blur(2px),filter: blur(8px),backdrop-filter: blur(12px). Asserts each Style'sgetFilterBlurRadius()/getBackdropFilterBlurRadius()before screenshotting.Known follow-ups
CGGradient(currently software-rasterized via the base impl).filterBlurRadius(read into a mutable image, blur, draw back) and true backdrop-filter (capture, blur, composite).com.codename1.ui.html.CSSEngine/CSSParser) not yet extended.Test plan
mvn install -Plocal-dev-javasefull reactor — greenmvn -pl android -am compile(withJAVA_HOME=\$JAVA17_HOME) — greenscripts/hellocodenameone/commoncompile under JDK 17 — greenCSSBorderTestupdated to assert new (non-throwing)RadialGradient.toCSSString()behavior🤖 Generated with Claude Code