Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

TwoPointConicGradients again #5299

Merged
merged 5 commits into from
May 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions lib/ui/painting.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1792,6 +1792,20 @@ class Path extends NativeFieldWrapperClass2 {
Path _transform(Float64List matrix4) native 'Path_transform';

/// Computes the bounding rectangle for this path.
///
/// A path containing only axis-aligned points on the same straight line will
/// have no area, and therefore `Rect.isEmpty` will return true for such a
/// path. Consider checking `rect.width + rect.height > 0.0` instead, or
/// using the [computeMetrics] API to check the path length.
///
/// For many more elaborate paths, the bounds may be inaccurate. For example,
/// when a path contains a circle, the points used to compute the bounds are
/// the circle's implied control points, which form a square around the circle;
/// if the circle has a transformation applied using [transform] then that
/// square is rotated, and the (axis-aligned, non-rotated) bounding box
/// therefore ends up grossly overestimating the actual area covered by the
/// circle.
// see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds
Rect getBounds() {
final Float32List rect = _getBounds();
return new Rect.fromLTRB(rect[0], rect[1], rect[2], rect[3]);
Expand Down Expand Up @@ -2296,25 +2310,45 @@ class Gradient extends Shader {
/// If `matrix4` is provided, the gradient fill will be transformed by the
/// specified 4x4 matrix relative to the local coordinate system. `matrix4` must
/// be a column-major matrix packed into a list of 16 values.
///
/// If `focal` is provided and not equal to `center` and `focalRadius` is
/// provided and not equal to 0.0, the generated shader will be a two point
/// conical radial gradient, with `focal` being the center of the focal
/// circle and `focalRadius` being the radius of that circle. If `focal` is
/// provided and not equal to `center`, at least one of the two offsets must
/// not be equal to [Offset.zero].
Gradient.radial(
Offset center,
double radius,
List<Color> colors, [
List<double> colorStops,
TileMode tileMode = TileMode.clamp,
Float64List matrix4
Float64List matrix4,
Offset focal,
double focalRadius = 0.0
]) : assert(_offsetIsValid(center)),
assert(colors != null),
assert(tileMode != null),
assert(matrix4 == null || _matrix4IsValid(matrix4)),
super._() {
focalRadius ??= 0.0;
_validateColorStops(colors, colorStops);
final Int32List colorsBuffer = _encodeColorList(colors);
final Float32List colorStopsBuffer = colorStops == null ? null : new Float32List.fromList(colorStops);
_constructor();
_initRadial(center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4);

// If focal is null or focal radius is null, this should be treated as a regular radial gradient
// If focal == center and the focal radius is 0.0, it's still a regular radial gradient
if (focal == null || (focal == center && focalRadius == 0.0)) {
_constructor();
_initRadial(center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4);
} else {
assert(center != Offset.zero || focal != Offset.zero); // will result in exception(s) in Skia side
_constructor();
_initConical(focal.dx, focal.dy, focalRadius, center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4);
}
}
void _initRadial(double centerX, double centerY, double radius, Int32List colors, Float32List colorStops, int tileMode, Float64List matrix4) native 'Gradient_initRadial';
void _initConical(double startX, double startY, double startRadius, double endX, double endY, double endRadius, Int32List colors, Float32List colorStops, int tileMode, Float64List matrix4) native 'Gradient_initTwoPointConical';

/// Creates a sweep gradient centered at `center` that starts at `startAngle`
/// and ends at `endAngle`.
Expand Down
32 changes: 31 additions & 1 deletion lib/ui/painting/gradient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ IMPLEMENT_WRAPPERTYPEINFO(ui, Gradient);
#define FOR_EACH_BINDING(V) \
V(Gradient, initLinear) \
V(Gradient, initRadial) \
V(Gradient, initSweep)
V(Gradient, initSweep) \
V(Gradient, initTwoPointConical)

FOR_EACH_BINDING(DART_NATIVE_CALLBACK)

Expand Down Expand Up @@ -109,6 +110,35 @@ void CanvasGradient::initSweep(double center_x,
has_matrix ? &sk_matrix : nullptr)));
}

void CanvasGradient::initTwoPointConical(double start_x,
double start_y,
double start_radius,
double end_x,
double end_y,
double end_radius,
const tonic::Int32List& colors,
const tonic::Float32List& color_stops,
SkShader::TileMode tile_mode,
const tonic::Float64List& matrix4) {
FXL_DCHECK(colors.num_elements() == color_stops.num_elements() ||
color_stops.data() == nullptr);

static_assert(sizeof(SkColor) == sizeof(int32_t),
"SkColor doesn't use int32_t.");

SkMatrix sk_matrix;
bool has_matrix = matrix4.data() != nullptr;
if (has_matrix) {
sk_matrix = ToSkMatrix(matrix4);
}

set_shader(UIDartState::CreateGPUObject(SkGradientShader::MakeTwoPointConical(
SkPoint::Make(start_x, start_y), start_radius,
SkPoint::Make(end_x, end_y), end_radius,
reinterpret_cast<const SkColor*>(colors.data()), color_stops.data(),
colors.num_elements(), tile_mode, 0, has_matrix ? &sk_matrix : nullptr)));
}

CanvasGradient::CanvasGradient() = default;

CanvasGradient::~CanvasGradient() = default;
Expand Down
11 changes: 11 additions & 0 deletions lib/ui/painting/gradient.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ class CanvasGradient : public Shader {
double end_angle,
const tonic::Float64List& matrix4);

void initTwoPointConical(double start_x,
double start_y,
double start_radius,
double end_x,
double end_y,
double end_radius,
const tonic::Int32List& colors,
const tonic::Float32List& color_stops,
SkShader::TileMode tile_mode,
const tonic::Float64List& matrix4);

static void RegisterNatives(tonic::DartLibraryNatives* natives);

private:
Expand Down
72 changes: 72 additions & 0 deletions testing/dart/gradient_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:typed_data' show Float64List;
import 'dart:ui';

import 'package:test/test.dart';

void main() {
test('Gradient.radial with no focal point', () {
expect(
new Gradient.radial(
Offset.zero,
null,
<Color>[const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)],
<double>[0.0, 1.0],
TileMode.mirror),
isNotNull,
);
});

// this is just a radial gradient, focal point is discarded.
test('radial center and focal == Offset.zero and focalRadius == 0.0 is ok',
() {
expect(
() => new Gradient.radial(
Offset.zero,
0.0,
<Color>[const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)],
<double>[0.0, 1.0],
TileMode.mirror,
null,
Offset.zero,
0.0,
),
isNotNull);
});

test('radial center != focal and focalRadius == 0.0 is ok', () {
expect(
() => new Gradient.radial(
Offset.zero,
0.0,
<Color>[const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)],
<double>[0.0, 1.0],
TileMode.mirror,
null,
const Offset(2.0, 2.0),
0.0,
),
isNotNull);
});

// this would result in div/0 on skia side.
test('radial center and focal == Offset.zero and focalRadius != 0.0 assert',
() {
expect(
() => new Gradient.radial(
Offset.zero,
0.0,
<Color>[const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)],
<double>[0.0, 1.0],
TileMode.mirror,
null,
Offset.zero,
1.0,
),
throwsA(const isInstanceOf<AssertionError>()),
);
});
}