-
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.
Yet another rewrite with much cleaner SIMD blends.
- Loading branch information
1 parent
680e9e1
commit 5e3e9bf
Showing
31 changed files
with
1,289 additions
and
2,923 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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,194 +1,16 @@ | ||
import 'dart:math' as math; | ||
import 'dart:typed_data'; | ||
|
||
import 'package:lodim/lodim.dart'; | ||
import 'package:meta/meta.dart'; | ||
import 'package:pxl/src/blend.dart'; | ||
import 'package:pxl/src/color.dart'; | ||
|
||
part 'buffer/clipped.dart'; | ||
part 'buffer/empty.dart'; | ||
part 'buffer/mapped.dart'; | ||
part 'buffer/pixels.dart'; | ||
part 'buffer/rgba.dart'; | ||
part 'buffer/scaled.dart'; | ||
part 'buffer/vec4.dart'; | ||
|
||
/// Returns `height` if it is not `null`, otherwise calculates the height. | ||
/// | ||
/// [length], [width], and [height] is checked to be non-negative and valid. | ||
int _checkAndInferHeight({ | ||
required int length, | ||
required int width, | ||
int? height, | ||
}) { | ||
RangeError.checkNotNegative(width, 'width'); | ||
if (height == null) { | ||
height = width != 0 ? length ~/ width : 0; | ||
} else { | ||
RangeError.checkNotNegative(height, 'height'); | ||
} | ||
if (length != width * height) { | ||
throw ArgumentError( | ||
'The length of the pixel data must be equal to the product of the ' | ||
'width and height.', | ||
); | ||
} | ||
return height; | ||
} | ||
|
||
/// A 2-dimensional view of a pixel-buffer like object with a fixed [width] and | ||
/// [height]. | ||
/// | ||
/// Buffer is analgous to [Iterable] but for 2-dimensional pixel data, and | ||
/// typically exists in order to provide a common interface for reading | ||
/// pixel data from different sources, or providing a common interface for | ||
/// compatibility. | ||
/// | ||
/// See [Pixels] for a mutable concrete implementation of this interface. | ||
abstract base mixin class Buffer<T extends Color> { | ||
/// Creates an empty buffer with zero width and height. | ||
/// | ||
/// This buffer is immutable and has no pixels. | ||
const factory Buffer.empty() = _EmptyBuffer<T>; | ||
|
||
/// The width of the buffer, in pixels. | ||
/// TODO: Implement Buffer. | ||
abstract final class Buffer<T extends Color> { | ||
/// TODO: Replace stub. | ||
int get width; | ||
|
||
/// The height of the buffer, in pixels. | ||
/// TODO: Replace stub. | ||
int get height; | ||
|
||
/// The total number of pixels in the buffer. | ||
/// TODO: Replace stub. | ||
int get length; | ||
|
||
/// Returns the pixel at [index] in the buffer without bound checking. | ||
/// | ||
/// ## Safety | ||
/// | ||
/// {@template pxl:unsafe} | ||
/// This method is intended for use in performance-sensitive code where the | ||
/// parameters are known to be within bounds, otherwise it may result in | ||
/// undefined behavior, including memory corruption and data loss. | ||
/// {@endtemplate} | ||
/// | ||
/// Prefer [operator []] for most operations. | ||
/// TODO: Replace stub. | ||
T uncheckedGet(int index); | ||
|
||
/// Returns the pixel at [index] in the buffer. | ||
/// | ||
/// The [index] must be in the range `0 ≤ index < length`. | ||
@pragma('dart2js:tryInline') | ||
@pragma('vm:prefer-inline') | ||
T operator [](int index) { | ||
RangeError.checkValueInInterval(index, 0, length - 1, 'index'); | ||
return uncheckedGet(index); | ||
} | ||
|
||
/// Returns the index of the pixel at the given [position] in the buffer. | ||
/// | ||
/// ## Safety | ||
/// | ||
/// {@macro pxl:unsafe} | ||
/// | ||
/// Prefer using [indexAt] for most operations. | ||
@pragma('dart2js:tryInline') | ||
@pragma('vm:prefer-inline') | ||
int uncheckedIndexAt(Pos position) { | ||
return position.y * width + position.x; | ||
} | ||
|
||
/// Returns the index of the pixel at the given [position] in the buffer. | ||
/// | ||
/// The [position] must be within the bounds of the buffer. | ||
@pragma('dart2js:tryInline') | ||
@pragma('vm:prefer-inline') | ||
@nonVirtual | ||
int indexAt(Pos position) { | ||
if (!toBoundsRect().contains(position)) { | ||
throw RangeError('position is out of bounds: $position'); | ||
} | ||
return uncheckedIndexAt(position); | ||
} | ||
|
||
/// Returns a view of the buffer clipped to the given [bounds]. | ||
/// | ||
/// If the buffer is entirely out of bounds, an empty buffer is returned. | ||
/// | ||
/// This is guaranteed to be a constant-time operation. | ||
Buffer<T> clip(Rect bounds) => _ClippedBuffer(this, bounds); | ||
|
||
/// Returns a view of the buffer scaled by the given factors. | ||
/// | ||
/// Both [widthBy] and [heightBy] must be positive integers. | ||
Buffer<T> scale({int widthBy = 1, int heightBy = 1}) { | ||
if (widthBy < 1) { | ||
throw ArgumentError.value( | ||
widthBy, | ||
'widthBy', | ||
'must be greater than 0', | ||
); | ||
} | ||
if (heightBy < 1) { | ||
throw ArgumentError.value( | ||
heightBy, | ||
'heightBy', | ||
'must be greater than 0', | ||
); | ||
} | ||
return _ScaledBuffer(this, widthBy, heightBy); | ||
} | ||
|
||
/// Returns a view of the buffer mapped to the given function. | ||
/// | ||
/// The [mapper] function is called for each pixel in the buffer. | ||
/// | ||
/// The returned buffer has the same dimensions as the original buffer. | ||
Buffer<R> map<R extends Color>(R Function(T color) mapper) { | ||
return _MappedBuffer<T, R>(this, mapper); | ||
} | ||
|
||
/// Returns the bounds of the buffer. | ||
/// | ||
/// If [clip] is provided, the bounds are clipped to the given rectangle. | ||
/// | ||
/// If the buffer is entirely out of bounds, an empty rectangle is returned. | ||
@pragma('dart2js:tryInline') | ||
@pragma('vm:prefer-inline') | ||
@nonVirtual | ||
Rect toBoundsRect([Rect? clip]) { | ||
final bounds = Rect.fromLTWH(0, 0, width, height); | ||
return clip == null ? bounds : bounds.intersect(clip); | ||
} | ||
|
||
/// Converts and returns the buffer as a list of pixels in RGBA format. | ||
/// | ||
/// The returned list is a copy of the buffer and can be modified without | ||
/// affecting the original. | ||
@pragma('dart2js:tryInline') | ||
@pragma('vm:prefer-inline') | ||
@pragma('dart2js:index-bounds:trust') | ||
@pragma('vm:unsafe:no-bounds-checks') | ||
Uint32List toRgba8888List() { | ||
final list = Uint32List(length); | ||
for (var i = 0; i < length; i++) { | ||
list[i] = this.uncheckedGet(i).rgba.value; | ||
} | ||
return list; | ||
} | ||
|
||
/// Converts and returns the buffer as a list of pixels in Vec4 format. | ||
/// | ||
/// The returned list is a copy of the buffer and can be modified without | ||
/// affecting the original. | ||
@pragma('dart2js:tryInline') | ||
@pragma('vm:prefer-inline') | ||
@pragma('dart2js:index-bounds:trust') | ||
@pragma('vm:unsafe:no-bounds-checks') | ||
Float32x4List toVec4List() { | ||
final list = Float32x4List(length); | ||
for (var i = 0; i < length; i++) { | ||
list[i] = this.uncheckedGet(i).vec4.value; | ||
} | ||
return list; | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.