提交 579dcf9d 编写于 作者: I Ian Hickson

Flesh our dart:ui graphics documentation. (#2721)

I gave up before I was completely done.

Items still remaining include:

 * drawVertices
 * drawAtlas
 * TransferMode.*
 * VertexMode

Also:
 * Add a convenience constructor to RSTransform
 * Add some convenience methods to Rect and Size
 * Make some defaults more explicit
上级 7a49b37a
......@@ -22,6 +22,7 @@ abstract class Image extends NativeFieldWrapperClass2 {
/// after this method is called.
void dispose() native "Image_dispose";
@override
String toString() => '[$width\u00D7$height]';
}
......@@ -56,7 +57,6 @@ void decodeImageFromList(Uint8List list, ImageDecoderCallback callback)
/// Paths can be drawn on canvases using [Canvas.drawPath], and can
/// used to create clip regions using [Canvas.clipPath].
class Path extends NativeFieldWrapperClass2 {
/// Create a new empty [Path] object.
Path() { _constructor(); }
void _constructor() native "Path_constructor";
......@@ -178,20 +178,55 @@ class Path extends NativeFieldWrapperClass2 {
enum BlurStyle {
// These mirror SkBlurStyle and must be kept in sync.
/// Fuzzy inside and outside.
/// Fuzzy inside and outside. This is useful for painting shadows that are
/// offset from the shape that ostensible is casting the shadow.
normal,
/// Solid inside, fuzzy outside.
/// Solid inside, fuzzy outside. This corresponds to drawing the shape, and
/// additionally drawing the blur. This can make objects appear brighter,
/// maybe even as if they were fluorescent.
solid,
/// Nothing inside, fuzzy outside.
/// Nothing inside, fuzzy outside. This is useful for painting shadows for
/// partially transparent shapes, when they are painted separately but without
/// an offset, so that the shadow doesn't paint below the shape.
outer,
/// Fuzzy inside, nothing outside.
/// Fuzzy inside, nothing outside. This can make shapes appear to be lit from
/// within.
inner,
}
/// A mask filter to apply to shapes as they are painted. A mask filter is a
/// function that takes a bitmap of color pixels, and returns another bitmap of
/// color pixels.
///
/// Instances of this class are used with [Paint.maskFilter] on [Paint] objects.
class MaskFilter extends NativeFieldWrapperClass2 {
/// Creates a mask filter that takes the shape being drawn and blurs it.
///
/// This is commonly used to approximate shadows.
///
/// The `style` argument controls the kind of effect to draw; see [BlurStyle].
///
/// The `sigma` argument controls the size of the effect. It is the standard
/// deviation of the Gaussian blur to apply. The value must be greater than
/// zero. The sigma corresponds to very roughly half the radius of the effect
/// in pixels.
///
/// If the `ignoreTransform` argument is set, then the current transform is
/// ignored when computing the blur. This makes the operation cheaper, but
/// lowers the quality of the effect. In particular, it means that the sigma
/// will be relative to the device pixel coordinate space, rather than the
/// logical pixel coordinate space, which means the blur will look different
/// on different devices.
///
/// If the `highQuality` argument is set, then the quality of the blur may be
/// slightly improved, at the cost of making the operation even more
/// expensive.
///
/// Even in the best conditions and with the lowest quality settings, a blur
/// is an expensive operation and blurs should therefore be used sparingly.
MaskFilter.blur(BlurStyle style, double sigma, {
bool ignoreTransform: false,
bool highQuality: false
......@@ -211,10 +246,22 @@ class MaskFilter extends NativeFieldWrapperClass2 {
}
}
/// A description of a filter to apply when drawing with a particular [Paint].
/// A description of a color filter to apply when drawing a shape or compositing
/// a layer with a particular [Paint]. A color filter is a function that takes
/// two colors, and outputs one color. When applied during compositing, it is
/// independently applied to each pixel of the layer being drawn before the
/// entire layer is merged with the destination.
///
/// See [Paint.colorFilter].
/// Instances of this class are used with [Paint.colorFilter] on [Paint]
/// objects.
class ColorFilter extends NativeFieldWrapperClass2 {
/// Creates a color filter that applies the transfer mode given as the second
/// argument. The source color is the one given as the first argument, and the
/// destination color is the one from the layer being composited.
///
/// The output of this filter is then composited into the background according
/// to the [Paint.transferMode], using the output of this filter as the source
/// and the background as the destination.
ColorFilter.mode(Color color, TransferMode transferMode) {
_constructor(color, transferMode);
}
......@@ -241,7 +288,7 @@ class ImageFilter extends NativeFieldWrapperClass2 {
// }
// void _initPicture(Picture picture) native "ImageFilter_initPicture";
/// Applies a Gaussian blur.
/// Creates an image filter that applies a Gaussian blur.
ImageFilter.blur({ double sigmaX: 0.0, double sigmaY: 0.0 }) {
_constructor();
_initBlur(sigmaX, sigmaY);
......@@ -250,7 +297,7 @@ class ImageFilter extends NativeFieldWrapperClass2 {
}
/// Base class for objects such as [Gradient] and [ImageShader] which
/// correspond to shaders.
/// correspond to shaders as used by [Paint.shader].
abstract class Shader extends NativeFieldWrapperClass2 { }
/// Defines what happens at the edge of the gradient.
......@@ -263,6 +310,10 @@ enum TileMode {
mirror
}
/// A shader (as used by [Paint.shader]) that renders a color gradient.
///
/// There are two useful types of gradients, created by [new Gradient.linear]
/// and [new Griadent.radial].
class Gradient extends Shader {
/// Creates a Gradient object that is not initialized.
///
......@@ -271,10 +322,13 @@ class Gradient extends Shader {
Gradient();
void _constructor() native "Gradient_constructor";
/// Creates a linear gradient from [endPoint[0]] to [endPoint[1]]. If
/// [colorStops] is provided, [colorStops[i]] is a number from 0 to 1 that
/// specifies where [color[i]] begins in the gradient.
// TODO(mpcomplete): Maybe pass a list of (color, colorStop) pairs instead?
/// Creates a linear gradient from `endPoint[0]` to `endPoint[1]`. If
/// `colorStops` is provided, `colorStops[i]` is a number from 0 to 1 that
/// specifies where `color[i]` begins in the gradient. If `colorStops` is not
/// provided, then two stops at 0.0 and 1.0 are implied. The behavior before
/// and after the radius is described by the `tileMode` argument.
// TODO(mpcomplete): Consider passing a list of (color, colorStop) pairs
// instead.
Gradient.linear(List<Point> endPoints,
List<Color> colors,
[List<double> colorStops = null,
......@@ -287,10 +341,12 @@ class Gradient extends Shader {
}
void _initLinear(List<Point> endPoints, List<Color> colors, List<double> colorStops, int tileMode) native "Gradient_initLinear";
/// Creates a radial gradient centered at [center] that ends at [radius]
/// distance from the center. If [colorStops] is provided, [colorStops[i]] is
/// a number from 0 to 1 that specifies where [color[i]] begins in the
/// gradient.
/// Creates a radial gradient centered at `center` that ends at `radius`
/// distance from the center. If `colorStops` is provided, `colorStops[i]` is
/// a number from 0 to 1 that specifies where `color[i]` begins in the
/// gradient. If `colorStops` is not provided, then two stops at 0.0 and 1.0
/// are implied. The behavior before and after the radius is described by the
/// `tileMode` argument.
Gradient.radial(Point center,
double radius,
List<Color> colors,
......@@ -308,7 +364,13 @@ class Gradient extends Shader {
}
}
/// A shader (as used by [Paint.shader]) that tiles an image.
class ImageShader extends Shader {
/// Creates an image-tiling shader. The first argument specifies the image to
/// tile. The second and third arguments specify the [TileMode] for the x
/// direction and y direction respectively. The fourth argument gives the
/// matrix to apply to the effect. All the arguments are required and must not
/// be null.
ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4) {
if (image == null)
throw new ArgumentError("[image] argument cannot be null");
......@@ -338,6 +400,18 @@ enum VertexMode {
/// [Canvas] objects are used in creating [Picture] objects, which can
/// themselves be used with a [SceneBuilder] to build a [Scene]. In
/// normal usage, however, this is all handled by the framework.
///
/// A canvas has a current transformation matrix which is applied to all
/// operations. Initially, the transformation matrix is the identity transform.
/// It can be modified using the [translate], [scale], [rotate], [skew],
/// [transform], and [setMatrix] methods.
///
/// A canvas also has a current clip region which is applied to all operations.
/// Initially, the clip region is infinite. It can be modified using the
/// [clipRect], [clipRRect], and [clipPath] methods.
///
/// The current transform and clip can be saved and restored using the stack
/// managed by the [save], [saveLayer], and [restore] methods.
class Canvas extends NativeFieldWrapperClass2 {
/// Creates a canvas for recording graphical operations into the
/// given picture recorder.
......@@ -369,21 +443,20 @@ class Canvas extends NativeFieldWrapperClass2 {
/// Call [restore] to pop the save stack.
void save() native "Canvas_save";
/// Saves a copy of the current transform and clip on the save
/// stack, and then creates a new group which subsequent calls will
/// become a part of. When the save stack is later popped, the group
/// will be flattened and have the given paint applied.
/// Saves a copy of the current transform and clip on the save stack, and then
/// creates a new group which subsequent calls will become a part of. When the
/// save stack is later popped, the group will be flattened into a layer and
/// have the given `paint`'s [Paint.colorFilter] and [Paint.transferMode]
/// applied.
///
/// This lets you create composite effects, for example making a
/// group of drawing commands semi-transparent. Without using
/// [saveLayer], each part of the group would be painted
/// individually, so where they overlap would be darker than where
/// they do not. By using [saveLayer] to group them together, they
/// can be drawn with an opaque color at first, and then the entire
/// group can be made transparent using the [saveLayer]'s paint.
/// This lets you create composite effects, for example making a group of
/// drawing commands semi-transparent. Without using [saveLayer], each part of
/// the group would be painted individually, so where they overlap would be
/// darker than where they do not. By using [saveLayer] to group them
/// together, they can be drawn with an opaque color at first, and then the
/// entire group can be made transparent using the [saveLayer]'s paint.
///
/// Call [restore] to pop the save stack and apply the paint to the
/// group.
/// Call [restore] to pop the save stack and apply the paint to the group.
void saveLayer(Rect bounds, Paint paint) {
if (bounds == null)
_saveLayerWithoutBounds(paint);
......@@ -402,6 +475,9 @@ class Canvas extends NativeFieldWrapperClass2 {
/// Otherwise, does nothing.
///
/// Use [save] and [saveLayer] to push state onto the stack.
///
/// If the state was pushed with with [saveLayer], then this call will also
/// cause the new layer to be composited into the previous layer.
void restore() native "Canvas_restore";
/// Returns the number of items on the save stack, including the
......@@ -412,11 +488,26 @@ class Canvas extends NativeFieldWrapperClass2 {
/// This number cannot go below 1.
int getSaveCount() native "Canvas_getSaveCount";
/// Add a translation to the current transform, shifting the coordinate space
/// horizontally by the first argument and vertically by the second argument.
void translate(double dx, double dy) native "Canvas_translate";
/// Add an axis-aligned scale to the current transform, scaling by the first
/// argument in the horizontal direction and the second in the vertical
/// direction.
void scale(double sx, double sy) native "Canvas_scale";
/// Add a rotation to the current transform. The argument is in radians clockwise.
void rotate(double radians) native "Canvas_rotate";
/// Add an axis-aligned skew to the current transform, with the first argument
/// being the horizontal skew in radians clockwise around the origin, and the
/// second argument being the vertical skew in radians clockwise around the
/// origin.
void skew(double sx, double sy) native "Canvas_skew";
/// Multiply the current transform by the specified 4⨉4 transformation matrix
/// specified as a list of values in column-major order.
void transform(Float64List matrix4) {
if (matrix4.length != 16)
throw new ArgumentError("[matrix4] must have 16 entries.");
......@@ -424,6 +515,8 @@ class Canvas extends NativeFieldWrapperClass2 {
}
void _transform(Float64List matrix4) native "Canvas_transform";
/// Replaces the current transform with the specified 4⨉4 transformation
/// matrix specified as a list of values in column-major order.
void setMatrix(Float64List matrix4) {
if (matrix4.length != 16)
throw new ArgumentError("[matrix4] must have 16 entries.");
......@@ -431,7 +524,12 @@ class Canvas extends NativeFieldWrapperClass2 {
}
void _setMatrix(Float64List matrix4) native "Canvas_setMatrix";
/// Returns the current 4⨉4 transformation matrix as a list of 16 values in
/// column-major order.
Float64List getTotalMatrix() native "Canvas_getTotalMatrix";
/// Reduces the clip region to the intersection of the current clip and the
/// given rectangle.
void clipRect(Rect rect) {
_clipRect(rect.left, rect.top, rect.right, rect.bottom);
}
......@@ -440,22 +538,37 @@ class Canvas extends NativeFieldWrapperClass2 {
double right,
double bottom) native "Canvas_clipRect";
/// Reduces the clip region to the intersection of the current clip and the
/// given rounded rectangle.
void clipRRect(RRect rrect) native "Canvas_clipRRect";
/// Reduces the clip region to the intersection of the current clip and the
/// given [Path].
void clipPath(Path path) native "Canvas_clipPath";
/// Paints the given [Color] onto the canvas, applying the given
/// [TransferMode], with the given color being the source and the background
/// being the destination.
void drawColor(Color color, TransferMode transferMode) {
_drawColor(color.value, transferMode.index);
}
void _drawColor(int color, int transferMode) native "Canvas_drawColor";
/// Draws a line between the given [Point]s using the given paint. The line is
/// stroked, the value of the [Paint.style] is ignored for this call.
void drawLine(Point p1, Point p2, Paint paint) {
_drawLine(p1.x, p1.y, p2.x, p2.y, paint);
}
void _drawLine(double x1, double y1, double x2, double y2, Paint paint) native "Canvas_drawLine";
/// Fills the canvas with the given [Paint].
///
/// To fill the canvas with a solid color and transfer mode, consider
/// [drawColor] instead.
void drawPaint(Paint paint) native "Canvas_drawPaint";
/// Draws a rectangle with the given [Paint]. Whether the rectangle is filled
/// or stroked (or both) is controlled by [Paint.style].
void drawRect(Rect rect, Paint paint) {
_drawRect(rect.left, rect.top, rect.right, rect.bottom, paint);
}
......@@ -465,10 +578,20 @@ class Canvas extends NativeFieldWrapperClass2 {
double bottom,
Paint paint) native "Canvas_drawRect";
/// Draws a rounded rectangle with the given [Paint]. Whether the rectangle is
/// filled or stroked (or both) is controlled by [Paint.style].
void drawRRect(RRect rrect, Paint paint) native "Canvas_drawRRect";
/// Draws a shape consisting of the difference between two rounded rectangles
/// with the given [Paint]. Whether this shape is filled or stroked (or both)
/// is controlled by [Paint.style].
///
/// This shape is almost but not quite entirely unlike an annulus.
void drawDRRect(RRect outer, RRect inner, Paint paint) native "Canvas_drawDRRect";
/// Draws an axis-aligned oval that fills the given axis-aligned rectangle
/// with the given [Paint]. Whether the oval is filled or stroked (or both) is
/// controlled by [Paint.style].
void drawOval(Rect rect, Paint paint) {
_drawOval(rect.left, rect.top, rect.right, rect.bottom, paint);
}
......@@ -478,22 +601,32 @@ class Canvas extends NativeFieldWrapperClass2 {
double bottom,
Paint paint) native "Canvas_drawOval";
/// Draws a circle centered at the point given by the first two arguments and
/// that has the radius given by the third argument, with the [Paint] given in
/// the fourth argument. Whether the circle is filled or stroked (or both) is
/// controlled by [Paint.style].
void drawCircle(Point c, double radius, Paint paint) {
_drawCircle(c.x, c.y, radius, paint);
}
void _drawCircle(double x, double y, double radius, Paint paint) native "Canvas_drawCircle";
/// Draws the given [Path] with the given [Paint]. Whether this shape is
/// filled or stroked (or both) is controlled by [Paint.style]. If the path is
/// filled, then subpaths within it are implicitly closed (see [Path.close]).
void drawPath(Path path, Paint paint) native "Canvas_drawPath";
/// Draws the given [Image] into the canvas with its top-left corner at the
/// given [Point]. The image is composited into the canvas using the given [Paint].
void drawImage(Image image, Point p, Paint paint) {
_drawImage(image, p.x, p.y, paint);
}
void _drawImage(Image image, double x, double y, Paint paint) native "Canvas_drawImage";
/// Draws the src rect from the image into the canvas as dst rect.
/// Draws the subset of the given image described by the `src` argument into
/// the canvas in the axis-aligned rectangle given by the `dst` argument.
///
/// Might sample from outside the src rect by half the width of an applied
/// filter.
/// This might sample from outside the `src` rect by up to half the width of
/// an applied filter.
void drawImageRect(Image image, Rect src, Rect dst, Paint paint) {
_drawImageRect(image,
src.left,
......@@ -517,6 +650,19 @@ class Canvas extends NativeFieldWrapperClass2 {
double dstBottom,
Paint paint) native "Canvas_drawImageRect";
/// Draws the given [Image] into the canvas using the given [Paint].
///
/// The image is drawn in nine portions described by splitting the image by
/// drawing two horizontal lines and two vertical lines, where the `center`
/// argument describes the rectangle formed by the four points where these
/// four lines intersect each other. (This forms a 3-by-3 grid of regions,
/// the center region being described by the `center` argument.)
///
/// The four regions in the corners are drawn, without scaling, in the four
/// corners of the destination rectangle described by `dst`. The remaining
/// five regions are drawn by stretching them to fit such that they exactly
/// cover the destination rectangle while maintaining their relative
/// positions.
void drawImageNine(Image image, Rect center, Rect dst, Paint paint) {
_drawImageNine(image,
center.left,
......
......@@ -99,6 +99,7 @@ class Color {
);
}
@override
bool operator ==(dynamic other) {
if (other is! Color)
return false;
......@@ -106,7 +107,9 @@ class Color {
return value == typedOther.value;
}
@override
int get hashCode => _value.hashCode;
@override
String toString() => "Color(0x${_value.toRadixString(16).padLeft(8, '0')})";
}
......@@ -11,8 +11,24 @@ part of dart_ui;
///
/// See [Paint.filterQuality].
enum FilterQuality {
/// Fastest possible filtering, albeit also the lowest quality.
///
/// Typically this implies nearest-neighbour filtering.
none,
/// Better quality than [none], faster than [medium].
///
/// Typically this implies bilinear interpolation.
low,
/// Better quality than [low], faster than [high].
///
/// Typically this implies a combination of bilinear interpolation and
/// pyramidal parametric prefiltering (mipmaps).
medium,
/// Best possible quality filtering, albeit also the slowest.
///
/// Typically this implies bicubic interpolation or better.
high,
}
......@@ -6,8 +6,13 @@ part of dart_ui;
/// An immutable 2D floating-point offset.
///
/// An Offset represents a vector from an unspecified point
/// An Offset represents a vector from an unspecified [Point].
///
/// Adding an offset to a [Point] returns the [Point] that is indicated by the
/// vector from that first point.
class Offset extends OffsetBase {
/// Creates an offset. The first argument sets [dx], the horizontal component,
/// and the second sets [dy], the vertical component.
const Offset(double dx, double dy) : super(dx, dy);
/// The x component of the offset.
......@@ -20,6 +25,8 @@ class Offset extends OffsetBase {
double get distance => math.sqrt(_dx * _dx + _dy * _dy);
/// The square of the magnitude of the offset.
///
/// This is cheaper than computing the [distance] itself.
double get distanceSquared => _dx * _dx + _dy * _dy;
/// An offset with zero magnitude.
......@@ -34,15 +41,52 @@ class Offset extends OffsetBase {
/// Returns a new offset with translateX added to the x component and translateY added to the y component.
Offset translate(double translateX, double translateY) => new Offset(dx + translateX, dy + translateY);
/// Unary negation operator. Returns an offset with the coordinates negated.
Offset operator -() => new Offset(-dx, -dy);
/// Binary subtraction operator. Returns an offset whose [dx] value is the
/// left-hand-side operand's [dx] minus the right-hand-side operand's [dx] and
/// whose [dy] value is the left-hand-side operand's [dy] minus the
/// right-hand-side operand's [dy].
Offset operator -(Offset other) => new Offset(dx - other.dx, dy - other.dy);
/// Binary addition operator. Returns an offset whose [dx] value is the sum of
/// the [dx] values of the two operands, and whose [dy] value is the sum of
/// the [dy] values of the two operands.
Offset operator +(Offset other) => new Offset(dx + other.dx, dy + other.dy);
/// Multiplication operator. Returns an offset whose coordinates are the
/// coordinates of the left-hand-side operand (an Offset) multiplied by the
/// scalar right-hand-side operand (a double).
Offset operator *(double operand) => new Offset(dx * operand, dy * operand);
/// Division operator. Returns an offset whose coordinates are the
/// coordinates of the left-hand-side operand (an Offset) divided by the
/// scalar right-hand-side operand (a double).
Offset operator /(double operand) => new Offset(dx / operand, dy / operand);
/// Integer (truncating) division operator. Returns an offset whose
/// coordinates are the coordinates of the left-hand-side operand (an Offset)
/// divided by the scalar right-hand-side operand (a double), rounded towards
/// zero.
Offset operator ~/(double operand) => new Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble());
/// Modulo (remainder) operator. Returns an offset whose coordinates are the
/// remainder of dividing the coordinates of the left-hand-side operand (an
/// Offset) by the scalar right-hand-side operand (a double).
Offset operator %(double operand) => new Offset(dx % operand, dy % operand);
/// Returns a rect of the given size that starts at (0, 0) plus this offset.
/// Rectangle constructor operator. Combines an offset and a [Size] to form a
/// [Rect] whose top-left coordinate is the point given by adding this offset,
/// the left-hand-side operand, to the origin, and whose size is the
/// right-hand-side operand.
///
/// ```dart
/// Rect myRect = Offset.zero & const Size(100.0, 100.0);
/// // same as: new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)
/// ```
///
/// See also: [Point.&]
Rect operator &(Size other) => new Rect.fromLTWH(dx, dy, other.width, other.height);
/// Returns the point at (0, 0) plus this offset.
......@@ -62,7 +106,9 @@ class Offset extends OffsetBase {
}
/// Compares two Offsets for equality.
@override
bool operator ==(dynamic other) => other is Offset && super == other;
@override
String toString() => "Offset(${dx?.toStringAsFixed(1)}, ${dy?.toStringAsFixed(1)})";
}
......@@ -4,19 +4,70 @@
part of dart_ui;
/// Base class for [Size] and [Offset], which are both ways to describe
/// a distance as a two-dimensional axis-aligned vector.
abstract class OffsetBase {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
///
/// The first argument sets the horizontal dimension, and the second the
/// vertical dimension.
const OffsetBase(this._dx, this._dy);
final double _dx;
final double _dy;
/// Returns true if either dimension is [double.INFINITY], and false if both
/// are finite (or negative infinity, or NaN).
///
/// This is different than comparing for equality with an instance that has
/// _both_ dimensions set to [double.INFINITY].
bool get isInfinite => _dx >= double.INFINITY || _dy >= double.INFINITY;
/// Less-than operator. Compares an [Offset] or [Size] to another [Offset] or
/// [Size], and returns true if both the horizontal and vertical values of the
/// left-hand-side operand are smaller than the horizontal and vertical values
/// of the right-hand-side operand respectively. Returns false otherwise.
///
/// This is a partial ordering. It is possible for two values to be neither
/// less, nor greater than, nor equal to, another.
bool operator <(OffsetBase other) => _dx < other._dx && _dy < other._dy;
/// Less-than-or-equal-to operator. Compares an [Offset] or [Size] to another
/// [Offset] or [Size], and returns true if both the horizontal and vertical
/// values of the left-hand-side operand are smaller than or equal to the
/// horizontal and vertical values of the right-hand-side operand
/// respectively. Returns false otherwise.
///
/// This is a partial ordering. It is possible for two values to be neither
/// less, nor greater than, nor equal to, another.
bool operator <=(OffsetBase other) => _dx <= other._dx && _dy <= other._dy;
/// Greater-than operator. Compares an [Offset] or [Size] to another [Offset]
/// or [Size], and returns true if both the horizontal and vertical values of
/// the left-hand-side operand are bigger than the horizontal and vertical
/// values of the right-hand-side operand respectively. Returns false
/// otherwise.
///
/// This is a partial ordering. It is possible for two values to be neither
/// less, nor greater than, nor equal to, another.
bool operator >(OffsetBase other) => _dx > other._dx && _dy > other._dy;
/// Less-than-or-equal-to operator. Compares an [Offset] or [Size] to another
/// [Offset] or [Size], and returns true if both the horizontal and vertical
/// values of the left-hand-side operand are bigger than or equal to the
/// horizontal and vertical values of the right-hand-side operand
/// respectively. Returns false otherwise.
///
/// This is a partial ordering. It is possible for two values to be neither
/// less, nor greater than, nor equal to, another.
bool operator >=(OffsetBase other) => _dx > other._dx && _dy >= other._dy;
/// Equality operator. Compares an [Offset] or [Size] to another [Offset] or
/// [Size], and returns true if the horizontal and vertical values of the
/// left-hand-side operand are equal to the horizontal and vertical values of
/// the right-hand-side operand respectively. Returns false otherwise.
@override
bool operator ==(dynamic other) {
if (other is! OffsetBase)
return false;
......@@ -25,5 +76,6 @@ abstract class OffsetBase {
_dy == typedOther._dy;
}
@override
int get hashCode => hashValues(_dx, _dy);
}
......@@ -29,6 +29,7 @@ class Paint {
///
/// If null, defaults to [PaintingStyle.fill].
PaintingStyle style;
static const PaintingStyle _kDefaultStyle = PaintingStyle.fill;
/// How wide to make edges drawn when [style] is set to
/// [PaintingStyle.stroke] or [PaintingStyle.strokeAndFill]. The
......@@ -37,6 +38,7 @@ class Paint {
///
/// The values null and 0.0 correspond to a hairline width.
double strokeWidth;
static const double _kDefaultStrokeWidth = 0.0;
/// The kind of finish to place on the end of lines drawn when
/// [style] is set to [PaintingStyle.stroke] or
......@@ -44,6 +46,7 @@ class Paint {
///
/// If null, defaults to [StrokeCap.butt], i.e. no caps.
StrokeCap strokeCap;
static const StrokeCap _kDefaultStrokeCap = StrokeCap.butt;
/// Whether to apply anti-aliasing to lines and images drawn on the
/// canvas.
......@@ -51,30 +54,79 @@ class Paint {
/// Defaults to true. The value null is treated as false.
bool isAntiAlias = true;
/// The color to use when stroking or filling a shape.
///
/// Defaults to black.
///
/// See also:
///
/// * [style], which controls whether to stroke or fill (or both).
/// * [colorFilter], which overrides [color].
/// * [shader], which overrides [color] with more elaborate effects.
///
/// This color is not used when compositing. To colorize a layer, use
/// [colorFilter].
Color color = _kDefaultPaintColor;
static const Color _kDefaultPaintColor = const Color(0xFF000000);
TransferMode transferMode;
ColorFilter colorFilter;
/// A mask filter (for example, a blur) to apply to a shape after it has been
/// drawn but before it has been composited into the image.
///
/// See [MaskFilter] for details.
MaskFilter maskFilter;
/// Controls the performance vs quality trade-off to use when applying
/// filters, such as [maskFilter], or when drawing images, as with
/// [Canvas.drawImageRect] or [Canvas.drawImageNine].
// TODO(ianh): verify that the image drawing methods actually respect this
FilterQuality filterQuality;
/// The shader to use when stroking or filling a shape.
///
/// When this is null, the [color] is used instead.
///
/// See also:
///
/// * [Gradient], a shader that paints a color gradient.
/// * [ImageShader], a shader that tiles an [Image].
/// * [colorFilter], which overrides [shader].
/// * [color], which is used if [shader] and [colorFilter] are null.
Shader shader;
/// A color filter to apply when a shape is drawn or when a layer is
/// composited.
///
/// See [ColorFilter] for details.
///
/// When a shape is being drawn, [colorFilter] overrides [color] and [shader].
ColorFilter colorFilter;
/// A transfer 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
/// [Canvas.drawPath]) or layer being composited (the graphics that were drawn
/// between the [Canvas.saveLayer] and [Canvas.restore] calls), after applying
/// the [colorFilter], if any.
///
/// The destination colors are from the background onto which the shape or
/// layer is being composited.
///
/// If null, defaults to [TransferMode.srcOver].
TransferMode transferMode;
static const TransferMode _kDefaultTransferMode = TransferMode.srcOver;
// Must match PaintFields enum in Paint.cpp.
dynamic get _value {
// The most common usage is a Paint with no options besides a color and
// anti-aliasing. In this case, save time by just returning the color
// as an int.
if ((style == null || style == PaintingStyle.fill) &&
(strokeWidth == null || strokeWidth == 0.0) &&
(strokeCap == null || strokeCap == StrokeCap.butt) &&
if ((style == null || style == _kDefaultStyle) &&
(strokeWidth == null || strokeWidth == _kDefaultStrokeWidth) &&
(strokeCap == null || strokeCap == _kDefaultStrokeCap) &&
isAntiAlias &&
color != null &&
transferMode == null &&
(transferMode == null || transferMode == _kDefaultTransferMode) &&
colorFilter == null &&
maskFilter == null &&
filterQuality == null &&
......@@ -96,6 +148,7 @@ class Paint {
];
}
@override
String toString() {
StringBuffer result = new StringBuffer();
String semicolon = '';
......@@ -106,7 +159,7 @@ class Paint {
result.write(' $strokeWidth');
else
result.write(' hairline');
if (strokeCap != null && strokeCap != StrokeCap.butt)
if (strokeCap != null && strokeCap != _kDefaultStrokeCap)
result.write(' $strokeCap');
semicolon = '; ';
}
......
......@@ -4,29 +4,77 @@
part of dart_ui;
/// Holds 2 floating-point coordinates.
/// An immutable 2D floating-point x,y coordinate pair.
///
/// A Point represents a specific position in Cartesian space.
///
/// Subtracting a point from another returns the [Offset] that represents the
/// vector between the two points.
class Point {
/// Creates a point. The first argument sets [x], the horizontal component,
/// and the second sets [y], the vertical component.
const Point(this.x, this.y);
/// The horizontal component of the point.
final double x;
/// The vertical component of the point.
final double y;
/// The point at the origin, (0, 0).
static const Point origin = const Point(0.0, 0.0);
/// Unary negation operator. Returns a point with the coordinates negated.
Point operator -() => new Point(-x, -y);
/// Binary subtraction operator. Returns an [Offset] representing the
/// direction and distance from the other point (the right-hand-side operand)
/// to this point (the left-hand-side operand).
Offset operator -(Point other) => new Offset(x - other.x, y - other.y);
/// Binary addition operator. Returns a point that is this point (the
/// left-hand-side operand) plus a vector [Offset] (the right-hand-side
/// operand).
Point operator +(Offset other) => new Point(x + other.dx, y + other.dy);
/// Rectangle constructor operator. Combines a point and a [Size] to form a
/// [Rect] whose top-left coordinate is this point, the left-hand-side
/// operand, and whose size is the right-hand-side operand.
///
/// ```dart
/// Rect myRect = Point.origin & const Size(100.0, 100.0);
/// // same as: new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0)
/// ```
///
/// See also: [Offset.&]
Rect operator &(Size other) => new Rect.fromLTWH(x, y, other.width, other.height);
/// Multiplication operator. Returns a point whose coordinates are the
/// coordinates of the left-hand-side operand (a Point) multiplied by the
/// scalar right-hand-side operand (a double).
Point operator *(double operand) => new Point(x * operand, y * operand);
/// Division operator. Returns a point whose coordinates are the
/// coordinates of the left-hand-side operand (a point) divided by the
/// scalar right-hand-side operand (a double).
Point operator /(double operand) => new Point(x / operand, y / operand);
/// Integer (truncating) division operator. Returns a point whose
/// coordinates are the coordinates of the left-hand-side operand (a point)
/// divided by the scalar right-hand-side operand (a double), rounded towards
/// zero.
Point operator ~/(double operand) => new Point((x ~/ operand).toDouble(), (y ~/ operand).toDouble());
/// Modulo (remainder) operator. Returns a point whose coordinates are the
/// remainder of the coordinates of the left-hand-side operand (a point)
/// divided by the scalar right-hand-side operand (a double).
Point operator %(double operand) => new Point(x % operand, y % operand);
/// Converts this point to an [Offset] with the same coordinates.
// does the equivalent of "return this - Point.origin"
Offset toOffset() => new Offset(x, y);
/// Linearly interpolate between two points
/// Linearly interpolate between two points.
///
/// If either point is null, this function interpolates from [Point.origin].
static Point lerp(Point a, Point b, double t) {
......@@ -39,6 +87,7 @@ class Point {
return new Point(lerpDouble(a.x, b.x, t), lerpDouble(a.y, b.y, t));
}
@override
bool operator ==(dynamic other) {
if (other is! Point)
return false;
......@@ -47,7 +96,9 @@ class Point {
y == typedOther.y;
}
@override
int get hashCode => hashValues(x, y);
@override
String toString() => "Point(${x?.toStringAsFixed(1)}, ${y?.toStringAsFixed(1)})";
}
......@@ -220,6 +220,7 @@ class RRect {
);
}
@override
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
......@@ -233,7 +234,9 @@ class RRect {
return true;
}
@override
int get hashCode => hashList(_value);
@override
String toString() => "RRect.fromLTRBXY(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, ${radiusX.toStringAsFixed(1)}, ${radiusY.toStringAsFixed(1)})";
}
......@@ -4,8 +4,36 @@
part of dart_ui;
// Modeled after Skia's SkRSXform
/// A transform consisting of a translation, a rotation, and a uniform scale.
///
/// Used by [Canvas.drawAtlas]. This is a more efficient way to represent these
/// simple transformations than a full matrix.
// Modeled after Skia's SkRSXform.
class RSTransform {
/// Creates an RSTransform.
///
/// An [RSTransform] expresses the combination of a translation, a rotation
/// around a particular point, and a scale factor.
///
/// The first argument, `scos`, is the cosine of the rotation, multiplied by
/// the scale factor.
///
/// The second argument, `ssin`, is the sine of the rotation, multiplied by
/// that same scale factor.
///
/// The third argument is the x coordinate of the translation, minus the
/// `scos` argument multiplied by the x-coordinate of the rotation point, plus
/// the `ssin` argument multiplied by the y-coordinate of the rotation point.
///
/// The fourth argument is the y coordinate of the translation, minus the `ssin`
/// argument multiplied by the x-coordinate of the rotation point, minus the
/// `scos` argument multiplied by the y-coordinate of the rotation point.
///
/// The [new RSTransform.fromComponents] method may be a simpler way to
/// construct these values. However, if there is a way to factor out the
/// computations of the sine and cosine of the rotation so that they can be
/// reused over multiple calls to this constructor, it may be more efficient
/// to directly use this constructor instead.
RSTransform(double scos, double ssin, double tx, double ty) {
_value
..[0] = scos
......@@ -14,9 +42,55 @@ class RSTransform {
..[3] = ty;
}
/// Creates an RSTransform from its individual components.
///
/// The `rotation` parameter gives the rotation in radians.
///
/// The `scale` parameter describes the uniform scale factor.
///
/// The `anchorX` and `anchorY` parameters give the coordinate of the point
/// around which to rotate.
///
/// The `translateX` and `translateY` parameters give the coordinate of the
/// offset by which to translate.
///
/// This constructor computes the arguments of the [new RSTransform]
/// constructor and then defers to that constructor to actually create the
/// object. If many [RSTransform] objects are being created and there is a way
/// to factor out the computations of the sine and cosine of the rotation
/// (which are computed each time this constructor is called) and reuse them
/// over multiple [RSTransform] objects, it may be more efficient to directly
/// use the more direct [new RSTransform] constructor instead.
factory RSTransform.fromComponents({
double rotation,
double scale,
double anchorX,
double anchorY,
double translateX,
double translateY
}) {
final double scos = math.cos(rotation) * scale;
final double ssin = math.sin(rotation) * scale;
final double tx = translateX + -scos * anchorX + ssin * anchorY;
final double ty = translateY + -ssin * anchorX - scos * anchorY;
return new RSTransform(scos, ssin, tx, ty);
}
final Float32List _value = new Float32List(4);
/// The cosine of the rotation multiplied by the scale factor.
double get scos => _value[0];
/// The sine of the rotation multiplied by that same scale factor.
double get ssin => _value[1];
/// The x coordinate of the translation, minus [scos] multiplied by the
/// x-coordinate of the rotation point, plus [ssin] multiplied by the
/// y-coordinate of the rotation point.
double get tx => _value[2];
/// The y coordinate of the translation, minus [ssin] multiplied by the
/// x-coordinate of the rotation point, minus [scos] multiplied by the
/// y-coordinate of the rotation point.
double get ty => _value[3];
}
......@@ -19,6 +19,9 @@ class Rect {
}
/// Construct a rectangle from its left and top edges, its width, and its height.
///
/// To construct a [Rect] from a [Point] or [Offset] and a [Size], you can use
/// the rectangle constructor operator `&`. See [Point.&] and [Offset.&].
Rect.fromLTWH(double left, double top, double width, double height) {
_value
..[0] = left
......@@ -101,21 +104,53 @@ class Rect {
return w < h ? w : h;
}
/// The point halfway between the left and right and the top and bottom edges of this rectangle.
Point get center => new Point(left + width / 2.0, top + height / 2.0);
/// The point at the intersection of the top and left edges of this rectangle.
///
/// See also [Size.topLeft].
Point get topLeft => new Point(left, top);
/// The point at the center of the top edge of this rectangle.
///
/// See also [Size.topCenter].
Point get topCenter => new Point(left + width / 2.0, top);
/// The point at the intersection of the top and right edges of this rectangle.
///
/// See also [Size.topRight].
Point get topRight => new Point(right, top);
/// The point at the center of the left edge of this rectangle.
///
/// See also [Size.centerLeft].
Point get centerLeft => new Point(left, top + height / 2.0);
/// The point halfway between the left and right and the top and bottom edges of this rectangle.
///
/// See also [Size.center].
Point get center => new Point(left + width / 2.0, top + height / 2.0);
/// The point at the center of the right edge of this rectangle.
///
/// See also [Size.centerLeft].
Point get centerRight => new Point(right, top + height / 2.0);
/// The point at the intersection of the bottom and left edges of this rectangle.
///
/// See also [Size.bottomLeft].
Point get bottomLeft => new Point(left, bottom);
/// The point at the center of the bottom edge of this rectangle.
///
/// See also [Size.bottomLeft].
Point get bottomCenter => new Point(left + width / 2.0, bottom);
/// The point at the intersection of the bottom and right edges of this rectangle.
///
/// See also [Size.bottomRight].
Point get bottomRight => new Point(right, bottom);
/// Whether the given point lies between the left and right and the top and bottom edges of this rectangle.
///
/// Rectangles include their top and left edges but exclude their bottom and right edges.
......@@ -143,6 +178,7 @@ class Rect {
);
}
@override
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
......@@ -156,7 +192,9 @@ class Rect {
return true;
}
@override
int get hashCode => hashList(_value);
@override
String toString() => "Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})";
}
......@@ -5,38 +5,103 @@
part of dart_ui;
/// Holds a 2D floating-point size.
/// Think of this as a vector from Point(0,0) to Point(size.width, size.height)
///
/// You can think of this as a vector from Point(0,0) to Point(size.width,
/// size.height).
class Size extends OffsetBase {
/// Creates a Size with the given width and height.
const Size(double width, double height) : super(width, height);
/// Creates an instance of Size that has the same values as another.
// Used by the rendering library's _DebugSize hack.
Size.copy(Size source) : super(source.width, source.height);
/// Creates a square Size whose width and height are the given dimension.
const Size.square(double dimension) : super(dimension, dimension);
/// Creates a Size with the given width and an infinite height.
const Size.fromWidth(double width) : super(width, double.INFINITY);
/// Creates a Size with the given height and an infinite width.
const Size.fromHeight(double height) : super(double.INFINITY, height);
/// Creates a square Size whose width and height are twice the given dimension.
///
/// This is a square that contains a circle with the given radius.
const Size.fromRadius(double radius) : super(radius * 2.0, radius * 2.0);
/// The horizontal extent of this size.
double get width => _dx;
/// The vertical extent of this size.
double get height => _dy;
/// An empty size, one with a zero width and a zero height.
static const Size zero = const Size(0.0, 0.0);
/// A size whose width and height are infinite.
///
/// See also [isInfinite], which checks whether either dimension is infinite.
static const Size infinite = const Size(double.INFINITY, double.INFINITY);
dynamic operator -(dynamic other) {
/// Binary subtraction operator for Size.
///
/// Subtracting a Size from a Size returns the [Offset] that describes how
/// much bigger the left-hand-side operand is than the right-hand-side
/// operand. Adding that resulting Offset to the Size that was the
/// right-hand-side operand would return a Size equal to the Size that was the
/// left-hand-side operand. (i.e. if `sizeA - sizeB -> offsetA`, then `offsetA
/// + sizeB -> sizeA`)
///
/// Subtracting an [Offset] from a Size returns the Size that is smaller than
/// the Size operand by the difference given by the Offset operand. In other
/// words, the returned Size has a [width] consisting of the [width] of the
/// left-hand-side operand minus the [Offset.dx] dimension of the
/// right-hand-side operand, and a [height] consisting of the [height] of the
/// left-hand-side operand minus the [Offset.dy] dimension of the
/// right-hand-side operand.
dynamic operator -(OffsetBase other) {
if (other is Size)
return new Offset(width - other.width, height - other.height);
if (other is Offset)
return new Size(width - other.dx, height - other.dy);
throw new ArgumentError(other);
}
/// Binary addition operator for adding an Offset to a Size. Returns a Size
/// whose [width] is the sum of the [width] of the left-hand-side operand, a
/// Size, and the [Offset.dx] dimension of the right-hand-side operand, an
/// [Offset], and whose [height] is the sum of the [height] of the
/// left-hand-side operand and the [Offset.dy] dimension of the
/// right-hand-side operand.
Size operator +(Offset other) => new Size(width + other.dx, height + other.dy);
/// Multiplication operator. Returns a size whose dimensions are the
/// dimensions of the left-hand-side operand (a Size) multiplied by the
/// scalar right-hand-side operand (a double).
Size operator *(double operand) => new Size(width * operand, height * operand);
/// Division operator. Returns a size whose dimensions are the dimensions of
/// the left-hand-side operand (a Size) divided by the scalar right-hand-side
/// operand (a double).
Size operator /(double operand) => new Size(width / operand, height / operand);
/// Integer (truncating) division operator. Returns a size whose dimensions
/// are the dimensions of the left-hand-side operand (a Size) divided by the
/// scalar right-hand-side operand (a double), rounded towards zero.
Size operator ~/(double operand) => new Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble());
/// Modulo (remainder) operator. Returns a size whose dimensions are the
/// remainder of dividing the left-hand-side operand (a Size) by the scalar
/// right-hand-side operand (a double).
Size operator %(double operand) => new Size(width % operand, height % operand);
/// Whether this size encloses a non-zero area.
///
/// Negative areas are considered empty.
bool get isEmpty => width <= 0.0 || height <= 0.0;
/// The lesser of the width and the height.
/// The lesser of the [width] and the [height].
double get shortestSide {
double w = width.abs();
double h = height.abs();
......@@ -45,10 +110,65 @@ class Size extends OffsetBase {
// Convenience methods that do the equivalent of calling the similarly named
// methods on a Rect constructed from the given origin and this size.
Point center(Point origin) => new Point(origin.x + width / 2.0, origin.y + height / 2.0);
/// The point at the intersection of the top and left edges of the rectangle
/// described by the given point (which is interpreted as the top-left corner)
/// and this size.
///
/// See also [Rect.topLeft].
Point topLeft(Point origin) => origin;
/// The point at the center of the top edge of the rectangle described by the
/// given point (which is interpreted as the top-left corner) and this size.
///
/// See also [Rect.topCenter].
Point topCenter(Point origin) => new Point(origin.x + width / 2.0, origin.y);
/// The point at the intersection of the top and right edges of the rectangle
/// described by the given point (which is interpreted as the top-left corner)
/// and this size.
///
/// See also [Rect.topRight].
Point topRight(Point origin) => new Point(origin.x + width, origin.y);
/// The point at the center of the left edge of the rectangle described by the
/// given point (which is interpreted as the top-left corner) and this size.
///
/// See also [Rect.centerLeft].
Point centerLeft(Point origin) => new Point(origin.x, origin.y + height / 2.0);
/// The point halfway between the left and right and the top and bottom edges
/// of the rectangle described by the given point (which is interpreted as the
/// top-left corner) and this size.
///
/// See also [Rect.center].
Point center(Point origin) => new Point(origin.x + width / 2.0, origin.y + height / 2.0);
/// The point at the center of the right edge of the rectangle described by the
/// given point (which is interpreted as the top-left corner) and this size.
///
/// See also [Rect.centerLeft].
Point centerRight(Point origin) => new Point(origin.x + width, origin.y + height / 2.0);
/// The point at the intersection of the bottom and left edges of the
/// rectangle described by the given point (which is interpreted as the
/// top-left corner) and this size.
///
/// See also [Rect.bottomLeft].
Point bottomLeft(Point origin) => new Point(origin.x, origin.y + height);
/// The point at the center of the bottom edge of the rectangle described by
/// the given point (which is interpreted as the top-left corner) and this
/// size.
///
/// See also [Rect.bottomLeft].
Point bottomCenter(Point origin) => new Point(origin.x + width / 2.0, origin.y + height);
/// The point at the intersection of the bottom and right edges of the
/// rectangle described by the given point (which is interpreted as the
/// top-left corner) and this size.
///
/// See also [Rect.bottomRight].
Point bottomRight(Point origin) => new Point(origin.x + width, origin.y + height);
/// Linearly interpolate between two sizes
......@@ -65,7 +185,9 @@ class Size extends OffsetBase {
}
/// Compares two Sizes for equality.
@override
bool operator ==(dynamic other) => other is Size && super == other;
@override
String toString() => "Size(${width?.toStringAsFixed(1)}, ${height?.toStringAsFixed(1)})";
}
......@@ -14,7 +14,7 @@ part of dart_ui;
/// can be used to blend the pixels. The image below shows the effects
/// of these modes.
///
/// [![Open Skia fiddle to view image.](https://fiddle.skia.org/i/871ab434ce53a611e5eb2b662c69d154_raster.png)](https://fiddle.skia.org/c/871ab434ce53a611e5eb2b662c69d154)
/// [![Open Skia fiddle to view image.](https://flutter.io/images/transfer_mode.png)](https://fiddle.skia.org/c/864acd0659c7a866ea7296a3184b8bdd)
///
/// See [Paint.transferMode].
enum TransferMode {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册