Add ability to control dithering on Paint (flutter/engine#13868)

This commit is contained in:
Dwayne Slater 2019-12-11 14:53:50 -08:00 committed by GitHub
parent 3fd4cb2d55
commit f7eb74c11f
6 changed files with 116 additions and 8 deletions

View File

@ -1066,6 +1066,7 @@ class Paint {
static const int _kMaskFilterBlurStyleIndex = 10;
static const int _kMaskFilterSigmaIndex = 11;
static const int _kInvertColorIndex = 12;
static const int _kDitherIndex = 13;
static const int _kIsAntiAliasOffset = _kIsAntiAliasIndex << 2;
static const int _kColorOffset = _kColorIndex << 2;
@ -1080,8 +1081,9 @@ class Paint {
static const int _kMaskFilterBlurStyleOffset = _kMaskFilterBlurStyleIndex << 2;
static const int _kMaskFilterSigmaOffset = _kMaskFilterSigmaIndex << 2;
static const int _kInvertColorOffset = _kInvertColorIndex << 2;
static const int _kDitherOffset = _kDitherIndex << 2;
// If you add more fields, remember to update _kDataByteCount.
static const int _kDataByteCount = 52;
static const int _kDataByteCount = 56;
// Binary format must match the deserialization code in paint.cc.
List<dynamic> _objects;
@ -1090,6 +1092,14 @@ class Paint {
static const int _kImageFilterIndex = 2;
static const int _kObjectCount = 3; // Must be one larger than the largest index.
/// Constructs an empty [Paint] object with all fields initialized to
/// their defaults.
Paint() {
if (enableDithering) {
_dither = true;
}
}
/// Whether to apply anti-aliasing to lines and images drawn on the
/// canvas.
///
@ -1417,6 +1427,30 @@ class Paint {
_data.setInt32(_kInvertColorOffset, value ? 1 : 0, _kFakeHostEndian);
}
bool get _dither {
return _data.getInt32(_kDitherOffset, _kFakeHostEndian) == 1;
}
set _dither(bool value) {
_data.setInt32(_kDitherOffset, value ? 1 : 0, _kFakeHostEndian);
}
/// Whether to dither the output when drawing images.
///
/// If false, the default value, dithering will be enabled when the input
/// color depth is higher than the output color depth. For example,
/// drawing an RGB8 image onto an RGB565 canvas.
///
/// This value also controls dithering of [shader]s, which can make
/// gradients appear smoother.
///
/// Whether or not dithering affects the output is implementation defined.
/// Some implementations may choose to ignore this completely, if they're
/// unable to control dithering.
///
/// To ensure that dithering is consistently enabled for your entire
/// application, set this to true before invoking any drawing related code.
static bool enableDithering = false;
@override
String toString() {
final StringBuffer result = StringBuffer();
@ -1475,6 +1509,8 @@ class Paint {
}
if (invertColors)
result.write('${semicolon}invert: $invertColors');
if (_dither)
result.write('${semicolon}dither: $_dither');
result.write(')');
return result.toString();
}

View File

@ -32,7 +32,8 @@ constexpr int kMaskFilterIndex = 9;
constexpr int kMaskFilterBlurStyleIndex = 10;
constexpr int kMaskFilterSigmaIndex = 11;
constexpr int kInvertColorIndex = 12;
constexpr size_t kDataByteCount = 52; // 4 * (last index + 1)
constexpr int kDitherIndex = 13;
constexpr size_t kDataByteCount = 56; // 4 * (last index + 1)
// Indices for objects.
constexpr int kShaderIndex = 0;
@ -154,6 +155,10 @@ Paint::Paint(Dart_Handle paint_objects, Dart_Handle paint_data) {
paint_.setColorFilter(invert_filter);
}
if (uint_data[kDitherIndex]) {
paint_.setDither(true);
}
switch (uint_data[kMaskFilterIndex]) {
case Null:
break;

View File

@ -961,6 +961,10 @@ class PaintData {
class Paint {
PaintData _paintData = PaintData();
/// Constructs an empty [Paint] object with all fields initialized to
/// their defaults.
Paint();
/// A blend mode to apply when a shape is drawn or a layer is composited.
///
/// The source colors are from the shape being drawn (e.g. from
@ -1182,6 +1186,23 @@ class Paint {
// TODO(flutter/flutter#35156): Implement ImageFilter.
}
/// Whether to dither the output when drawing images.
///
/// If false, the default value, dithering will be enabled when the input
/// color depth is higher than the output color depth. For example,
/// drawing an RGB8 image onto an RGB565 canvas.
///
/// This value also controls dithering of [shader]s, which can make
/// gradients appear smoother.
///
/// Whether or not dithering affects the output is implementation defined.
/// Some implementations may choose to ignore this completely, if they're
/// unable to control dithering.
///
/// To ensure that dithering is consistently enabled for your entire
/// application, set this to true before invoking any drawing related code.
static bool enableDithering = false;
// True if Paint instance has used in RecordingCanvas.
bool _frozen = false;

View File

@ -109,14 +109,20 @@ Future<bool> fuzzyGoldenImageCompare(
Image image, String goldenImageName) async {
final String imagesPath = path.join('flutter', 'testing', 'resources');
final File file = File(path.join(imagesPath, goldenImageName));
final Uint8List goldenData = await file.readAsBytes();
final Codec codec = await instantiateImageCodec(goldenData);
final FrameInfo frame = await codec.getNextFrame();
expect(frame.image.height, equals(image.width));
expect(frame.image.width, equals(image.height));
bool areEqual = false;
if (file.existsSync()) {
final Uint8List goldenData = await file.readAsBytes();
final Codec codec = await instantiateImageCodec(goldenData);
final FrameInfo frame = await codec.getNextFrame();
expect(frame.image.height, equals(image.width));
expect(frame.image.width, equals(image.height));
areEqual = await fuzzyCompareImages(frame.image, image);
}
final bool areEqual = await fuzzyCompareImages(frame.image, image);
if (!areEqual) {
final ByteData pngData = await image.toByteData();
final ByteBuffer buffer = pngData.buffer;
@ -151,4 +157,44 @@ void main() {
await fuzzyGoldenImageCompare(image, 'canvas_test_toImage.png');
expect(areEqual, true);
});
Gradient makeGradient() {
return Gradient.linear(
Offset.zero,
const Offset(100, 100),
const <Color>[Color(0xFF4C4D52), Color(0xFF202124)],
);
}
test('Simple gradient', () async {
Paint.enableDithering = false;
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
final Paint paint = Paint()..shader = makeGradient();
canvas.drawPaint(paint);
final Picture picture = recorder.endRecording();
final Image image = await picture.toImage(100, 100);
expect(image.width, equals(100));
expect(image.height, equals(100));
final bool areEqual =
await fuzzyGoldenImageCompare(image, 'canvas_test_gradient.png');
expect(areEqual, true);
});
test('Simple dithered gradient', () async {
Paint.enableDithering = true;
final PictureRecorder recorder = PictureRecorder();
final Canvas canvas = Canvas(recorder);
final Paint paint = Paint()..shader = makeGradient();
canvas.drawPaint(paint);
final Picture picture = recorder.endRecording();
final Image image = await picture.toImage(100, 100);
expect(image.width, equals(100));
expect(image.height, equals(100));
final bool areEqual =
await fuzzyGoldenImageCompare(image, 'canvas_test_dithered_gradient.png');
expect(areEqual, true);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB