Skip to content

Commit

Permalink
feat: Implement alpha support (behind feature flag)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernauer committed Jun 10, 2023
1 parent 2dd7e7d commit 369f0e0
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 18 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ rstest = "0.17"
[features]
default = ["vnc"]
vnc = ["dep:vncserver"]
alpha = []

[lib]
name = "breakwater"
Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ You can get the list of available features by looking at the [Cargo.toml](Cargo.
As of writing the following features are supported

* `vnc` (enabled by default): Starts a VNC server, where users can connect to. Needs `libvncserver-dev` to be installed. Please note that the VNC server offers basically no latency, but consumes quite some CPU.
* `alpha` (disabled by default): Respect alpha values during `PX` commands. Disabled by default as this can cause performance degradation.

To e.g. turn the VNS server off, build with

```bash
cargo run --release --no-default-features # --features feature-to-enable
cargo run --release --no-default-features # --features alpha,vnc to explicitly enable
```

# Run in docker container
Expand Down Expand Up @@ -153,6 +154,3 @@ The servers were connected with two 40G and one 10G links, through which traffic
|-------------------------------------------------------|----------|---------------------|
| [Shoreline](https://github.com/TobleMiner/shoreline) | C | 34 Gbit/s |
| [Breakwater](https://github.com/sbernauer/breakwater) | Rust | 52 Gbit/s |

# TODOs
* Implement Alpha channel feature. For performance reasons there should be a compile-time switch for it
5 changes: 5 additions & 0 deletions src/framebuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ impl FrameBuffer {
}
}

#[inline(always)]
pub fn get_unchecked(&self, x: usize, y: usize) -> u32 {
unsafe { (*self.buffer.get())[x + y * self.width] }
}

#[inline(always)]
pub fn set(&self, x: usize, y: usize, rgba: u32) {
// TODO: If we make the FrameBuffer large enough (e.g. 10_000 x 10_000) we don't need to check the bounds here (x and y are max 4 digit numbers).
Expand Down
15 changes: 10 additions & 5 deletions src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,16 @@ mod test {
#[case("PX 0 42 abcdef\nPX 0 42\n", "PX 0 42 abcdef\n")]
#[case("PX 42 0 abcdef\nPX 42 0\n", "PX 42 0 abcdef\n")]
// With alpha
// TODO: At the moment alpha channel is not supported and silently ignored (pixels are painted with 0% transparency)
#[case("PX 0 0 ffffffaa\nPX 0 0\n", "PX 0 0 ffffff\n")]
#[case("PX 0 0 abcdefaa\nPX 0 0\n", "PX 0 0 abcdef\n")]
#[case("PX 0 1 abcdefaa\nPX 0 1\n", "PX 0 1 abcdef\n")]
#[case("PX 1 0 abcdefaa\nPX 1 0\n", "PX 1 0 abcdef\n")]
#[case("PX 0 0 ffffff00\nPX 0 0\n", if cfg!(feature = "alpha") {"PX 0 0 000000\n"} else {"PX 0 0 ffffff\n"})]
#[case("PX 0 0 ffffffff\nPX 0 0\n", "PX 0 0 ffffff\n")]
#[case("PX 0 1 abcdef00\nPX 0 1\n", if cfg!(feature = "alpha") {"PX 0 1 000000\n"} else {"PX 0 1 abcdef\n"})]
#[case("PX 1 0 abcdefff\nPX 1 0\n", "PX 1 0 abcdef\n")]
#[case("PX 0 0 ffffff88\nPX 0 0\n", if cfg!(feature = "alpha") {"PX 0 0 888888\n"} else {"PX 0 0 ffffff\n"})]
#[case("PX 0 0 ffffff11\nPX 0 0\n", if cfg!(feature = "alpha") {"PX 0 0 111111\n"} else {"PX 0 0 ffffff\n"})]
#[case("PX 0 0 abcdef80\nPX 0 0\n", if cfg!(feature = "alpha") {"PX 0 0 556677\n"} else {"PX 0 0 abcdef\n"})]
// 0xab = 171, 0x88 = 136
// (171 * 136) / 255 = 91 = 0x5b
#[case("PX 0 0 abcdef88\nPX 0 0\n", if cfg!(feature = "alpha") {"PX 0 0 5b6d7f\n"} else {"PX 0 0 abcdef\n"})]
// Short commands
#[case("PX 0 0 00\nPX 0 0\n", "PX 0 0 000000\n")]
#[case("PX 0 0 ff\nPX 0 0\n", "PX 0 0 ffffff\n")]
Expand Down
45 changes: 36 additions & 9 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Pixelflut server powered by breakwater https://github.com/sbernauer/breakwater
Available commands:
HELP: Show this help
PX x y rrggbb: Color the pixel (x,y) with the given hexadecimal color rrggbb
PX x y rrggbbaa: Color the pixel (x,y) with the given hexadecimal color rrggbb (alpha channel is ignored for now)
PX x y rrggbbaa: Color the pixel (x,y) with the given hexadecimal color rrggbb (alpha channel is ignored by default, unless breakwater is compiled with \"--feature alpha\". This is for performance reasons)
PX x y gg: Color the pixel (x,y) with the hexadecimal color gggggg. Basically this is the same as the other commands, but is a more efficient way of filling white, black or gray areas
PX x y: Get the color value of the pixel (x,y)
SIZE: Get the size of the drawing surface, e.g. `SIZE 1920 1080`
Expand Down Expand Up @@ -148,13 +148,11 @@ pub async fn parse_pixelflut_commands(
| (ASCII_HEXADECIMAL_VALUES[buffer[i - 6] as usize] as u32);

fb.set(x, y, rgba);
if cfg!(feature = "count_pixels") {
// statistics.inc_pixels(ip);
}
continue;
}

// ... or must be followed by 8 bytes RGBA and newline
#[cfg(not(feature = "alpha"))]
if buffer[i + 8] == b'\n' {
last_byte_parsed = i + 8;
i += 9; // We can advance one byte more than normal as we use continue and therefore not get incremented at the end of the loop
Expand All @@ -172,10 +170,42 @@ pub async fn parse_pixelflut_commands(
| (ASCII_HEXADECIMAL_VALUES[buffer[i - 8] as usize] as u32);

fb.set(x, y, rgba);
if cfg!(feature = "count_pixels") {
// statistics.inc_pixels(ip);

continue;
}
#[cfg(feature = "alpha")]
if buffer[i + 8] == b'\n' {
last_byte_parsed = i + 8;
i += 9; // We can advance one byte more than normal as we use continue and therefore not get incremented at the end of the loop

let alpha =
(ASCII_HEXADECIMAL_VALUES[buffer[i - 3] as usize] as u32) << 4
| (ASCII_HEXADECIMAL_VALUES[buffer[i - 2] as usize] as u32);

if alpha == 0 || x >= fb.get_width() || y >= fb.get_height() {
continue;
}

let alpha_comp = 0xff - alpha;
let current = fb.get_unchecked(x, y);
let r = (ASCII_HEXADECIMAL_VALUES[buffer[i - 5] as usize] as u32)
<< 4
| (ASCII_HEXADECIMAL_VALUES[buffer[i - 4] as usize] as u32);
let g = (ASCII_HEXADECIMAL_VALUES[buffer[i - 7] as usize] as u32)
<< 4
| (ASCII_HEXADECIMAL_VALUES[buffer[i - 6] as usize] as u32);
let b = (ASCII_HEXADECIMAL_VALUES[buffer[i - 9] as usize] as u32)
<< 4
| (ASCII_HEXADECIMAL_VALUES[buffer[i - 8] as usize] as u32);

let r: u32 =
(((current >> 24) & 0xff) * alpha_comp + r * alpha) / 0xff;
let g: u32 =
(((current >> 16) & 0xff) * alpha_comp + g * alpha) / 0xff;
let b: u32 =
(((current >> 8) & 0xff) * alpha_comp + b * alpha) / 0xff;

fb.set(x, y, r << 16 | g << 8 | b);
continue;
}

Expand All @@ -197,9 +227,6 @@ pub async fn parse_pixelflut_commands(
| (ASCII_HEXADECIMAL_VALUES[buffer[i - 2] as usize] as u32);

fb.set(x, y, rgba);
if cfg!(feature = "count_pixels") {
// statistics.inc_pixels(ip);
}

continue;
}
Expand Down

0 comments on commit 369f0e0

Please sign in to comment.