Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Antialiasing behaviour when same-colour #14288

Open
radzish opened this issue Jan 26, 2018 · 104 comments
Open

Antialiasing behaviour when same-colour #14288

radzish opened this issue Jan 26, 2018 · 104 comments
Labels
a: gamedev Issues related to game development with Flutter c: rendering UI glitches reported at the engine/skia rendering level customer: crowd Affects or could affect many people, though not necessarily a specific customer. engine flutter/engine repository. See also e: labels. found in release: 3.3 Found to occur in 3.3 found in release: 3.4 Found to occur in 3.4 framework flutter/packages/flutter repository. See also f: labels. has reproducible steps The issue has been confirmed reproducible and is ready to work on P3 Issues that are less important to the Flutter project team-engine Owned by Engine team triaged-engine Triaged by Engine team workaround available There is a workaround available to overcome the issue

Comments

@radzish
Copy link

radzish commented Jan 26, 2018

Latest status update: #14288 (comment); some work around suggestions: #14288 (comment)


Steps to Reproduce

Following source code:

import 'package:flutter/material.dart';

const Color color = const Color.fromARGB(255, 100, 100, 100);

void main() =>
    runApp(
      new Container(
        color: const Color.fromARGB(255, 0, 0, 0),
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.end,
          textDirection: TextDirection.ltr,
          children: [
            new Expanded(
              child: new Container(
                color: color,
              ),
            ),
            new Expanded(
              child: new Container(
                color: color,
              ),
            ),
            new Expanded(
              child: new Container(
                color: color,
              ),
            ),
            new Expanded(
              child: new Container(
                color: color,
              ),
            ),
            new Expanded(
              child: new Container(
                color: color,
              ),
            ),
            new Expanded(
              child: new Container(
                color: color,
              ),
            ),
            new Expanded(
              child: new Container(
                color: color,
              ),
            ),
          ],
        ),
      ),
    );

produces following result:

Looks like background of the container is popping out and we see vertical lines. That should not be the case as all children of the row are Expanded and thus should fill the whole area.
If we remove one child lines are gone.

Logs

Launching lib/main.dart on Android SDK built for x86 in debug mode...
Initializing gradle...
Resolving dependencies...
Running 'gradlew assembleDebug'...
Built build/app/outputs/apk/app-debug.apk (22.4MB).
I/FlutterActivityDelegate( 8398): onResume setting current activity to this
D/EGL_emulation( 8398): eglMakeCurrent: 0xaad2c640: ver 3 1 (tinfo 0xa057c5b0)
E/eglCodecCommon( 8398): glUtilsParamSize: unknow param 0x000082da
E/eglCodecCommon( 8398): glUtilsParamSize: unknow param 0x000082da
E/eglCodecCommon( 8398): glUtilsParamSize: unknow param 0x00008cdf
E/eglCodecCommon( 8398): glUtilsParamSize: unknow param 0x00008cdf
E/eglCodecCommon( 8398): glUtilsParamSize: unknow param 0x00008824
E/eglCodecCommon( 8398): glUtilsParamSize: unknow param 0x00008824
D/        ( 8398): HostConnection::get() New Host Connection established 0xa31a3640, tid 8416
D/EGL_emulation( 8398): eglMakeCurrent: 0xaad2c640: ver 3 1 (tinfo 0xa3183790)
D/EGL_emulation( 8398): eglMakeCurrent: 0xaad2c760: ver 3 1 (tinfo 0xa057cc10)
Syncing files to device Android SDK built for x86...

Flutter Doctor

[✓] Flutter (on Linux, locale en_US.UTF-8, channel master)
    • Flutter version unknown at <path_to_flutter>
    • Framework revision 5ae770345a (3 days ago), 2018-01-23 13:46:14 -0800
    • Engine revision 171d032f86
    • Tools Dart version 2.0.0-dev.16.0
    • Engine Dart version 2.0.0-edge.93d8c9fe2a2c22dc95ec85866af108cfab71ad06

[✓] Android toolchain - develop for Android devices (Android SDK 27.0.3)
    • Android SDK at <path_to_android>
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-27, build-tools 27.0.3
    • ANDROID_HOME = <path_to_android>
    • Java binary at: <path_to_android-studio>/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-915-b01)

[✓] Android Studio (version 3.0)
    • Android Studio at <path_to_android-studio>
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-915-b01)

[✓] Connected devices
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 8.1.0 (API 27) (emulator)
@radzish
Copy link
Author

radzish commented Jan 26, 2018

Another example (I believe it is somehow related: )

import 'package:flutter/material.dart';

const Color grey = const Color.fromARGB(255, 100, 100, 100);
const Color black = const Color.fromARGB(255, 0, 0, 0);

void main() =>
    runApp(
      new Container(
        color: grey,
        child: new Center(
          child: new Container(
            width: 151.0,
            height: 151.0,
            color: black,
            child: new Container(
              color: grey,
            ),
          ),
        ),
      ),
    );

We should not see border here. If widht/height are changed to 150.0, square is gone.

@Hixie
Copy link
Contributor

Hixie commented Jan 29, 2018

This is normal behaviour. What's happening is that the boxes are not quite aligned with pixel boundaries, so there's some anti-aliasing happening on the boundaries, which involves transparency, which means that for those pixels the two grays are overlapping and looking darker.

As a general rule when doing anti-aliasing you want to avoid putting identically-coloured boxes adjacent or over each other unless you can guarantee physical pixel alignment.

Alternatively, you can use saveLayer (or RepaintBoundary) to cause a bunch of paint operations to get merged into one and composited as one. Not sure that that would help in these cases specifically but it is a tool that can be useful in this kind of situation.

@radzish
Copy link
Author

radzish commented Jan 30, 2018

This is not boxes overlapping, but rather spare space between boxes, so color of background is popping up. I was changing background to different color and this color was popping out.

@radzish
Copy link
Author

radzish commented Feb 22, 2018

Root cause is that boxes can not be aligned with physical pixels. I would not call it "normal", I would rather call it "expected".
On android similar (semantically) case is handled properly:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:background="#000000"
    tools:context="com.radzish.android_lines_bug.MainActivity">

    <FrameLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:background="#646464"
        android:layout_height="match_parent"/>

    <FrameLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:background="#646464"
        android:layout_height="match_parent"/>

    <FrameLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:background="#646464"
        android:layout_height="match_parent"/>

    <FrameLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:background="#646464"
        android:layout_height="match_parent"/>

    <FrameLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:background="#646464"
        android:layout_height="match_parent"/>

    <FrameLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:background="#646464"
        android:layout_height="match_parent"/>

    <FrameLayout
        android:layout_width="0dp"
        android:layout_weight="1"
        android:background="#646464"
        android:layout_height="match_parent"/>

</LinearLayout>

So I think flutter should improve in this case.
The only workaround I found for me at the moment is sizing children manually like this:

    int CHILDREN_COUNT = 7;
    List<Widget> children = new List(CHILDREN_COUNT);

    MediaQueryData mediaQueryData = MediaQuery.of(context);
    int physicalWidth = (mediaQueryData.size.width * mediaQueryData.devicePixelRatio).floor();

    for (int i = 0, pixelsLeft = physicalWidth; i < CHILDREN_COUNT; i++) {
      int columnWidth = (pixelsLeft / (CHILDREN_COUNT - i)).floor();

      children[i] = new Container(
        width: columnWidth / mediaQueryData.devicePixelRatio,
        color: color,
      );

      pixelsLeft -= columnWidth;
    }

@Hixie Hixie changed the title Expanded elements not taking the whole are of their parent Expanded elements not taking the whole area of their parent May 29, 2018
@Hixie Hixie changed the title Expanded elements not taking the whole area of their parent Document the issue of antialiasing when same-colour blocks abut, and some workarounds May 29, 2018
@Hixie Hixie added the d: api docs Issues with https://api.flutter.dev/ label May 29, 2018
@Hixie Hixie added this to the Goals milestone May 29, 2018
@Hixie Hixie changed the title Document the issue of antialiasing when same-colour blocks abut, and some workarounds Document the issue of antialiasing when same-colour blocks abut, and some workarounds (API docs, FAQ) May 29, 2018
@zoechi zoechi added framework flutter/packages/flutter repository. See also f: labels. and removed framework flutter/packages/flutter repository. See also f: labels. labels Dec 4, 2018
@Hixie
Copy link
Contributor

Hixie commented Feb 15, 2019

@radzish what is Android doing to avoid the problem?

@Hixie
Copy link
Contributor

Hixie commented Feb 15, 2019

These comments have suggestions for things to put in documentation:
#14288 (comment)
#17084 (comment)
#15035 (comment)

@DenisBogatirov
Copy link

Any updates here? Because this is very annoying((
Or any examples how to use saveLayer or RepaintBoundary?

@sapphire008
Copy link

sapphire008 commented Jan 21, 2024

@knopp Thank you for creating pixel_snap. Can you help show an example on how to use it with CustomScrollView with Slivers? (Related to: #37578). I tried something like the following, but the thin line still exists between my list of slivers:

import 'package:pixel_snap/pixel_snap.dart';
import 'package:pixel_snap/material.dart';

...

CustomScrollView(
  controller: PixelSnapScrollController(),
  slivers: [
    SliverAppBar(
        pinned: true,
        expandedHeight: ps(MediaQuery.of(context).size.height * 2 / 3),
        ...
    ),
    SliverPadding(
        padding: const EdgeInsets.all(16.0).pixelSnap(ps),
        sliver: SliverToBoxAdapter(child: Text(...)),
    ),
  ],
)

@knopp
Copy link
Member

knopp commented Jan 21, 2024

@knopp Thank you for creating pixel_snap. Can you help show an example on how to use it with CustomScrollView with Slivers? (Related to: #37578). I tried something like the following, but the thin line still exists between my list of slivers:

import 'package:pixel_snap/pixel_snap.dart';
import 'package:pixel_snap/material.dart';

...

CustomScrollView(
  controller: PixelSnapScrollController(),
  slivers: [
    SliverAppBar(
        pinned: true,
        expandedHeight: ps(MediaQuery.of(context).size.height * 2 / 3),
        ...
    ),
    SliverPadding(
        padding: const EdgeInsets.all(16.0).pixelSnap(ps),
        sliver: SliverToBoxAdapter(child: Text(...)),
    ),
  ],
)

If you can provide a reproducible example please create issue in pixel_snap repository and I'll take a look. The snippet that you have posted here looks good, but it is possible that the CustomScrollView itself is not pixel snapped. I don't know what widgets are around it that affect layout that's why a complete example is needed.

@ltOgt
Copy link

ltOgt commented Feb 12, 2024

There are many cases where adjacent boxes dont have the same color, and can not be merged.
(E.g. list items with selection state, or some highlighted items, or items with different heights)

Example with alternating color, and a timer that auto scrolls to show the gaps:

import 'dart:async';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: const Scaffold(
        body: MyWidget(),
      ),
    );
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget({Key? key}) : super(key: key);

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late ScrollController _controller;
  late Timer _timer;
  double _scrollOffset = 0.0;

  @override
  void initState() {
    super.initState();
    _controller = ScrollController();
    _timer = Timer.periodic(const Duration(seconds: 1), _scroll);
  }

  @override
  void dispose() {
    _timer.cancel();
    _controller.dispose();
    super.dispose();
  }

  void _scroll(Timer timer) {
    _scrollOffset += 0.25;
    _controller.jumpTo(_scrollOffset);
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black,
      child: SingleChildScrollView(
        controller: _controller,
        child: Column(
          children: [
            for (int i = 0; i < 1000; i++) //
              Container(
                color: i % 2 == 0 ? Colors.blue : Colors.grey,
                height: 20,
                width: 100,
                child: Text("$i"),
              ),
          ],
        ),
      ),
    );
  }
}
Screen.Recording.2024-02-12.at.19.53.31.mov

And without auto scroll:

Screen.Recording.2024-02-12.at.19.54.02.mov

And without alternating colors, showing the color from behind:

Screen.Recording.2024-02-12.at.19.56.51.mov

@casey977
Copy link

casey977 commented Feb 15, 2024

Hello,

I'm dealing with the same problems as mention above, thin white lines between widgets (rows in my case), when rendering to web. It looks like an anti-alias or not-pixel-perfect issue, also as already mentioned above.

Have I understood it correctly that there is no proper/native solution to this, and the best option is to use pixel_snap?

UPDATE: I also tried building/running for Linux desktop, and I get the same white lines. But using a Scaffold with backgroundColor works as a workaround, which happens to be usable in this case.

@erlangparasu
Copy link

Hello,

I'm dealing with the same problems as mention above, thin white lines between widgets (rows in my case), when rendering to web. It looks like an anti-alias or not-pixel-perfect issue, also as already mentioned above.

Have I understood it correctly that there is no proper/native solution to this, and the best option is to use pixel_snap?

UPDATE: I also tried building/running for Linux desktop, and I get the same white lines. But using a Scaffold with backgroundColor works as a workaround, which happens to be usable in this case.

Also you may try space_fixer https://pub.dev/packages/space_fixer

Example:

          Container(
            width: MediaQuery.of(context).size.width,
            height: 50,
            color: Colors.black,
          ),
          SpaceFixerHorizontalLine(
            context: context,
            overflowHeight: 3,
            overflowColor: Colors.black,
          ),
          Container(
            width: MediaQuery.of(context).size.width,
            height: 50,
            color: Colors.black,
          ),

https://github.com/erlangparasu/space_fixer/blob/e3a5b8f158f64507a486c1f8904431cb8c6ce773/example/example1.dart#L59

You can use this widget as a divider in the list

@knopp
Copy link
Member

knopp commented Feb 16, 2024

There are many cases where adjacent boxes dont have the same color, and can not be merged. (E.g. list items with selection state, or some highlighted items, or items with different heights)

[removed]

flutter pub add pixel_snap

Replace import 'package:flutter/material.dart' with import 'package:pixel_snap/material.dart'. Gaps disappear. In this case it would be beacuse the scroll controller from pixel snap snaps the position to physical pixels.

pixel_snap.mov

@badayumut
Copy link

Same issue on 3.13.7

Update: I found a solution, below SizedBox is a item in ListView SizedBox(height: h, child: images[index]); White Gap SizedBox(height: h.toInt().toDouble(), child: images[index]); No Gap

I don't know why, but it works well for me

Why would i ever give fixed height to a list item.

@Amir-P
Copy link
Contributor

Amir-P commented Jun 12, 2024

Just wanted to share another case with you (#150035). It happens with Impeller on iOS too.

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          Positioned.fill(child: ColoredBox(color: Colors.blue)),
          Center(
            child: SizedBox(
              width: 200,
              height: 200,
              child: Stack(
                children: [
                  Positioned.fill(child: ColoredBox(color: Colors.red)),
                  Positioned.fill(
                    child: ColoredBox(color: Colors.blue),
                  )
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

@ulisseshen
Copy link

Is it a skia or a Flutter issue?

@LvtLvt
Copy link

LvtLvt commented Jul 18, 2024

Hello,
I'm dealing with the same problems as mention above, thin white lines between widgets (rows in my case), when rendering to web. It looks like an anti-alias or not-pixel-perfect issue, also as already mentioned above.
Have I understood it correctly that there is no proper/native solution to this, and the best option is to use pixel_snap?
UPDATE: I also tried building/running for Linux desktop, and I get the same white lines. But using a Scaffold with backgroundColor works as a workaround, which happens to be usable in this case.

Also you may try space_fixer https://pub.dev/packages/space_fixer

Example:

          Container(
            width: MediaQuery.of(context).size.width,
            height: 50,
            color: Colors.black,
          ),
          SpaceFixerHorizontalLine(
            context: context,
            overflowHeight: 3,
            overflowColor: Colors.black,
          ),
          Container(
            width: MediaQuery.of(context).size.width,
            height: 50,
            color: Colors.black,
          ),

https://github.com/erlangparasu/space_fixer/blob/e3a5b8f158f64507a486c1f8904431cb8c6ce773/example/example1.dart#L59

You can use this widget as a divider in the list

thanks to you, I was able to easily solve the problem without much overhead. thank you so much

@officialismailshah
Copy link

@devnta
Copy link

devnta commented Oct 15, 2024

Unbelievable, this issue has existed for a few years?
Any official solution on this?

@spydon
Copy link

spydon commented Oct 21, 2024

This issue can be very apparent when you're making games with Flutter (and Flame), and the curious thing is that nowadays the issue is usually worse with Impeller than it is with Skia.
image

@Mik77o
Copy link

Mik77o commented Nov 21, 2024

The same issue is noticed on Flutter Web (3.24.4) for ListView (mostly while scrolling).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: gamedev Issues related to game development with Flutter c: rendering UI glitches reported at the engine/skia rendering level customer: crowd Affects or could affect many people, though not necessarily a specific customer. engine flutter/engine repository. See also e: labels. found in release: 3.3 Found to occur in 3.3 found in release: 3.4 Found to occur in 3.4 framework flutter/packages/flutter repository. See also f: labels. has reproducible steps The issue has been confirmed reproducible and is ready to work on P3 Issues that are less important to the Flutter project team-engine Owned by Engine team triaged-engine Triaged by Engine team workaround available There is a workaround available to overcome the issue
Projects
None yet
Development

No branches or pull requests