| // Copyright 2015 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. |
| |
| part of dart_ui; |
| |
| Color _scaleAlpha(Color a, double factor) { |
| return a.withAlpha((a.alpha * factor).round()); |
| } |
| |
| /// An immutable 32 bit color value in ARGB |
| class Color { |
| /// Construct a color from the lower 32 bits of an int. |
| /// |
| /// Bits 24-31 are the alpha value. |
| /// Bits 16-23 are the red value. |
| /// Bits 8-15 are the green value. |
| /// Bits 0-7 are the blue value. |
| const Color(int value) : value = value & 0xFFFFFFFF; |
| |
| /// Construct a color from the lower 8 bits of four integers. |
| const Color.fromARGB(int a, int r, int g, int b) : |
| value = ((((a & 0xff) << 24) | |
| ((r & 0xff) << 16) | |
| ((g & 0xff) << 8) | |
| ((b & 0xff) << 0)) & 0xFFFFFFFF); |
| |
| /// A 32 bit value representing this color. |
| /// |
| /// Bits 24-31 are the alpha value. |
| /// Bits 16-23 are the red value. |
| /// Bits 8-15 are the green value. |
| /// Bits 0-7 are the blue value. |
| final int value; |
| |
| /// The alpha channel of this color in an 8 bit value. |
| int get alpha => (0xff000000 & value) >> 24; |
| |
| /// The alpha channel of this color as a double. |
| double get opacity => alpha / 0xFF; |
| |
| /// The red channel of this color in an 8 bit value. |
| int get red => (0x00ff0000 & value) >> 16; |
| |
| /// The green channel of this color in an 8 bit value. |
| int get green => (0x0000ff00 & value) >> 8; |
| |
| /// The blue channel of this color in an 8 bit value. |
| int get blue => (0x000000ff & value) >> 0; |
| |
| /// Returns a new color that matches this color with the alpha channel |
| /// replaced with a (which ranges from 0 to 255). |
| Color withAlpha(int a) { |
| return new Color.fromARGB(a, red, green, blue); |
| } |
| |
| /// Returns a new color that matches this color with the alpha channel |
| /// replaced with the given opacity (which ranges from 0.0 to 1.0). |
| Color withOpacity(double opacity) { |
| assert(opacity >= 0.0 && opacity <= 1.0); |
| return withAlpha((255.0 * opacity).round()); |
| } |
| |
| /// Returns a new color that matches this color with the red channel replaced |
| /// with r. |
| Color withRed(int r) { |
| return new Color.fromARGB(alpha, r, green, blue); |
| } |
| |
| /// Returns a new color that matches this color with the green channel |
| /// replaced with g. |
| Color withGreen(int g) { |
| return new Color.fromARGB(alpha, red, g, blue); |
| } |
| |
| /// Returns a new color that matches this color with the blue channel replaced |
| /// with b. |
| Color withBlue(int b) { |
| return new Color.fromARGB(alpha, red, green, b); |
| } |
| |
| /// Linearly interpolate between two colors |
| /// |
| /// If either color is null, this function linearly interpolates from a |
| /// transparent instance of the other color. |
| static Color lerp(Color a, Color b, double t) { |
| if (a == null && b == null) |
| return null; |
| if (a == null) |
| return _scaleAlpha(b, t); |
| if (b == null) |
| return _scaleAlpha(a, 1.0 - t); |
| return new Color.fromARGB( |
| lerpDouble(a.alpha, b.alpha, t).toInt(), |
| lerpDouble(a.red, b.red, t).toInt(), |
| lerpDouble(a.green, b.green, t).toInt(), |
| lerpDouble(a.blue, b.blue, t).toInt() |
| ); |
| } |
| |
| @override |
| bool operator ==(dynamic other) { |
| if (other is! Color) |
| return false; |
| final Color typedOther = other; |
| return value == typedOther.value; |
| } |
| |
| @override |
| int get hashCode => value.hashCode; |
| |
| @override |
| String toString() => "Color(0x${value.toRadixString(16).padLeft(8, '0')})"; |
| } |
| |
| /// Algorithms to use when painting on the canvas. |
| /// |
| /// When drawing a shape or image onto a canvas, different algorithms |
| /// can be used to blend the pixels. The image below shows the effects |
| /// of these modes. |
| /// |
| /// [](https://fiddle.skia.org/c/864acd0659c7a866ea7296a3184b8bdd) |
| /// |
| /// See [Paint.transferMode]. |
| enum TransferMode { |
| // This list comes from Skia's SkXfermode.h and the values (order) should be |
| // kept in sync. |
| // See: https://skia.org/user/api/skpaint#SkXfermode |
| |
| clear, |
| src, |
| dst, |
| srcOver, |
| dstOver, |
| srcIn, |
| dstIn, |
| srcOut, |
| dstOut, |
| srcATop, |
| dstATop, |
| xor, |
| plus, |
| modulate, |
| |
| // Following blend modes are defined in the CSS Compositing standard. |
| |
| screen, // The last coeff mode. |
| |
| overlay, |
| darken, |
| lighten, |
| colorDodge, |
| colorBurn, |
| hardLight, |
| softLight, |
| difference, |
| exclusion, |
| multiply, // The last separable mode. |
| |
| hue, |
| saturation, |
| color, |
| luminosity, |
| } |
| |
| /// Quality levels for image filters. |
| /// |
| /// See [Paint.filterQuality]. |
| enum FilterQuality { |
| // This list comes from Skia's SkFilterQuality.h and the values (order) should |
| // be kept in sync. |
| |
| /// 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, |
| } |
| |
| /// Styles to use for line endings. |
| /// |
| /// See [Paint.strokeCap]. |
| enum StrokeCap { |
| /// Begin and end contours with a flat edge and no extension. |
| butt, |
| |
| /// Begin and end contours with a semi-circle extension. |
| round, |
| |
| /// Begin and end contours with a half square extension. This is |
| /// similar to extending each contour by half the stroke width (as |
| /// given by [Paint.strokeWidth]). |
| square, |
| } |
| |
| /// Strategies for painting shapes and paths on a canvas. |
| /// |
| /// See [Paint.style]. |
| enum PaintingStyle { |
| // This list comes from Skia's SkPaint.h and the values (order) should be kept |
| // in sync. |
| |
| /// Apply the [Paint] to the inside of the shape. For example, when |
| /// applied to the [Paint.drawCircle] call, this results in a disc |
| /// of the given size being painted. |
| fill, |
| |
| /// Apply the [Paint] to the edge of the shape. For example, when |
| /// applied to the [Paint.drawCircle] call, this results is a hoop |
| /// of the given size being painted. The line drawn on the edge will |
| /// be the width given by the [Paint.strokeWidth] property. |
| stroke, |
| |
| /// Apply the [Paint] to the inside of the shape and the edge of the |
| /// shape at the same time. The resulting drawing is similar to what |
| /// would be achieved by inflating the shape by half the stroke |
| /// width (as given by [Paint.strokeWidth]), and then using [fill]. |
| strokeAndFill, |
| } |
| |
| // If we actually run on big endian machines, we'll need to do something smarter |
| // here. We don't use [Endianness.HOST_ENDIAN] because it's not a compile-time |
| // constant and can't propagate into the set/get calls. |
| const Endianness _kFakeHostEndian = Endianness.LITTLE_ENDIAN; |
| |
| /// A description of the style to use when drawing on a [Canvas]. |
| /// |
| /// Most APIs on [Canvas] take a [Paint] object to describe the style |
| /// to use for that operation. |
| class Paint { |
| // Paint objects are encoded in two buffers: |
| // |
| // * _data is binary data in four-byte fields, each of which is either a |
| // uint32_t or a float. The default value for each field is encoded as |
| // zero to make initialization trivial. Most values already have a default |
| // value of zero, but some, such a color, have a non-zero default value. |
| // To encode or decode these values, XOR the value with the default value. |
| // |
| // * _objects is a list of unencodable objects, typically wrappers for native |
| // objects. The objects are simply stored in the list without any additional |
| // encoding. |
| // |
| // The binary format must match the deserialization code in paint.cc. |
| |
| final ByteData _data = new ByteData(_kDataByteCount); |
| static const int _kIsAntiAliasIndex = 0; |
| static const int _kColorIndex = 1; |
| static const int _kTransferModeIndex = 2; |
| static const int _kStyleIndex = 3; |
| static const int _kStrokeWidthIndex = 4; |
| static const int _kStrokeCapIndex = 5; |
| static const int _kFilterQualityIndex = 6; |
| static const int _kColorFilterIndex = 7; |
| static const int _kColorFilterColorIndex = 8; |
| static const int _kColorFilterTransferModeIndex = 9; |
| |
| static const int _kIsAntiAliasOffset = _kIsAntiAliasIndex << 2; |
| static const int _kColorOffset = _kColorIndex << 2; |
| static const int _kTransferModeOffset = _kTransferModeIndex << 2; |
| static const int _kStyleOffset = _kStyleIndex << 2; |
| static const int _kStrokeWidthOffset = _kStrokeWidthIndex << 2; |
| static const int _kStrokeCapOffset = _kStrokeCapIndex << 2; |
| static const int _kFilterQualityOffset = _kFilterQualityIndex << 2; |
| static const int _kColorFilterOffset = _kColorFilterIndex << 2; |
| static const int _kColorFilterColorOffset = _kColorFilterColorIndex << 2; |
| static const int _kColorFilterTransferModeOffset = _kColorFilterTransferModeIndex << 2; |
| // If you add more fields, remember to update _kDataByteCount. |
| static const int _kDataByteCount = 40; |
| |
| // Binary format must match the deserialization code in paint.cc. |
| List<dynamic> _objects; |
| static const int _kMaskFilterIndex = 0; |
| static const int _kShaderIndex = 1; |
| static const int _kObjectCount = 2; // Must be one larger than the largest index |
| |
| /// Whether to apply anti-aliasing to lines and images drawn on the |
| /// canvas. |
| /// |
| /// Defaults to true. |
| bool get isAntiAlias { |
| return _data.getInt32(_kIsAntiAliasOffset, _kFakeHostEndian) == 0; |
| } |
| set isAntiAlias(bool value) { |
| // We encode true as zero and false as one because the default value, which |
| // we always encode as zero, is true. |
| final int encoded = value ? 0 : 1; |
| _data.setInt32(_kIsAntiAliasOffset, encoded, _kFakeHostEndian); |
| } |
| |
| static const int _kColorDefault = 0xFF000000; |
| |
| /// The color to use when stroking or filling a shape. |
| /// |
| /// Defaults to opaque 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 get color { |
| final int encoded = _data.getInt32(_kColorOffset, _kFakeHostEndian); |
| return new Color(encoded ^ _kColorDefault); |
| } |
| set color(Color value) { |
| assert(value != null); |
| final int encoded = value.value ^ _kColorDefault; |
| _data.setInt32(_kColorOffset, encoded, _kFakeHostEndian); |
| } |
| |
| static final int _kTransferModeDefault = TransferMode.srcOver.index; |
| |
| /// 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. |
| /// |
| /// Defaults to [TransferMode.srcOver]. |
| TransferMode get transferMode { |
| final int encoded = _data.getInt32(_kTransferModeOffset, _kFakeHostEndian); |
| return TransferMode.values[encoded ^ _kTransferModeDefault]; |
| } |
| set transferMode(TransferMode value) { |
| assert(value != null); |
| final int encoded = value.index ^ _kTransferModeDefault; |
| _data.setInt32(_kTransferModeOffset, encoded, _kFakeHostEndian); |
| } |
| |
| /// Whether to paint inside shapes, the edges of shapes, or both. |
| /// |
| /// Defaults to [PaintingStyle.fill]. |
| PaintingStyle get style { |
| return PaintingStyle.values[_data.getInt32(_kStyleOffset, _kFakeHostEndian)]; |
| } |
| set style(PaintingStyle value) { |
| assert(value != null); |
| final int encoded = value.index; |
| _data.setInt32(_kStyleOffset, encoded, _kFakeHostEndian); |
| } |
| |
| /// How wide to make edges drawn when [style] is set to |
| /// [PaintingStyle.stroke] or [PaintingStyle.strokeAndFill]. The |
| /// width is given in logical pixels measured in the direction |
| /// orthogonal to the direction of the path. |
| /// |
| /// Defaults to 0.0, which correspond to a hairline width. |
| double get strokeWidth { |
| return _data.getFloat32(_kStrokeWidthOffset, _kFakeHostEndian); |
| } |
| set strokeWidth(double value) { |
| assert(value != null); |
| final double encoded = value; |
| _data.setFloat32(_kStrokeWidthOffset, encoded, _kFakeHostEndian); |
| } |
| |
| /// The kind of finish to place on the end of lines drawn when |
| /// [style] is set to [PaintingStyle.stroke] or |
| /// [PaintingStyle.strokeAndFill]. |
| /// |
| /// Defaults to [StrokeCap.butt], i.e. no caps. |
| StrokeCap get strokeCap { |
| return StrokeCap.values[_data.getInt32(_kStrokeCapOffset, _kFakeHostEndian)]; |
| } |
| set strokeCap(StrokeCap value) { |
| assert(value != null); |
| final int encoded = value.index; |
| _data.setInt32(_kStrokeCapOffset, encoded, _kFakeHostEndian); |
| } |
| |
| /// 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 get maskFilter { |
| if (_objects == null) |
| return null; |
| return _objects[_kMaskFilterIndex]; |
| } |
| set maskFilter(MaskFilter value) { |
| _objects ??= new List<dynamic>(_kObjectCount); |
| _objects[_kMaskFilterIndex] = value; |
| } |
| |
| /// 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]. |
| /// |
| /// Defaults to [FilterQuality.none]. |
| // TODO(ianh): verify that the image drawing methods actually respect this |
| FilterQuality get filterQuality { |
| return FilterQuality.values[_data.getInt32(_kFilterQualityOffset, _kFakeHostEndian)]; |
| } |
| set filterQuality(FilterQuality value) { |
| assert(value != null); |
| final int encoded = value.index; |
| _data.setInt32(_kFilterQualityOffset, encoded, _kFakeHostEndian); |
| } |
| |
| /// 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 get shader { |
| if (_objects == null) |
| return null; |
| return _objects[_kShaderIndex]; |
| } |
| set shader(Shader value) { |
| _objects ??= new List<dynamic>(_kObjectCount); |
| _objects[_kShaderIndex] = value; |
| } |
| |
| /// 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 get colorFilter { |
| final bool isNull = _data.getInt32(_kColorFilterOffset, _kFakeHostEndian) == 0; |
| if (isNull) |
| return null; |
| return new ColorFilter.mode( |
| new Color(_data.getInt32(_kColorFilterColorOffset, _kFakeHostEndian)), |
| TransferMode.values[_data.getInt32(_kColorFilterTransferModeOffset, _kFakeHostEndian)] |
| ); |
| } |
| set colorFilter(ColorFilter value) { |
| if (value == null) { |
| _data.setInt32(_kColorFilterOffset, 0, _kFakeHostEndian); |
| _data.setInt32(_kColorFilterColorOffset, 0, _kFakeHostEndian); |
| _data.setInt32(_kColorFilterTransferModeOffset, 0, _kFakeHostEndian); |
| } else { |
| assert(value._color != null); |
| assert(value._transferMode != null); |
| _data.setInt32(_kColorFilterOffset, 1, _kFakeHostEndian); |
| _data.setInt32(_kColorFilterColorOffset, value._color.value, _kFakeHostEndian); |
| _data.setInt32(_kColorFilterTransferModeOffset, value._transferMode.index, _kFakeHostEndian); |
| } |
| } |
| |
| @override |
| String toString() { |
| StringBuffer result = new StringBuffer(); |
| String semicolon = ''; |
| result.write('Paint('); |
| if (style == PaintingStyle.stroke || style == PaintingStyle.strokeAndFill) { |
| result.write('$style'); |
| if (strokeWidth != 0.0) |
| result.write(' $strokeWidth'); |
| else |
| result.write(' hairline'); |
| if (strokeCap != StrokeCap.butt) |
| result.write(' $strokeCap'); |
| semicolon = '; '; |
| } |
| if (isAntiAlias != true) { |
| result.write('${semicolon}antialias off'); |
| semicolon = '; '; |
| } |
| if (color != const Color(0xFF000000)) { |
| if (color != null) |
| result.write('$semicolon$color'); |
| else |
| result.write('${semicolon}no color'); |
| semicolon = '; '; |
| } |
| if (transferMode != TransferMode.srcOver) { |
| result.write('$semicolon$transferMode'); |
| semicolon = '; '; |
| } |
| if (colorFilter != null) { |
| result.write('${semicolon}colorFilter: $colorFilter'); |
| semicolon = '; '; |
| } |
| if (maskFilter != null) { |
| result.write('${semicolon}maskFilter: $maskFilter'); |
| semicolon = '; '; |
| } |
| if (filterQuality != FilterQuality.none) { |
| result.write('${semicolon}filterQuality: $filterQuality'); |
| semicolon = '; '; |
| } |
| if (shader != null) |
| result.write('${semicolon}shader: $shader'); |
| result.write(')'); |
| return result.toString(); |
| } |
| } |
| |
| /// Opaque handle to raw decoded image data (pixels). |
| /// |
| /// To obtain an Image object, use the [decodeImageFromDataPipe] or |
| /// [decodeImageFromList] functions. |
| /// |
| /// To draw an Image, use one of the methods on the [Canvas] class, such as |
| /// [drawImage]. |
| abstract class Image extends NativeFieldWrapperClass2 { |
| /// The number of image pixels along the image's horizontal axis. |
| int get width native "Image_width"; |
| |
| /// The number of image pixels along the image's vertical axis. |
| int get height native "Image_height"; |
| |
| /// Release the resources used by this object. The object is no longer usable |
| /// after this method is called. |
| void dispose() native "Image_dispose"; |
| |
| @override |
| String toString() => '[$width\u00D7$height]'; |
| } |
| |
| /// Callback signature for [decodeImageFromDataPipe] and [decodeImageFromList]. |
| typedef void ImageDecoderCallback(Image result); |
| |
| /// Convert an image file from a mojo pipe into an [Image] object. |
| void decodeImageFromDataPipe(int handle, ImageDecoderCallback callback) |
| native "decodeImageFromDataPipe"; |
| |
| /// Convert an image file from a byte array into an [Image] object. |
| void decodeImageFromList(Uint8List list, ImageDecoderCallback callback) |
| native "decodeImageFromList"; |
| |
| /// Determines how the interior of a [Path] is calculated. |
| enum PathFillType { |
| /// The interior is defined by a non-zero sum of signed edge crossings. |
| winding, |
| |
| /// The interior is defined by an odd number of edge crossings. |
| evenOdd, |
| |
| /// The interior is defined as the exterior region calculated by [winding]. |
| inverseWinding, |
| |
| /// The interior is defined as the exterior region calculated by [evenOdd]. |
| inverseEvenOdd, |
| } |
| |
| /// A complex, one-dimensional subset of a plane. |
| /// |
| /// A path consists of a number of subpaths, and a _current point_. |
| /// |
| /// Subpaths consist of segments of various types, such as lines, |
| /// arcs, or beziers. Subpaths can be open or closed, and can |
| /// self-intersect. |
| /// |
| /// Closed subpaths enclose a (possibly discontiguous) region of the |
| /// plane based on whether a line from a given point on the plane to a |
| /// point at infinity intersects the path an even (non-enclosed) or an |
| /// odd (enclosed) number of times. |
| /// |
| /// The _current point_ is initially at the origin. After each |
| /// operation adding a segment to a subpath, the current point is |
| /// updated to the end of that segment. |
| /// |
| /// 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"; |
| |
| /// Determines how the interior of this path is calculated. |
| PathFillType get fillType => PathFillType.values[_getFillType()]; |
| set fillType (PathFillType value) => _setFillType(value.index); |
| |
| int _getFillType() native "Path_getFillType"; |
| void _setFillType(int fillType) native "Path_setFillType"; |
| |
| /// Starts a new subpath at the given coordinate. |
| void moveTo(double x, double y) native "Path_moveTo"; |
| |
| /// Starts a new subpath at the given offset from the current point. |
| void relativeMoveTo(double dx, double dy) native "Path_relativeMoveTo"; |
| |
| /// Adds a straight line segment from the current point to the given |
| /// point. |
| void lineTo(double x, double y) native "Path_lineTo"; |
| |
| /// Adds a straight line segment from the current point to the point |
| /// at the given offset from the current point. |
| void relativeLineTo(double dx, double dy) native "Path_relativeLineTo"; |
| |
| /// Adds a quadratic bezier segment that curves from the current |
| /// point to the given point (x2,y2), using the control point |
| /// (x1,y1). |
| void quadraticBezierTo(double x1, double y1, double x2, double y2) native "Path_quadraticBezierTo"; |
| |
| /// Adds a quadratic bezier segment that curves from the current |
| /// point to the point at the offset (x2,y2) from the current point, |
| /// using the control point at the offset (x1,y1) from the current |
| /// point. |
| void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) native "Path_relativeQuadraticBezierTo"; |
| |
| /// Adds a cubic bezier segment that curves from the current point |
| /// to the given point (x3,y3), using the control points (x1,y1) and |
| /// (x2,y2). |
| void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) native "Path_cubicTo"; |
| |
| /// Adds a cubcic bezier segment that curves from the current point |
| /// to the point at the offset (x3,y3) from the current point, using |
| /// the control points at the offsets (x1,y1) and (x2,y2) from the |
| /// current point. |
| void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) native "Path_relativeCubicTo"; |
| |
| /// Adds a bezier segment that curves from the current point to the |
| /// given point (x2,y2), using the control points (x1,y1) and the |
| /// weight w. If the weight is greater than 1, then the curve is a |
| /// hyperbola; if the weight equals 1, it's a parabola; and if it is |
| /// less than 1, it is an ellipse. |
| void conicTo(double x1, double y1, double x2, double y2, double w) native "Path_conicTo"; |
| |
| /// Adds a bezier segment that curves from the current point to the |
| /// point at the offset (x2,y2) from the current point, using the |
| /// control point at the offset (x1,y1) from the current point and |
| /// the weight w. If the weight is greater than 1, then the curve is |
| /// a hyperbola; if the weight equals 1, it's a parabola; and if it |
| /// is less than 1, it is an ellipse. |
| void relativeConicTo(double x1, double y1, double x2, double y2, double w) native "Path_relativeConicTo"; |
| |
| /// If the [forceMoveTo] argument is false, adds a straight line |
| /// segment and an arc segment. |
| /// |
| /// If the [forceMoveTo] argument is true, starts a new subpath |
| /// consisting of an arc segment. |
| /// |
| /// In either case, the arc segment consists of the arc that follows |
| /// the edge of the oval bounded by the given rectangle, from |
| /// startAngle radians around the oval up to startAngle + sweepAngle |
| /// radians around the oval, with zero radians being the point on |
| /// the right hand side of the oval that crosses the horizontal line |
| /// that intersects the center of the rectangle and with positive |
| /// angles going clockwise around the oval. |
| /// |
| /// The line segment added if [forceMoveTo] is false starts at the |
| /// current point and ends at the start of the arc. |
| void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { |
| _arcTo(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, forceMoveTo); |
| } |
| void _arcTo(double left, double top, double right, double bottom, |
| double startAngle, double sweepAngle, bool forceMoveTo) native "Path_arcTo"; |
| |
| /// Adds a new subpath that consists of four lines that outline the |
| /// given rectangle. |
| void addRect(Rect rect) { |
| _addRect(rect.left, rect.top, rect.right, rect.bottom); |
| } |
| void _addRect(double left, double top, double right, double bottom) native "Path_addRect"; |
| |
| /// Adds a new subpath that consists of a curve that forms the |
| /// ellipse that fills the given rectangle. |
| void addOval(Rect oval) { |
| _addOval(oval.left, oval.top, oval.right, oval.bottom); |
| } |
| void _addOval(double left, double top, double right, double bottom) native "Path_addOval"; |
| |
| /// Adds a new subpath with one arc segment that consists of the arc |
| /// that follows the edge of the oval bounded by the given |
| /// rectangle, from startAngle radians around the oval up to |
| /// startAngle + sweepAngle radians around the oval, with zero |
| /// radians being the point on the right hand side of the oval that |
| /// crosses the horizontal line that intersects the center of the |
| /// rectangle and with positive angles going clockwise around the |
| /// oval. |
| void addArc(Rect oval, double startAngle, double sweepAngle) { |
| _addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle); |
| } |
| void _addArc(double left, double top, double right, double bottom, |
| double startAngle, double sweepAngle) native "Path_addArc"; |
| |
| /// Adds a new subpath with a sequence of line segments that connect the given |
| /// points. If `close` is true, a final line segment will be added that |
| /// connects the last point to the first point. |
| void addPolygon(List<Point> points, bool close) { |
| _addPolygon(_encodePointList(points), close); |
| } |
| void _addPolygon(Float32List points, bool close) native "Path_addPolygon"; |
| |
| /// Adds a new subpath that consists of the straight lines and |
| /// curves needed to form the rounded rectangle described by the |
| /// argument. |
| void addRRect(RRect rrect) => _addRRect(rrect._value); |
| void _addRRect(Float32List rrect) native "Path_addRRect"; |
| |
| /// Adds a new subpath that consists of the given path offset by the given |
| /// offset. |
| void addPath(Path path, Offset offset) => _addPath(path, offset.dx, offset.dy); |
| void _addPath(Path path, double dx, double dy) native "Path_addPath"; |
| |
| /// Adds the given path to this path by extending the current segment of this |
| /// path with the the first segment of the given path. |
| void extendWithPath(Path path, Offset offset) => _extendWithPath(path, offset.dx, offset.dy); |
| void _extendWithPath(Path path, double dx, double dy) native "Path_extendWithPath"; |
| |
| /// Closes the last subpath, as if a straight line had been drawn |
| /// from the current point to the first point of the subpath. |
| void close() native "Path_close"; |
| |
| /// Clears the [Path] object of all subpaths, returning it to the |
| /// same state it had when it was created. The _current point_ is |
| /// reset to the origin. |
| void reset() native "Path_reset"; |
| |
| /// Tests to see if the point is within the path. (That is, whether |
| /// the point would be in the visible portion of the path if the |
| /// path was used with [Canvas.clipPath].) |
| /// |
| /// Returns true if the point is in the path, and false otherwise. |
| bool contains(Point position) => _contains(position.x, position.y); |
| bool _contains(double x, double y) native "Path_contains"; |
| |
| /// Returns a copy of the path with all the segments of every |
| /// subpath translated by the given offset. |
| Path shift(Offset offset) => _shift(offset.dx, offset.dy); |
| Path _shift(double dx, double dy) native "Path_shift"; |
| |
| /// Returns a copy of the path with all the segments of every |
| /// subpath transformed by the given matrix. |
| Path transform(Float64List matrix4) { |
| if (matrix4.length != 16) |
| throw new ArgumentError("[matrix4] must have 16 entries."); |
| return _transform(matrix4); |
| } |
| Path _transform(Float64List matrix4) native "Path_transform"; |
| } |
| |
| /// Styles to use for blurs in [MaskFilter] objects. |
| enum BlurStyle { |
| // These mirror SkBlurStyle and must be kept in sync. |
| |
| /// Fuzzy inside and outside. This is useful for painting shadows that are |
| /// offset from the shape that ostensibly is casting the shadow. |
| normal, |
| |
| /// 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. 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. 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 |
| }) { |
| _constructor(style.index, sigma, _makeBlurFlags(ignoreTransform, highQuality)); |
| } |
| void _constructor(int style, double sigma, int flags) native "MaskFilter_constructor"; |
| |
| // Convert constructor parameters to the SkBlurMaskFilter::BlurFlags type. |
| static int _makeBlurFlags(bool ignoreTransform, bool highQuality) { |
| int flags = 0; |
| if (ignoreTransform) |
| flags |= 0x01; |
| if (highQuality) |
| flags |= 0x02; |
| return flags; |
| } |
| } |
| |
| /// 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. |
| /// |
| /// Instances of this class are used with [Paint.colorFilter] on [Paint] |
| /// objects. |
| class ColorFilter { |
| /// 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) |
| : _color = color, _transferMode = transferMode; |
| |
| final Color _color; |
| final TransferMode _transferMode; |
| |
| @override |
| bool operator ==(dynamic other) { |
| if (other is! ColorFilter) |
| return false; |
| final ColorFilter typedOther = other; |
| return _color == typedOther._color && |
| _transferMode == typedOther._transferMode; |
| } |
| |
| @override |
| int get hashCode => hashValues(_color, _transferMode); |
| |
| @override |
| String toString() => "ColorFilter($_color, $TransferMode)"; |
| } |
| |
| /// A filter operation to apply to a raster image. |
| /// |
| /// See [SceneBuilder.pushBackdropFilter]. |
| class ImageFilter extends NativeFieldWrapperClass2 { |
| void _constructor() native "ImageFilter_constructor"; |
| |
| /// A source filter containing an image. |
| // ImageFilter.image({ Image image }) { |
| // _constructor(); |
| // _initImage(image); |
| // } |
| // void _initImage(Image image) native "ImageFilter_initImage"; |
| |
| /// A source filter containing a picture. |
| // ImageFilter.picture({ Picture picture }) { |
| // _constructor(); |
| // _initPicture(picture); |
| // } |
| // void _initPicture(Picture picture) native "ImageFilter_initPicture"; |
| |
| /// Creates an image filter that applies a Gaussian blur. |
| ImageFilter.blur({ double sigmaX: 0.0, double sigmaY: 0.0 }) { |
| _constructor(); |
| _initBlur(sigmaX, sigmaY); |
| } |
| void _initBlur(double sigmaX, double sigmaY) native "ImageFilter_initBlur"; |
| } |
| |
| /// Base class for objects such as [Gradient] and [ImageShader] which |
| /// correspond to shaders as used by [Paint.shader]. |
| abstract class Shader extends NativeFieldWrapperClass2 { } |
| |
| /// Defines what happens at the edge of the gradient. |
| enum TileMode { |
| /// Edge is clamped to the final color. |
| clamp, |
| |
| /// Edge is repeated from first color to last. |
| repeated, |
| |
| /// Edge is mirrored from last color to first. |
| mirror, |
| } |
| |
| Int32List _encodeColorList(List<Color> colors) { |
| final int colorCount = colors.length; |
| final Int32List result = new Int32List(colorCount); |
| for (int i = 0; i < colorCount; ++i) |
| result[i] = colors[i].value; |
| return result; |
| } |
| |
| Float32List _encodePointList(List<Point> points) { |
| final int pointCount = points.length; |
| final Float32List result = new Float32List(pointCount * 2); |
| for (int i = 0; i < pointCount; ++i) { |
| final int xIndex = i * 2; |
| final int yIndex = xIndex + 1; |
| final Point point = points[i]; |
| result[xIndex] = point.x; |
| result[yIndex] = point.y; |
| } |
| return result; |
| } |
| |
| /// 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. |
| /// |
| /// Use the [Gradient.linear] or [Gradient.radial] constructors to |
| /// obtain a usable [Gradient] object. |
| 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. 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, |
| TileMode tileMode = TileMode.clamp]) { |
| if (endPoints == null || endPoints.length != 2) |
| throw new ArgumentError("Expected exactly 2 [endPoints]."); |
| _validateColorStops(colors, colorStops); |
| final Float32List endPointsBuffer = _encodePointList(endPoints); |
| final Int32List colorsBuffer = _encodeColorList(colors); |
| final Float32List colorStopsBuffer = colorStops == null ? null : new Float32List.fromList(colorStops); |
| _constructor(); |
| _initLinear(endPointsBuffer, colorsBuffer, colorStopsBuffer, tileMode.index); |
| } |
| void _initLinear(Float32List endPoints, Int32List colors, Float32List 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. 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, |
| [List<double> colorStops = null, |
| TileMode tileMode = TileMode.clamp]) { |
| _validateColorStops(colors, colorStops); |
| final Int32List colorsBuffer = _encodeColorList(colors); |
| final Float32List colorStopsBuffer = colorStops == null ? null : new Float32List.fromList(colorStops); |
| _constructor(); |
| _initRadial(center.x, center.y, radius, colorsBuffer, colorStopsBuffer, tileMode.index); |
| } |
| void _initRadial(double centerX, double centerY, double radius, Int32List colors, Float32List colorStops, int tileMode) native "Gradient_initRadial"; |
| |
| static void _validateColorStops(List<Color> colors, List<double> colorStops) { |
| if (colorStops != null && colors.length != colorStops.length) |
| throw new ArgumentError("[colors] and [colorStops] parameters must be equal length."); |
| } |
| } |
| |
| /// 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"); |
| if (tmx == null) |
| throw new ArgumentError("[tmx] argument cannot be null"); |
| if (tmy == null) |
| throw new ArgumentError("[tmy] argument cannot be null"); |
| if (matrix4 == null) |
| throw new ArgumentError("[matrix4] argument cannot be null"); |
| if (matrix4.length != 16) |
| throw new ArgumentError("[matrix4] must have 16 entries."); |
| _constructor(); |
| _initWithImage(image, tmx.index, tmy.index, matrix4); |
| } |
| void _constructor() native "ImageShader_constructor"; |
| void _initWithImage(Image image, int tmx, int tmy, Float64List matrix4) native "ImageShader_initWithImage"; |
| } |
| |
| /// Defines how a list of points is interpreted when drawing a set of triangles. |
| /// |
| /// Used by [Canvas.drawVertices]. |
| enum VertexMode { |
| /// Draw each sequence of three points as the vertices of a triangle. |
| triangles, |
| |
| /// Draw each sliding window of three points as the vertices of a triangle. |
| triangleStrip, |
| |
| /// Draw the first point and each sliding window of two points as the vertices of a triangle. |
| triangleFan, |
| } |
| |
| /// Defines how a list of points is interpreted when drawing a set of points. |
| /// |
| /// Used by [Canvas.drawPoints]. |
| enum PointMode { |
| /// Draw each point separately. |
| /// |
| /// If the [Paint.strokeCap] is [StrokeCat.round], then each point is drawn |
| /// as a circle with the diameter of the [Paint.strokeWidth], filled as |
| /// described by the [Paint] (ignoring [Paint.style]). |
| /// |
| /// Otherwise, each point is drawn as an axis-aligned square with sides of |
| /// length [Paint.strokeWidth], filled as described by the [Paint] (ignoring |
| /// [Paint.style]). |
| points, |
| |
| /// Draw each sequence of two points as a line segment. |
| /// |
| /// If the number of points is odd, then the last point is ignored. |
| /// |
| /// The lines are stroked as described by the [Paint] (ignoring |
| /// [Paint.style]). |
| lines, |
| |
| /// Draw the entire sequence of point as one line. |
| /// |
| /// The lines are stroked as described by the [Paint] (ignoring |
| /// [Paint.style]). |
| polygon, |
| } |
| |
| /// An interface for recording graphical operations. |
| /// |
| /// [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. |
| /// |
| /// Graphical operations that affect pixels entirely outside the given |
| /// cullRect might be discarded by the implementation. However, the |
| /// implementation might draw outside these bounds if, for example, a command |
| /// draws partially inside and outside the cullRect. To ensure that pixels |
| /// outside a given region are discarded, consider using a [clipRect]. |
| /// |
| /// To end the recording, call [PictureRecorder.endRecording] on the |
| /// given recorder. |
| Canvas(PictureRecorder recorder, Rect cullRect) { |
| if (recorder == null) |
| throw new ArgumentError('The given PictureRecorder was null.'); |
| if (recorder.isRecording) |
| throw new ArgumentError('The given PictureRecorder is already associated with another Canvas.'); |
| // TODO(ianh): throw if recorder is defunct (https://github.com/flutter/flutter/issues/2531) |
| _constructor(recorder, cullRect.left, cullRect.top, cullRect.right, cullRect.bottom); |
| } |
| void _constructor(PictureRecorder recorder, |
| double left, |
| double top, |
| double right, |
| double bottom) native "Canvas_constructor"; |
| |
| /// Saves a copy of the current transform and clip on the save stack. |
| /// |
| /// 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 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. |
| /// |
| /// 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._objects, paint._data); |
| } else { |
| _saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, |
| paint._objects, paint._data); |
| } |
| } |
| void _saveLayerWithoutBounds(List<dynamic> paintObjects, ByteData paintData) |
| native "Canvas_saveLayerWithoutBounds"; |
| // TODO(jackson): Paint should be optional, but making it optional causes crash |
| void _saveLayer(double left, |
| double top, |
| double right, |
| double bottom, |
| List<dynamic> paintObjects, |
| ByteData paintData) native "Canvas_saveLayer"; |
| |
| /// Pops the current save stack, if there is anything to pop. |
| /// 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 |
| /// initial state. This means it returns 1 for a clean canvas, and |
| /// that each call to [save] and [saveLayer] increments it, and that |
| /// each matching call to [restore] decrements it. |
| /// |
| /// 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."); |
| _transform(matrix4); |
| } |
| 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."); |
| _setMatrix(matrix4); |
| } |
| void _setMatrix(Float64List matrix4) native "Canvas_setMatrix"; |
| |
| /// 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); |
| } |
| void _clipRect(double left, |
| double top, |
| 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) => _clipRRect(rrect._value); |
| void _clipRRect(Float32List 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._objects, paint._data); |
| } |
| void _drawLine(double x1, |
| double y1, |
| double x2, |
| double y2, |
| List<dynamic> paintObjects, |
| ByteData paintData) 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) => _drawPaint(paint._objects, paint._data); |
| void _drawPaint(List<dynamic> paintObjects, ByteData paintData) 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._objects, paint._data); |
| } |
| void _drawRect(double left, |
| double top, |
| double right, |
| double bottom, |
| List<dynamic> paintObjects, |
| ByteData paintData) 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) { |
| _drawRRect(rrect._value, paint._objects, paint._data); |
| } |
| void _drawRRect(Float32List rrect, |
| List<dynamic> paintObjects, |
| ByteData paintData) 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) { |
| _drawDRRect(outer._value, inner._value, paint._objects, paint._data); |
| } |
| void _drawDRRect(Float32List outer, |
| Float32List inner, |
| List<dynamic> paintObjects, |
| ByteData paintData) 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._objects, paint._data); |
| } |
| void _drawOval(double left, |
| double top, |
| double right, |
| double bottom, |
| List<dynamic> paintObjects, |
| ByteData paintData) 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._objects, paint._data); |
| } |
| void _drawCircle(double x, |
| double y, |
| double radius, |
| List<dynamic> paintObjects, |
| ByteData paintData) 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) { |
| _drawPath(path, paint._objects, paint._data); |
| } |
| void _drawPath(Path path, |
| List<dynamic> paintObjects, |
| ByteData paintData) 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._objects, paint._data); |
| } |
| void _drawImage(Image image, |
| double x, |
| double y, |
| List<dynamic> paintObjects, |
| ByteData paintData) native "Canvas_drawImage"; |
| |
| /// 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. |
| /// |
| /// 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, |
| src.top, |
| src.right, |
| src.bottom, |
| dst.left, |
| dst.top, |
| dst.right, |
| dst.bottom, |
| paint._objects, |
| paint._data); |
| } |
| void _drawImageRect(Image image, |
| double srcLeft, |
| double srcTop, |
| double srcRight, |
| double srcBottom, |
| double dstLeft, |
| double dstTop, |
| double dstRight, |
| double dstBottom, |
| List<dynamic> paintObjects, |
| ByteData paintData) 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, |
| center.top, |
| center.right, |
| center.bottom, |
| dst.left, |
| dst.top, |
| dst.right, |
| dst.bottom, |
| paint._objects, |
| paint._data); |
| } |
| void _drawImageNine(Image image, |
| double centerLeft, |
| double centerTop, |
| double centerRight, |
| double centerBottom, |
| double dstLeft, |
| double dstTop, |
| double dstRight, |
| double dstBottom, |
| List<dynamic> paintObjects, |
| ByteData paintData) native "Canvas_drawImageNine"; |
| |
| /// Draw the given picture onto the canvas. To create a picture, see |
| /// [PictureRecorder]. |
| void drawPicture(Picture picture) native "Canvas_drawPicture"; |
| |
| /// Draws the text in the given paragraph into this canvas at the given offset. |
| /// |
| /// Valid only after [Paragraph.layout] has been called on the paragraph. |
| void drawParagraph(Paragraph paragraph, Offset offset) { |
| paragraph._paint(this, offset.dx, offset.dy); |
| } |
| |
| /// Draws a sequence of points according to the given [PointMode]. |
| void drawPoints(PointMode pointMode, List<Point> points, Paint paint) { |
| _drawPoints(paint._objects, paint._data, pointMode.index, _encodePointList(points)); |
| } |
| void _drawPoints(List<dynamic> paintObjects, |
| ByteData paintData, |
| int pointMode, |
| Float32List points) native "Canvas_drawPoints"; |
| |
| void drawVertices(VertexMode vertexMode, |
| List<Point> vertices, |
| List<Point> textureCoordinates, |
| List<Color> colors, |
| TransferMode transferMode, |
| List<int> indicies, |
| Paint paint) { |
| final int vertexCount = vertices.length; |
| |
| if (textureCoordinates.isNotEmpty && textureCoordinates.length != vertexCount) |
| throw new ArgumentError("[vertices] and [textureCoordinates] lengths must match"); |
| if (colors.isNotEmpty && colors.length != vertexCount) |
| throw new ArgumentError("[vertices] and [colors] lengths must match"); |
| |
| final Float32List vertexBuffer = _encodePointList(vertices); |
| final Float32List textureCoordinateBuffer = textureCoordinates.isEmpty ? null : _encodePointList(textureCoordinates); |
| final Int32List colorBuffer = colors.isEmpty ? null : _encodeColorList(colors); |
| final Int32List indexBuffer = new Int32List.fromList(indicies); |
| |
| _drawVertices( |
| paint._objects, paint._data, vertexMode.index, vertexBuffer, |
| textureCoordinateBuffer, colorBuffer, transferMode.index, indexBuffer |
| ); |
| } |
| void _drawVertices(List<dynamic> paintObjects, |
| ByteData paintData, |
| int vertexMode, |
| Float32List vertices, |
| Float32List textureCoordinates, |
| Int32List colors, |
| int transferMode, |
| Int32List indicies) native "Canvas_drawVertices"; |
| |
| // TODO(eseidel): Paint should be optional, but optional doesn't work. |
| void drawAtlas(Image atlas, |
| List<RSTransform> transforms, |
| List<Rect> rects, |
| List<Color> colors, |
| TransferMode transferMode, |
| Rect cullRect, |
| Paint paint) { |
| final int rectCount = rects.length; |
| |
| if (transforms.length != rectCount) |
| throw new ArgumentError("[transforms] and [rects] lengths must match"); |
| if (colors.isNotEmpty && colors.length != rectCount) |
| throw new ArgumentError("if supplied, [colors] length must match that of [transforms] and [rects]"); |
| |
| final Float32List rstTransformBuffer = new Float32List(rectCount * 4); |
| final Float32List rectBuffer = new Float32List(rectCount * 4); |
| |
| for (int i = 0; i < rectCount; ++i) { |
| final int index0 = i * 4; |
| final int index1 = index0 + 1; |
| final int index2 = index0 + 2; |
| final int index3 = index0 + 3; |
| final RSTransform rstTransform = transforms[i]; |
| final Rect rect = rects[i]; |
| rstTransformBuffer[index0] = rstTransform.scos; |
| rstTransformBuffer[index1] = rstTransform.ssin; |
| rstTransformBuffer[index2] = rstTransform.tx; |
| rstTransformBuffer[index3] = rstTransform.ty; |
| rectBuffer[index0] = rect.left; |
| rectBuffer[index1] = rect.top; |
| rectBuffer[index2] = rect.right; |
| rectBuffer[index3] = rect.bottom; |
| } |
| |
| final Int32List colorBuffer = colors.isEmpty ? null : _encodeColorList(colors); |
| final Float32List cullRectBuffer = cullRect?._value; |
| |
| _drawAtlas( |
| paint._objects, paint._data, atlas, rstTransformBuffer, rectBuffer, |
| colorBuffer, transferMode.index, cullRectBuffer |
| ); |
| } |
| void _drawAtlas(List<dynamic> paintObjects, |
| ByteData paintData, |
| Image atlas, |
| Float32List rstTransforms, |
| Float32List rects, |
| Int32List colors, |
| int transferMode, |
| Float32List cullRect) native "Canvas_drawAtlas"; |
| } |
| |
| /// An object representing a sequence of recorded graphical operations. |
| /// |
| /// To create a [Picture], use a [PictureRecorder]. |
| /// |
| /// A [Picture] can be placed in a [Scene] using a [SceneBuilder], via |
| /// the [SceneBuilder.addPicture] method. A [Picture] can also be |
| /// drawn into a [Canvas], using the [Canvas.drawPicture] method. |
| abstract class Picture extends NativeFieldWrapperClass2 { |
| /// Creates an uninitialized Picture object. |
| /// |
| /// Calling the Picture constructor directly will not create a useable |
| /// object. To create a Picture object, use a [PictureRecorder]. |
| Picture(); // (this constructor is here just so we can document it) |
| |
| /// Release the resources used by this object. The object is no longer usable |
| /// after this method is called. |
| void dispose() native "Picture_dispose"; |
| } |
| |
| /// Records a [Picture] containing a sequence of graphical operations. |
| /// |
| /// To begin recording, construct a [Canvas] to record the commands. |
| /// To end recording, use the [PictureRecorder.endRecording] method. |
| class PictureRecorder extends NativeFieldWrapperClass2 { |
| /// Creates a new idle PictureRecorder. To associate it with a |
| /// [Canvas] and begin recording, pass this [PictureRecorder] to the |
| /// [Canvas] constructor. |
| PictureRecorder() { _constructor(); } |
| void _constructor() native "PictureRecorder_constructor"; |
| |
| /// Whether this object is currently recording commands. |
| /// |
| /// Specifically, this returns true if a [Canvas] object has been |
| /// created to record commands and recording has not yet ended via a |
| /// call to [endRecording], and false if either this |
| /// [PictureRecorder] has not yet been associated with a [Canvas], |
| /// or the [endRecording] method has already been called. |
| bool get isRecording native "PictureRecorder_isRecording"; |
| |
| /// Finishes recording graphical operations. |
| /// |
| /// Returns a picture containing the graphical operations that have been |
| /// recorded thus far. After calling this function, both the picture recorder |
| /// and the canvas objects are invalid and cannot be used further. |
| /// |
| /// Returns null if the PictureRecorder is not associated with a canvas. |
| Picture endRecording() native "PictureRecorder_endRecording"; |
| } |