-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
204a54a
commit 043e9be
Showing
8 changed files
with
555 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export 'package:pxl/src/blend.dart'; | ||
export 'package:pxl/src/buffer.dart'; | ||
export 'package:pxl/src/format.dart'; | ||
export 'package:pxl/src/geometry.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,122 @@ | ||
abstract interface class BlendMode {} | ||
import 'dart:typed_data'; | ||
|
||
import 'package:meta/meta.dart'; | ||
import 'package:pxl/src/format.dart'; | ||
import 'package:pxl/src/internal.dart'; | ||
|
||
part 'blend/porter_duff.dart'; | ||
|
||
/// 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. A custom [BlendMode] can be used to implement | ||
/// custom blending algorithms, or one of the predefined [BlendMode]s can be | ||
/// used. | ||
/// | ||
/// Each algorithm takes two colors as input, the _source_ color, which is the | ||
/// color being drawn, and the _destination_ color, which is the color already | ||
/// on the canvas. The algorithm then returns a new color that is the result of | ||
/// blending the two colors. | ||
/// | ||
/// ## SIMD | ||
/// | ||
/// Some blend modes can optionally use [SIMD optimizations][] by setting the | ||
/// `pxl.SIMD` Dart compilation environment variable: | ||
/// | ||
/// [SIMD optimizations]: https://en.wikipedia.org/wiki/SIMD | ||
/// | ||
/// ```sh | ||
/// dart compile -Dpxl.SIMD=true | ||
/// ``` | ||
/// | ||
/// This will use the [Float32x4] class to perform the blending calculations, | ||
/// which can be faster than using scalar but should be tested for performance | ||
/// and correctness. | ||
/// | ||
/// See <https://dart.dev/guides/environment-declarations> for more details. | ||
@immutable | ||
abstract mixin class BlendMode { | ||
/// Destination pixels covered by the source are cleared to 0. | ||
static const BlendMode clear = PorterDuff( | ||
PorterDuff.zero, | ||
PorterDuff.zero, | ||
); | ||
|
||
/// Destination pixels are replaced by the source pixels. | ||
static const BlendMode src = PorterDuff( | ||
PorterDuff.one, | ||
PorterDuff.zero, | ||
); | ||
|
||
/// Source pixels are replaced by the destination pixels. | ||
static const BlendMode dst = PorterDuff( | ||
PorterDuff.zero, | ||
PorterDuff.one, | ||
); | ||
|
||
/// The source color is placed over the destination color. | ||
static const BlendMode srcOver = PorterDuff( | ||
PorterDuff.one, | ||
PorterDuff.oneMinusSrc, | ||
); | ||
|
||
/// The destination color is placed over the source color. | ||
static const BlendMode dstOver = PorterDuff( | ||
PorterDuff.oneMinusDst, | ||
PorterDuff.one, | ||
); | ||
|
||
/// The source that overlaps the destination replaces the destination. | ||
static const BlendMode srcIn = PorterDuff( | ||
PorterDuff.dst, | ||
PorterDuff.zero, | ||
); | ||
|
||
/// The destination that overlaps the source replaces the source. | ||
static const BlendMode dstIn = PorterDuff( | ||
PorterDuff.zero, | ||
PorterDuff.src, | ||
); | ||
|
||
/// The source that does not overlap the destination replaces the destination. | ||
static const BlendMode srcOut = PorterDuff( | ||
PorterDuff.oneMinusDst, | ||
PorterDuff.zero, | ||
); | ||
|
||
/// The destination that does not overlap the source replaces the source. | ||
static const BlendMode dstOut = PorterDuff( | ||
PorterDuff.zero, | ||
PorterDuff.oneMinusSrc, | ||
); | ||
|
||
/// The source that overlaps the destination is blended with the destination. | ||
static const BlendMode srcAtop = PorterDuff( | ||
PorterDuff.dst, | ||
PorterDuff.oneMinusSrc, | ||
); | ||
|
||
/// The destination that overlaps the source is blended with the source. | ||
static const BlendMode dstAtop = PorterDuff( | ||
PorterDuff.oneMinusDst, | ||
PorterDuff.src, | ||
); | ||
|
||
/// The non-overlapping regions of the source and destination are combined. | ||
static const BlendMode xor = PorterDuff( | ||
PorterDuff.oneMinusDst, | ||
PorterDuff.oneMinusSrc, | ||
); | ||
|
||
/// The source and destination regions are added together. | ||
static const BlendMode plus = PorterDuff( | ||
PorterDuff.one, | ||
PorterDuff.one, | ||
); | ||
|
||
/// Returns a function that blends two pixels together. | ||
T Function(S src, T dst) getBlend<S, T>( | ||
PixelFormat<S, void> srcFormat, | ||
PixelFormat<T, void> dstFormat, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
part of '../blend.dart'; | ||
|
||
/// A [BlendMode] that uses [Porter-Duff coefficients][1] to blend colors. | ||
/// | ||
/// [1]: https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending | ||
/// | ||
/// A custom [BlendMode] can be created by providing custom coefficients to the | ||
/// [PorterDuff] constructor: | ||
/// | ||
/// ```dart | ||
/// // Creates a custom blend mode that always returns zero. | ||
/// final customBlendMode = PorterDuff(PorterDuff.zero, PorterDuff.one); | ||
/// ``` | ||
final class PorterDuff with BlendMode { | ||
/// Always returns zero (`0.0`). | ||
static double zero(double srcAlpha, double dstAlpha) => 0; | ||
|
||
/// Always returns one (`1.0`). | ||
static double one(double srcAlpha, double dstAlpha) => 1; | ||
|
||
/// Returns the source alpha channel (`srcAlpha`). | ||
static double src(double srcAlpha, double dstAlpha) => srcAlpha; | ||
|
||
/// Returns the destination alpha channel (`dstAlpha`). | ||
static double dst(double srcAlpha, double dstAlpha) => dstAlpha; | ||
|
||
/// Returns one minus the source alpha channel (`1 - srcAlpha`). | ||
static double oneMinusSrc(double srcAlpha, double dstAlpha) => 1 - srcAlpha; | ||
|
||
/// Returns one minus the destination alpha channel (`1 - dstAlpha`). | ||
static double oneMinusDst(double srcAlpha, double dstAlpha) => 1 - dstAlpha; | ||
|
||
/// Creates a blend mode with [Porter-Duff coefficients][1]. | ||
/// | ||
/// [1]: https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending | ||
/// | ||
/// The [src] and [dst] functions are used to calculate the source and | ||
/// destination coefficients, respectively. The result of blending the source | ||
/// and destination colors is calculated as: | ||
/// | ||
/// ```dart | ||
/// final srcAlpha = src(src.w, dst.w); | ||
/// final dstAlpha = dst(src.w, dst.w); | ||
/// return src * srcAlpha + dst * dstAlpha; | ||
/// ``` | ||
const PorterDuff(this._src, this._dst); | ||
final double Function(double, double) _src; | ||
final double Function(double, double) _dst; | ||
|
||
@override | ||
T Function(S src, T dst) getBlend<S, T>( | ||
PixelFormat<S, void> srcFormat, | ||
PixelFormat<T, void> dstFormat, | ||
) { | ||
return (src, dst) { | ||
final srcRgba = srcFormat.toFloatRgba(src); | ||
final dstRgba = dstFormat.toFloatRgba(dst); | ||
final result = _blendFloatRgba(srcRgba, dstRgba); | ||
return dstFormat.fromFloatRgba(result); | ||
}; | ||
} | ||
|
||
Float32x4 _blendFloatRgba(Float32x4 src, Float32x4 dst) { | ||
return useSimd | ||
? _blendFloat32x4SIMD(src, dst) | ||
: _blendFloat32x4Scalar(src, dst); | ||
} | ||
|
||
Float32x4 _blendFloat32x4Scalar(Float32x4 src, Float32x4 dst) { | ||
final Float32x4( | ||
x: sr, | ||
y: sg, | ||
z: sb, | ||
w: sa, | ||
) = src; | ||
final Float32x4( | ||
x: dr, | ||
y: dg, | ||
z: db, | ||
w: da, | ||
) = dst; | ||
|
||
final r = _src(sa, da) * sr + _dst(sa, da) * dr; | ||
final g = _src(sa, da) * sg + _dst(sa, da) * dg; | ||
final b = _src(sa, da) * sb + _dst(sa, da) * db; | ||
final a = _src(sa, da) * sa + _dst(sa, da) * da; | ||
return Float32x4(r, g, b, a); | ||
} | ||
|
||
Float32x4 _blendFloat32x4SIMD(Float32x4 src, Float32x4 dst) { | ||
final srcA = Float32x4.splat(_src(src.w, dst.w)); | ||
final dstA = Float32x4.splat(_dst(src.w, dst.w)); | ||
return src * srcA + dst * dstA; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.