Skip to content

Fail the CSS compile on @font-face rules the runtime can't honor - #5506

Closed
shai-almog wants to merge 2 commits into
docs/css-font-ttf-onlyfrom
feature/css-font-face-validation
Closed

Fail the CSS compile on @font-face rules the runtime can't honor#5506
shai-almog wants to merge 2 commits into
docs/css-font-ttf-onlyfrom
feature/css-font-face-validation

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Stacked on #5502 — review/merge that first; this PR's diff is only the validation change.

Why

An .otf sailed through compilation and only broke on the device. The compiler loads fonts with java.awt.Font.createFont(TRUETYPE_FONT, ...), which also parses OpenType/CFF, so the font resolved, rendered in the simulator and got written into theme.res — while Font.createTrueTypeFont rejects any file name not ending in .ttf (Font.java:342) and IPhoneBuilder never registered it in UIAppFonts (IPhoneBuilder.java:4989). The reported symptom is a font that just doesn't change, with no error anywhere.

What it rejects

CSSTheme.updateResources now validates every declared @font-face first and throws with the offending rules listed, which CN1CSSCLI turns into a non-zero exit and CompileCSSMojo into a failed build.

Case Why it can't work
Not ending in .ttf Font.createTrueTypeFont throws; iOS never registers it. Advice splits by case: convert an .otf, rename an upper-case .TTF (the runtime's endsWith is case-sensitive)
Local font that doesn't exist Used to surface as a bare FileNotFoundException naming no rule
Local font outside the CSS directory Merge mode syncs only that directory, so a ../ reference resolves for the author and breaks in a real build
Two rules → different files sharing a name Fonts deploy next to theme.res by file name alone; one would overwrite the other

Two families pointing at the same file stay legal — that copy is idempotent, and rejecting it would break a legitimate alias. There's a test for it.

Every declared rule is checked, not only the referenced ones, so a typo fails the build that introduced it rather than the later build that first uses the family. Remote fonts are judged by URL alone, so validation never downloads a font.

Note for reviewers

Containment is measured against baseURL, not cssFile. cssFile is a new File("test.css") placeholder unless a caller assigns it (only CN1CSSCLI does), so the first version of this check rejected every font in the existing tests.

Behaviour change

A project that today ships a .otf and renders the system font on device will now fail its build. That's the point — but it is a build-breaking change for anyone in that state, and the error message tells them to convert the font.

Verification

  • css-compiler + css-cli: 24 tests green, 9 of them new in CSSFontFaceValidationTest (each rejection path, plus the shared-file alias and a valid sheet that must still compile)
  • core-unittests CSS suites (CSSThemeCompilerTest, CSSThemeBorderRadiusTest, CSSThemeGradientTest): 28 tests green
  • End-to-end through the real forked CLI the mojo runs:
    • valid sheet with a root-level and a nested font → exit 0, both fonts deployed flat beside theme.res
    • same sheet with a .otf → exit 1 and:
      Invalid @font-face rules in file:.../theme.css:
        - @font-face "Nexa" points at Nexa-Regular.otf. OpenType fonts aren't supported --
          convert it to TrueType (.ttf) first. It would compile but fail at runtime, and the
          iOS build wouldn't register it at all
      
  • Guide gates: snippet validator, asciidoctor, Vale, paragraph-cap, LanguageTool all clean; copyright headers pass; new Java is ASCII-only
  • Every @font-face in the repo already uses .ttf, so nothing in-tree starts failing

🤖 Generated with Claude Code

An .otf sailed through compilation and only broke on the device. The compiler
loads fonts with java.awt.Font.createFont(TRUETYPE_FONT, ...), which also
parses OpenType/CFF, so the font resolved, rendered in the simulator and got
written into theme.res -- while Font.createTrueTypeFont rejects any file name
not ending in .ttf and IPhoneBuilder never registered it in UIAppFonts. The
reported symptom is a font that just doesn't change, with no error anywhere.

CSSTheme.updateResources now validates every declared @font-face first and
throws with the offending rules listed, which CN1CSSCLI turns into a non-zero
exit and CompileCSSMojo into a failed build. Rejected:

- anything not ending in .ttf, with the advice split by case: convert an .otf,
  rename an upper-case .TTF (the runtime's endsWith is case-sensitive)
- a local font that doesn't exist, which used to surface as a bare
  FileNotFoundException naming no rule
- a local font outside the directory holding the CSS file; merge mode syncs
  only that directory, so a ../ reference resolves for the author and breaks in
  a real build
- two rules resolving to different files that share a file name, since fonts
  are deployed next to theme.res by file name alone and one would overwrite the
  other. Two families pointing at the same file stay legal -- that copy is
  idempotent, and rejecting it would break a legitimate alias.

Every declared rule is checked, not only the referenced ones, so a typo fails
the build that introduced it instead of the later build that first uses the
family. Remote fonts are judged by URL alone, so validation never downloads.

Containment is measured against baseURL rather than cssFile: cssFile is a
"test.css" placeholder unless a caller assigns it, so using it rejected every
font in the existing tests.

Updates the guide and the initializr CSS reference, which described the old
compile-clean-fail-at-runtime behaviour.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2dbae3510

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2171 to +2173
if (!"file".equals(url.getProtocol())) {
// Remote fonts are downloaded into the CSS directory, so only the name matters here.
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject remote schemes the font loader cannot download

When src uses ftp://... or another non-file scheme ending in .ttf, this branch treats it as a downloadable remote font and validation succeeds. However, FontFace.getFontFile() only downloads protocols whose name starts with http; for these accepted schemes it returns null, so a referenced family silently falls back instead of being deployed. Restrict this branch to the remote protocols supported by getFontFile(), or extend the loader accordingly.

Useful? React with 👍 / 👎.

Comment on lines +2207 to +2209
String path = url.getPath();
try {
path = java.net.URLDecoder.decode(path, "UTF-8");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve literal plus signs in local font filenames

For a local URL, URLDecoder applies form-encoding semantics and converts a literal + in the path to a space. Thus valid rules for distinct files such as url(A+B.ttf) and url(A%20B.ttf) both produce the collision key A B.ttf and the new validator rejects the stylesheet, even though new File(url.toURI()).getName() deploys them as A+B.ttf and A B.ttf. Decode percent escapes without translating literal plus signs.

Useful? React with 👍 / 👎.

Comment on lines +2162 to +2165
String source = canonicalSource(url);
String previousSource = sourceByFileName.put(fileName, source);
String previousFamily = familyByFileName.put(fileName, family);
if (previousSource != null && !previousSource.equals(source)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Detect case-only deployment-name collisions

When compilation runs on a case-insensitive filesystem, rules for different files named, for example, Regular.ttf and regular.ttf occupy separate map keys here even though their deployment targets are the same file. FontFace.getFontFile() then skips copying the second target because it already exists, while theme.res still records each source's original casing; on a case-sensitive device one of those recorded filenames is absent. Reject case-only collisions whenever the deployment filesystem treats the names as identical.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

Two more ways a font passed the compile and then failed on a device, which is
the case the name check alone doesn't catch.

A file the font parser rejects left EditorTTFFont.actualFont null, because
refresh() swallows the Throwable, and then died as a bare NPE inside
EditableResources.save at getNativeFont()).getPSName() naming no rule. This
matters more now that the compile insists on a .ttf name: renaming an .otf is
the obvious workaround, and anything that isn't really loadable has to be
caught here rather than on the device.

A font with no PostScript name renders in the simulator and on Android, which
both look fonts up by file name, while iOS resolves purely by the PostScript
name written into the resource and falls back to the system font. That is
invisible until someone runs the app on an iPhone.

validateFontFaces now parses each local font and checks for a usable
PostScript name, reporting which rule and file is at fault.

Also corrects the extension message, which overclaimed. The constraint is the
file NAME -- Font.createTrueTypeFont tests endsWith(".ttf") and IPhoneBuilder
filters UIAppFonts the same way -- not the outline format. iOS registers
through UIAppFonts and resolves with [UIFont fontWithName:], Android uses
Typeface.createFromAsset, and both read CFF/OpenType content, so no check is
made against the sfnt flavour: an OpenType font renamed to .ttf is not proven
broken and isn't rejected on a guess.
@shai-almog

Copy link
Copy Markdown
Collaborator Author

Follow-up commit (6a8151d) extends this to the failure class the name check alone misses — compiles clean, breaks on device.

Two gaps closed:

  • A file the font parser rejects left EditorTTFFont.actualFont null (refresh() swallows the Throwable) and then died as a bare NPE inside EditableResources.save at getNativeFont()).getPSName(), naming no rule. This matters more now that the compile insists on a .ttf name — renaming an .otf is the obvious workaround, so anything not really loadable has to be caught at build time.
  • A font with no PostScript name renders in the simulator and on Android (both resolve by file name) while iOS resolves purely by the PostScript name written into the resource, and falls back to the system font. Invisible until someone runs it on an iPhone.

validateFontFaces now parses each local font and checks for a usable PostScript name, naming the rule and file.

Correction to the original message in this PR. It said "OpenType fonts aren't supported — convert to TrueType", which overclaims. The constraint is the file name: Font.createTrueTypeFont tests endsWith(".ttf") and IPhoneBuilder filters UIAppFonts the same way. iOS registers through UIAppFonts and resolves with [UIFont fontWithName:], Android uses Typeface.createFromAsset, and both read CFF/OpenType content — so an .otf renamed to .ttf would very likely work.

I deliberately did not add a check against the sfnt flavour. I can't prove that path broken without a device run, and rejecting it on a guess could break a shipping setup. If you'd rather be strict — reject OTTO content in a .ttf because we only claim TrueType — it's a one-line addition to fontContentError, but that's a call for you to make.

Verified through the real forked CLI:

- @font-face "Nexa" refers to Renamed.ttf, which isn't a font file Codename One can read
  (Unsupported sfnt .../Renamed.ttf)
BAD FONT EXIT=1
GOOD FONT EXIT=0

17 css-compiler + 8 css-cli tests green; guide gates (snippet validator, asciidoctor, Vale, paragraph-cap, LanguageTool) clean; copyright headers pass; new Java is ASCII-only.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a8151d907

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

+ "move the font beside the CSS file or into a subdirectory of it");
continue;
}
String contentError = fontContentError(fontFile);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh merged fonts before validating their contents

When a Maven build first fails this check and the developer fixes only the font file, subsequent builds keep validating the stale copy under target/css/cn1-merged-files. In the inspected stateless flow, CN1CSSCLI.updateMergeFileStateless() decides whether to call syncDirectories() using only the input theme.css timestamps, even though CompileCSSMojo correctly reruns for changes anywhere under the CSS directory. Consequently, replacing a corrupt or missing-PostScript-name font without touching theme.css continues to produce the old error until the user cleans target or touches the stylesheet; the merge cache must account for asset changes before this validation runs.

Useful? React with 👍 / 👎.

return url.toString();
}
}
return url.toString();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore URL fragments when comparing remote font sources

When two families alias the same remote font using URLs such as https://example.com/Font.ttf#body and https://example.com/Font.ttf#caption, this return value differs even though an HTTP fragment is not sent to the server and getFontFile() downloads both into the same Font.ttf cache file. The collision check therefore rejects a legitimate shared-file alias solely because its fragment differs; compare remote identities without the fragment before deciding that their contents can overwrite one another.

Useful? React with 👍 / 👎.

@shai-almog

shai-almog commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 151 screenshots: 151 matched.

Native Android coverage

  • 📊 Line coverage: 7.85% (7605/96841 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.72% (39610/513127), branch 2.80% (1363/48627), complexity 3.15% (1646/52177), method 4.87% (1345/27595), class 9.98% (367/3679)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 7.85% (7605/96841 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 7.72% (39610/513127), branch 2.80% (1363/48627), complexity 3.15% (1646/52177), method 4.87% (1345/27595), class 9.98% (367/3679)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1187 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.Frame – 0.00% (0/570 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 161ms / native 126ms = 1.2x speedup
SIMD float-mul (64K x300) java 129ms / native 132ms = 0.9x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 74.000 ms
Base64 CN1 decode 94.000 ms
Base64 native encode 363.000 ms
Base64 encode ratio (CN1/native) 0.204x (79.6% faster)
Base64 native decode 281.000 ms
Base64 decode ratio (CN1/native) 0.335x (66.5% faster)
Image encode benchmark status skipped (SIMD unsupported)

@shai-almog

shai-almog commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 181 screenshots: 181 matched.
✅ JavaScript-port screenshot tests passed.

@shai-almog

shai-almog commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

shai-almog commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@shai-almog

shai-almog commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 374 seconds

Build and Run Timing

Metric Duration
Simulator Boot 92000 ms
Simulator Boot (Run) 1000 ms
App Install 12000 ms
App Launch 11000 ms
Test Execution 441000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 72ms / native 3ms = 24.0x speedup
SIMD float-mul (64K x300) java 123ms / native 6ms = 20.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 505.000 ms
Base64 CN1 decode 219.000 ms
Base64 native encode 832.000 ms
Base64 encode ratio (CN1/native) 0.607x (39.3% faster)
Base64 native decode 385.000 ms
Base64 decode ratio (CN1/native) 0.569x (43.1% faster)
Base64 SIMD encode 183.000 ms
Base64 encode ratio (SIMD/CN1) 0.362x (63.8% faster)
Base64 SIMD decode 71.000 ms
Base64 decode ratio (SIMD/CN1) 0.324x (67.6% faster)
Base64 encode ratio (SIMD/native) 0.220x (78.0% faster)
Base64 decode ratio (SIMD/native) 0.184x (81.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 14.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.143x (85.7% faster)
Image applyMask (SIMD off) 108.000 ms
Image applyMask (SIMD on) 131.000 ms
Image applyMask ratio (SIMD on/off) 1.213x (21.3% slower)
Image modifyAlpha (SIMD off) 70.000 ms
Image modifyAlpha (SIMD on) 41.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.586x (41.4% faster)
Image modifyAlpha removeColor (SIMD off) 55.000 ms
Image modifyAlpha removeColor (SIMD on) 164.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 2.982x (198.2% slower)

@shai-almog

shai-almog commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 350 seconds

Build and Run Timing

Metric Duration
Simulator Boot 60000 ms
Simulator Boot (Run) 1000 ms
App Install 42000 ms
App Launch 2000 ms
Test Execution 404000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 79ms / native 3ms = 26.3x speedup
SIMD float-mul (64K x300) java 75ms / native 3ms = 25.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 294.000 ms
Base64 CN1 decode 146.000 ms
Base64 native encode 812.000 ms
Base64 encode ratio (CN1/native) 0.362x (63.8% faster)
Base64 native decode 469.000 ms
Base64 decode ratio (CN1/native) 0.311x (68.9% faster)
Base64 SIMD encode 88.000 ms
Base64 encode ratio (SIMD/CN1) 0.299x (70.1% faster)
Base64 SIMD decode 64.000 ms
Base64 decode ratio (SIMD/CN1) 0.438x (56.2% faster)
Base64 encode ratio (SIMD/native) 0.108x (89.2% faster)
Base64 decode ratio (SIMD/native) 0.136x (86.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 7.000 ms
Image createMask (SIMD on) 1.000 ms
Image createMask ratio (SIMD on/off) 0.143x (85.7% faster)
Image applyMask (SIMD off) 45.000 ms
Image applyMask (SIMD on) 37.000 ms
Image applyMask ratio (SIMD on/off) 0.822x (17.8% faster)
Image modifyAlpha (SIMD off) 62.000 ms
Image modifyAlpha (SIMD on) 37.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.597x (40.3% faster)
Image modifyAlpha removeColor (SIMD off) 108.000 ms
Image modifyAlpha removeColor (SIMD on) 752.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 6.963x (596.3% slower)

@shai-almog

shai-almog commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 148 screenshots: 148 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 283 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 59ms / native 3ms = 19.6x speedup
SIMD float-mul (64K x300) java 62ms / native 3ms = 20.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 187.000 ms
Base64 CN1 decode 151.000 ms
Base64 native encode 765.000 ms
Base64 encode ratio (CN1/native) 0.244x (75.6% faster)
Base64 native decode 457.000 ms
Base64 decode ratio (CN1/native) 0.330x (67.0% faster)
Base64 SIMD encode 82.000 ms
Base64 encode ratio (SIMD/CN1) 0.439x (56.1% faster)
Base64 SIMD decode 62.000 ms
Base64 decode ratio (SIMD/CN1) 0.411x (58.9% faster)
Base64 encode ratio (SIMD/native) 0.107x (89.3% faster)
Base64 decode ratio (SIMD/native) 0.136x (86.4% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 1.000 ms
Image createMask ratio (SIMD on/off) 0.125x (87.5% faster)
Image applyMask (SIMD off) 65.000 ms
Image applyMask (SIMD on) 50.000 ms
Image applyMask ratio (SIMD on/off) 0.769x (23.1% faster)
Image modifyAlpha (SIMD off) 50.000 ms
Image modifyAlpha (SIMD on) 76.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.520x (52.0% slower)
Image modifyAlpha removeColor (SIMD off) 56.000 ms
Image modifyAlpha removeColor (SIMD on) 68.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.214x (21.4% slower)

@shai-almog

Copy link
Copy Markdown
Collaborator Author

Folded into #5508 - one PR for the whole change.

@shai-almog shai-almog closed this Aug 2, 2026
@shai-almog
shai-almog deleted the feature/css-font-face-validation branch August 2, 2026 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant