Skip to content

Small light fonts appear to have a different weighting with Impeller on Android (and other platforms) #190340

Open

Description

@luke-willi

Steps to reproduce

Light text, especially at smaller sizes, appear to have a different weighting in Impeller than those rendered with SKIA.

Reproducible on latest release 3.44.8 and on channel main.

Used Android Emulator medium_phone for screenshots.

Reproduce using the simple app I've attached below, built with both SKIA and Impeller for comparison.

Note this first came to light when testing on our own linux embedded device and was fixed by enabling this patch for linux also #186074 . Is this an impeller bug rather than platform specific, and therefore should be enabled by default?

Expected results

Impeller and SKIA should render light and dark text the same.

Actual results

Impeller renders light text with a perceived different weighting than SKIA.

Code sample

Code sample
import 'dart:io' show Platform;
import 'dart:ui';

import 'package:flutter/material.dart';

void main() => runApp(const FontComparisonApp());

class FontComparisonApp extends StatelessWidget {
  const FontComparisonApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(debugShowCheckedModeBanner: false, home: _ComparisonScreen());
  }
}

class _ComparisonScreen extends StatelessWidget {
  const _ComparisonScreen();

  static const _sizes = [10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 24.0, 28.0, 32.0, 48.0];

  static const _weights = [
    (label: 'Thin 100', weight: FontWeight.w100),
    (label: 'Light 300', weight: FontWeight.w300),
    (label: 'Regular 400', weight: FontWeight.w400),
    (label: 'Medium 500', weight: FontWeight.w500),
    (label: 'Bold 700', weight: FontWeight.w700),
  ];

  static const _backgrounds = [
    (label: 'Black', bgColor: Color(0xFF000000), textColor: Color(0xFFFFFFFF)),
    (label: 'Nav bar navy', bgColor: Color(0xFF0D1523), textColor: Color(0xFFFFFFFF)),
    (label: 'Mid grey', bgColor: Color(0xFF444444), textColor: Color(0xFFFFFFFF)),
    (label: 'White', bgColor: Color(0xFFFFFFFF), textColor: Color(0xFF000000)),
  ];

  static const _dim = Color(0xFF888888);
  static const _labelStyle = TextStyle(color: _dim, fontSize: 12, decoration: TextDecoration.none);

  @override
  Widget build(BuildContext context) {
    final isImpeller = ImageFilter.isShaderFilterSupported;
    final renderer = isImpeller ? 'Impeller' : 'Skia';
    final rendererColor = isImpeller ? const Color(0xFF4CAF50) : const Color(0xFF2196F3);
    final backend = _gpuBackend();

    final isMobile = MediaQuery.of(context).size.width < 800;
    final padding = isMobile ? 16.0 : 48.0;
    final verticalSpacing = isMobile ? 16.0 : 24.0;

    return Scaffold(
      backgroundColor: const Color(0xFF111111),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(padding),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            for (final bg in _backgrounds) ...[
              _bgSection(bg.label, bg.bgColor, bg.textColor, renderer, rendererColor, backend, isMobile),
              SizedBox(height: verticalSpacing),
            ],
          ],
        ),
      ),
    );
  }

  Widget _rendererBadge(String renderer, Color rendererColor, String backend, Color textColor) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(
          renderer,
          style: TextStyle(
            color: rendererColor,
            fontSize: 11,
            fontWeight: FontWeight.w700,
            decoration: TextDecoration.none,
          ),
        ),
        const SizedBox(width: 6),
        Text(
          backend,
          style: TextStyle(
            color: textColor.withValues(alpha: 0.5),
            fontSize: 11,
            fontWeight: FontWeight.w400,
            decoration: TextDecoration.none,
          ),
        ),
      ],
    );
  }

  Widget _bgSection(
    String label,
    Color bgColor,
    Color textColor,
    String renderer,
    Color rendererColor,
    String backend,
    bool isMobile,
  ) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(label, style: _labelStyle),
        const SizedBox(height: 8),
        Container(
          decoration: BoxDecoration(color: bgColor, borderRadius: BorderRadius.circular(8)),
          padding: EdgeInsets.all(isMobile ? 12.0 : 20.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _rendererBadge(renderer, rendererColor, backend, textColor),
              const SizedBox(height: 10),
              for (final w in _weights) ...[
                Text(
                  w.label,
                  style: TextStyle(
                    color: textColor.withValues(alpha: 0.4),
                    fontSize: 10,
                    fontWeight: FontWeight.w400,
                    decoration: TextDecoration.none,
                  ),
                ),
                const SizedBox(height: 4),
                SingleChildScrollView(
                  scrollDirection: Axis.horizontal,
                  child: Row(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: [
                      for (final size in _sizes) ...[
                        _sampleText('Ag', size, w.weight, textColor),
                        if (size != _sizes.last) SizedBox(width: isMobile ? 8 : 16),
                      ],
                    ],
                  ),
                ),
                SizedBox(height: isMobile ? 12 : 16),
              ],
            ],
          ),
        ),
      ],
    );
  }

  Widget _sampleText(String text, double fontSize, FontWeight weight, Color color) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Text(
          text,
          style: TextStyle(color: color, fontSize: fontSize, fontWeight: weight, decoration: TextDecoration.none),
        ),
        const SizedBox(height: 2),
        Text(
          '${fontSize.toInt()}',
          style: TextStyle(
            color: color.withValues(alpha: 0.35),
            fontSize: 9,
            fontWeight: FontWeight.w400,
            decoration: TextDecoration.none,
          ),
        ),
      ],
    );
  }
}

String _gpuBackend() {
  if (Platform.isMacOS) return 'Metal';
  if (Platform.isIOS) return 'Metal';
  if (Platform.isAndroid) return 'Vulkan/GL';
  if (Platform.isLinux) return 'EGL/OpenGL ES';
  if (Platform.isWindows) return 'D3D/Vulkan';
  return 'Unknown';
}

Screenshots or Video

Screenshots / Video demonstration

SKIA:

Light:
Image

Dark:
Image

Impeller:

Light:
Image

Dark:
Image

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[✓] Flutter (Channel main, 3.47.0-1.0.pre-260, on macOS 26.5.2 25F84 darwin-arm64, locale en-GB) [3.8s]
    • Flutter version 3.47.0-1.0.pre-260 on channel main at /Users/lwn18/fvm/versions/main
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 89d96ef494 (2 days ago), 2026-07-29 05:23:36 -0400
    • Engine revision 89d96ef494
    • Dart version 3.14.0 (build 3.14.0-75.0.dev)
    • DevTools version 2.60.0
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios,
      enable-custom-devices, cli-animations, enable-native-assets, enable-record-use, enable-swift-package-manager, omit-legacy-version-file,
      enable-lldb-debugging, enable-uiscene-migration, enable-riscv64

[!] Android toolchain - develop for Android devices (Android SDK version 36.0.0) [1,111ms]
    • Android SDK at /Users/lwn18/Library/Android/sdk
    • Emulator version 36.6.11.0 (build_id 15507667) (CL:N/A)
    ✗ cmdline-tools component is missing.
      Try installing or updating Android Studio.
      Alternatively, download the tools from https://developer.android.com/studio#command-line-tools-only and make sure to set the ANDROID_HOME
      environment variable.
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/to/macos-android-setup for more details.

[!] Xcode - develop for iOS and macOS (Xcode 26.6) [957ms]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 17F113
    ✗ Unable to get list of installed Simulator runtimes.
    ! CocoaPods not installed.
        CocoaPods is a package manager for iOS or macOS platform code.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/to/platform-plugins
      For installation instructions, see https://guides.cocoapods.org/using/getting-started.html#installation

[✗] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome) [6ms]
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.

[✓] Connected device (2 available) [6.0s]
    • sdk gphone16k arm64 (mobile) • emulator-5554 • android-arm64 • Android 17 (API 37) (emulator)
    • macOS (desktop)              • macos         • darwin-arm64  • macOS 26.5.2 25F84 darwin-arm64

[✓] Network resources [273ms]
    • All expected network resources are available.

! Doctor found issues in 3 categories.

Activity

  1. suryaKommana2662 commented on Jul 31, 2026

    @suryaKommana2662
    Contributor

    @luke-willi Thanks for filing this with a detailed reproduction app and screenshots. Please take a look at #186975, which discusses a similar difference in text rendering between Impeller and Skia. You may also want to check the related issue #127810 and this [comment]. Could you please have a look at these related issues and let us know if they are helpful?

  2. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    and removed
    in triagePresently being triaged by the triage team
    on Jul 31, 2026
  3. luke-willi commented on Aug 5, 2026

    @luke-willi
    Author

    Thanks @suryaKommana2662, It appears there are multiple fixes for the same issue?

    MacOS:
    #186074

    Linux & Windows:
    #187122
    #187871
    #190477

    But none of these fix this on Android, is there a cross platform solution here?

    Our main platform is a linux embedded device, initally we did the same as the MacOS solution which fixed our issue. I will try the contrast change too, as that one will require no change from us once in a release.

  4. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Aug 5, 2026
  5. luke-willi commented on Aug 5, 2026

    @luke-willi
    Author

    I tried on the our platform, disabling the previous fix and bringing in the contrast changes for linux (https://github.com/flutter/flutter/pull/187122/files), didn't seem to make a different.

    I also added some logs to makes sure this was been triggered.

    console_important.log

    Image Image
  6. added
    e: impellerImpeller rendering backend issues and features requests
    on Aug 5, 2026
  7. gaaclarke commented on Aug 10, 2026

    @gaaclarke
    Member

    We had to fix this issue on desktop by performing gamma correction. It was really noticeable there. We might just have to turn that on for mobile platforms as well.

  8. added
    P2Important issues not at the top of the work list
    on Aug 10, 2026
  9. luke-willi commented on Aug 18, 2026

    @luke-willi
    Author

    I see the new gamma correction changes to the engine in 3.47. I tried on Linux desktop and I still get differences in font weight for SKIA and Impeller with the test app.

    Also, on our Embedded Linux platform (which uses the macOS fix for light fonts), I know see bolder fonts with Impeller than SKIA, which is expected as we now have two 'fixes'.

    Image Image
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listcustomer: bluee: impellerImpeller rendering backend issues and features requeststeam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions