Skip to content

Commit

Permalink
Checkpoint.
Browse files Browse the repository at this point in the history
  • Loading branch information
matanlurey committed Aug 22, 2024
1 parent 3c1f996 commit 0f90ed1
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 18 deletions.
60 changes: 56 additions & 4 deletions lib/src/buffer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import 'package:pxl/src/format.dart';
import 'package:pxl/src/geometry.dart';
import 'package:pxl/src/internal.dart';

part 'buffer/blit_ops.dart';
part 'buffer/float.dart';
part 'buffer/int.dart';
part 'buffer/pixels.dart';
Expand Down Expand Up @@ -156,12 +155,65 @@ abstract mixin class Buffer<T> {
/// abgr8888.red, abgr8888.green, abgr8888.blue,
/// ]));
///
/// final clipped = buffer.getRegion(Rect.fromLTWH(1, 1, 2, 2));
/// final clipped = buffer.mapClipped(Rect.fromLTWH(1, 1, 2, 2));
/// print(clipped.data); // [0xFF00FF00, 0xFF0000FF]
/// ```
Buffer<T> getRegion(Rect bounds) {
Buffer<T> mapClipped(Rect bounds) {
return _ClippedBuffer(this, bounds.intersect(this.bounds));
}

/// Returns a lazy iterable of pixels in the buffer from [start] to [end].
///
/// The returned iterable will contain all pixels in the buffer that are
/// within the rectangle defined by [start] and [end], inclusive.
///
/// The provided positions are clamped to the bounds of the buffer, and yield
/// no pixels if `start > end`.
Iterable<T> getRange(Pos start, Pos end) {
final bottomRight = bounds.bottomRight;
start = start.clamp(Pos.zero, bottomRight);
end = end.clamp(Pos.zero, bottomRight);
if (Pos.byRowMajor(start, end) > 0) {
return const Iterable.empty();
}
return getRangeUnsafe(start, end);
}

/// Returns a lazy iterable of pixels in the buffer from [start] to [end].
///
/// The returned iterable will contain all pixels in the buffer that are
/// within the rectangle defined by [start] and [end], inclusive.
///
/// The provided positions must be `(0, 0) <= start <= end < (width, height)`
/// or the behavior is undefined.
Iterable<T> getRangeUnsafe(Pos start, Pos end) {
final iStart = start.y * width + start.x;
final iEnd = end.y * width + end.x;
return data.skip(iStart).take(iEnd - iStart + 1);
}

/// Returns a lazy iterable of pixels in the rectangle defined by [rect].
///
/// The returned iterable will contain all pixels in the buffer that are
/// within the rectangle defined by [rect].
///
/// The provided rectangle is clamped to the bounds of the buffer and yields
/// no pixels if the rectangle is empty.
Iterable<T> getRect(Rect rect) => getRectUnsafe(rect.intersect(bounds));

/// Returns a lazy iterable of pixels in the rectangle defined by [rect].
///
/// The returned iterable will contain all pixels in the buffer that are
/// within the rectangle defined by [rect].
///
/// The provided rectangle must be contained within the bounds of the buffer
/// or the behavior is undefined.
Iterable<T> getRectUnsafe(Rect rect) {
if (rect.width == width) {
return getRangeUnsafe(rect.topLeft, rect.bottomRight);
}
return rect.positions.map(getUnsafe);
}
}

abstract final class _Buffer<T> with Buffer<T> {
Expand Down Expand Up @@ -226,7 +278,7 @@ final class _ClippedBuffer<T> extends _Buffer<T> {
final Rect _bounds;

@override
Iterable<T> get data => _bounds.positions.map(getUnsafe);
Iterable<T> get data => _source.getRect(_bounds);

@override
T getUnsafe(Pos pos) => _source.getUnsafe(pos + _bounds.topLeft);
Expand Down
1 change: 0 additions & 1 deletion lib/src/buffer/blit_ops.dart

This file was deleted.

122 changes: 109 additions & 13 deletions lib/src/buffer/pixels.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ abstract final class Pixels<P> with Buffer<P> {
/// ```dart
/// final pixels = IntPixels(2, 2);
/// pixels.clear();
/// pixels.clear(Rect.fromLTWH(1, 0, 1, 2));
/// pixels.clear(target: Rect.fromLTWH(1, 0, 1, 2));
/// ```
void clear([Rect? target]) {
fill(format.zero, target);
void clear({Rect? target}) {
fill(format.zero, target: target);
}

/// Clears the buffer to the [PixelFormat.zero] value.
Expand All @@ -93,13 +93,14 @@ abstract final class Pixels<P> with Buffer<P> {
/// ```dart
/// final pixels = IntPixels(2, 2);
/// pixels.clearUnsafe();
/// pixels.clearUnsafe(Rect.fromLTWH(1, 0, 1, 2));
/// pixels.clearUnsafe(target: Rect.fromLTWH(1, 0, 1, 2));
/// ```
void clearUnsafe([Rect? target]) {
fillUnsafe(format.zero, target);
@unsafeNoBoundsChecks
void clearUnsafe({Rect? target}) {
fillUnsafe(format.zero, target: target);
}

/// Fill the buffer with the given pixel.
/// Fill the buffer with the given [pixel].
///
/// This is equivalent to calling [set] for every pixel in the buffer.
///
Expand All @@ -112,16 +113,16 @@ abstract final class Pixels<P> with Buffer<P> {
/// ```dart
/// final pixels = IntPixels(2, 2);
/// pixels.fill(0xFFFFFFFF);
/// pixels.fill(0x00000000, Rect.fromLTWH(1, 0, 1, 2));
/// pixels.fill(0x00000000, target: Rect.fromLTWH(1, 0, 1, 2));
/// ```
void fill(P pixel, [Rect? target]) {
void fill(P pixel, {Rect? target}) {
if (target != null) {
target = target.intersect(bounds);
}
return fillUnsafe(pixel, target);
return fillUnsafe(pixel, target: target);
}

/// Fill the buffer with the given pixel **without bounds checking**.
/// Fill the buffer with the given [pixel].
///
/// This is equivalent to calling [setUnsafe] for every pixel in the buffer.
///
Expand All @@ -134,9 +135,10 @@ abstract final class Pixels<P> with Buffer<P> {
/// ```dart
/// final pixels = IntPixels(2, 2);
/// pixels.fillUnsafe(0xFFFFFFFF);
/// pixels.fillUnsafe(0x00000000, Rect.fromLTWH(1, 0, 1, 2));
/// pixels.fillUnsafe(0x00000000, target: Rect.fromLTWH(1, 0, 1, 2));
/// ```
void fillUnsafe(P pixel, [Rect? target]) {
@unsafeNoBoundsChecks
void fillUnsafe(P pixel, {Rect? target}) {
if (target == null) {
return data.fillRange(
0,
Expand All @@ -160,4 +162,98 @@ abstract final class Pixels<P> with Buffer<P> {
);
}
}

/// Fill the buffer with the given [pixels].
///
/// If a [target] rectangle is provided, only the pixels within that rectangle
/// are filled, and the rectangle will be clipped to the bounds of the buffer.
///
/// If the number of pixels in the iterable is less than the number of pixels
/// in the target rectangle, the remaining pixels will be filled with the zero
/// value of the format. If the number of pixels is greater, the extra pixels
/// will be ignored.
///
/// ## Example
///
/// ```dart
/// final pixels = IntPixels(2, 2);
/// pixels.fillWith([0xFFFFFFFF, 0x00000000]);
/// pixels.fillWith([0x00000000, 0xFFFFFFFF], target: Rect.fromLTWH(1, 0, 1, 2));
/// ```
void fillWith(
Iterable<P> pixels, {
Rect? target,
}) {
if (target == null) {
target = bounds;
} else {
target = target.intersect(bounds);
}
if (pixels.length < target.area) {
pixels = pixels.followedBy(
Iterable.generate(
target.area - pixels.length,
(_) => format.zero,
),
);
}
return fillWithUnsafe(pixels, target: target);
}

/// Fill the buffer with the given [pixels].
///
/// If a [target] rectangle is provided, only the pixels within that rectangle
/// are filled. If the rectangle is outside the bounds of the buffer, the
/// behavior is undefined.
///
/// If the number of pixels in the iterable is less than the number of pixels
/// in the target rectangle, or if the number of pixels is greater, the
/// behavior is undefined.
///
/// ## Example
///
/// ```dart
/// final pixels = IntPixels(2, 2);
/// pixels.fillWithUnsafe([0xFFFFFFFF, 0x00000000]);
/// pixels.fillWithUnsafe([0x00000000, 0xFFFFFFFF], target: Rect.fromLTWH(1, 0, 1, 2));
/// ```
@unsafeNoBoundsChecks
void fillWithUnsafe(
Iterable<P> pixels, {
Rect? target,
}) {
if (target == null) {
return data.setAll(0, pixels);
}
var skip = 0;
for (var y = target.top; y < target.bottom; y++) {
final x = y * width;
data.setRange(
x + target.left,
x + target.right,
pixels.skip(skip),
);
skip += target.width;
}
}

@override
Iterable<P> getRangeUnsafe(Pos start, Pos end) {
final s = _indexAtUnsafe(start);
final e = _indexAtUnsafe(end);
return data.getRange(s, e + 1);
}

@override
Iterable<P> getRectUnsafe(Rect rect) {
if (rect.width == width) {
return getRangeUnsafe(rect.topLeft, rect.bottomRight);
}

// TODO: Consider creating a custom iterator instead.
return Iterable.generate(rect.height, (y) {
final x = y * width;
return data.getRange(x + rect.left, x + rect.right + 1);
}).expand((e) => e);
}
}

0 comments on commit 0f90ed1

Please sign in to comment.