merge BitmapLinear-Commands
Some checks failed
Rust / build (pull_request) Failing after 1m5s

This commit is contained in:
Vinzenz Schroeter 2025-03-08 00:39:08 +01:00
parent d195f6100a
commit 427dd93088
6 changed files with 128 additions and 278 deletions

View file

@ -1,14 +1,31 @@
use crate::compression::into_compressed;
use crate::{
commands::TryFromPacketError, command_code::CommandCode,
command_code::CommandCode, commands::TryFromPacketError,
compression::into_decompressed, BitVec, CompressionCode, Header, Offset,
Packet, TypedCommand,
};
/// Binary operations for use with the [BitmapLinear] command.
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub enum BinaryOperation {
#[default]
Overwrite,
And,
Or,
Xor,
}
/// Set pixel data starting at the pixel offset on screen.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The [BinaryOperation] will be applied on the display comparing old and sent bit.
///
/// `new_bit = old_bit op sent_bit`
///
/// For example, [BinaryOperation::Or] can be used to turn on some pixels without affecting other pixels.
///
/// The contained [BitVec] is always uncompressed.
#[derive(Clone, PartialEq, Debug)]
pub struct BitmapLinear {
@ -16,18 +33,34 @@ pub struct BitmapLinear {
pub offset: Offset,
/// the pixels to send to the display as one long row
pub bitvec: BitVec,
/// The operation to apply on the display per bit comparing old and new state.
pub operation: BinaryOperation,
/// how to compress the command when converting to packet
pub compression: CompressionCode,
}
impl From<BitmapLinear> for Packet {
fn from(bitmap: BitmapLinear) -> Self {
Packet::bitmap_linear_into_packet(
CommandCode::BitmapLinear,
bitmap.offset,
bitmap.compression,
bitmap.bitvec.into(),
)
fn from(command: BitmapLinear) -> Self {
let command_code = match command.operation {
BinaryOperation::Overwrite => CommandCode::BitmapLinear,
BinaryOperation::And => CommandCode::BitmapLinearAnd,
BinaryOperation::Or => CommandCode::BitmapLinearOr,
BinaryOperation::Xor => CommandCode::BitmapLinearXor,
};
let payload: Vec<_> = command.bitvec.into();
let length = payload.len() as u16;
let payload = into_compressed(command.compression, payload);
Packet {
header: Header {
command_code: command_code.into(),
a: command.offset as u16,
b: length,
c: command.compression.into(),
d: 0,
},
payload,
}
}
}
@ -35,30 +68,10 @@ impl TryFrom<Packet> for BitmapLinear {
type Error = TryFromPacketError;
fn try_from(packet: Packet) -> Result<Self, Self::Error> {
let (offset, bitvec, compression) =
Self::packet_into_linear_bitmap(packet)?;
Ok(Self {
offset,
bitvec,
compression,
})
}
}
impl From<BitmapLinear> for TypedCommand {
fn from(command: BitmapLinear) -> Self {
Self::BitmapLinear(command)
}
}
impl BitmapLinear {
/// Helper method for Packets into `BitmapLinear*`-Commands
pub(crate) fn packet_into_linear_bitmap(
packet: Packet,
) -> Result<(Offset, BitVec, CompressionCode), TryFromPacketError> {
let Packet {
header:
Header {
command_code,
a: offset,
b: length,
c: sub,
@ -67,16 +80,30 @@ impl BitmapLinear {
},
payload,
} = packet;
let command_code = CommandCode::try_from(command_code)
.map_err(|_| TryFromPacketError::InvalidCommand(command_code))?;
let operation = match command_code {
CommandCode::BitmapLinear => BinaryOperation::Overwrite,
CommandCode::BitmapLinearAnd => BinaryOperation::And,
CommandCode::BitmapLinearOr => BinaryOperation::Or,
CommandCode::BitmapLinearXor => BinaryOperation::Xor,
_ => {
return Err(TryFromPacketError::InvalidCommand(
command_code.into(),
))
}
};
if reserved != 0 {
return Err(TryFromPacketError::ExtraneousHeaderValues);
}
let sub = match CompressionCode::try_from(sub) {
let compression = match CompressionCode::try_from(sub) {
Err(()) => {
return Err(TryFromPacketError::InvalidCompressionCode(sub));
}
Ok(value) => value,
};
let payload = match into_decompressed(sub, payload) {
let payload = match into_decompressed(compression, payload) {
None => return Err(TryFromPacketError::DecompressionFailed),
Some(value) => value,
};
@ -86,6 +113,17 @@ impl BitmapLinear {
payload.len(),
));
}
Ok((offset as Offset, BitVec::from_vec(payload), sub))
Ok(Self {
offset: offset as Offset,
bitvec: BitVec::from_vec(payload),
compression,
operation,
})
}
}
impl From<BitmapLinear> for TypedCommand {
fn from(command: BitmapLinear) -> Self {
Self::BitmapLinear(command)
}
}

View file

@ -1,52 +0,0 @@
use crate::{
commands::{BitmapLinear, TryFromPacketError},
command_code::CommandCode,
BitVec, CompressionCode, Offset, Packet, TypedCommand,
};
/// Set pixel data according to an and-mask starting at the offset.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained [BitVec] is always uncompressed.
#[derive(Clone, PartialEq, Debug)]
pub struct BitmapLinearAnd {
/// where to start overwriting pixel data
pub offset: Offset,
/// the pixels to send to the display as one long row
pub bitvec: BitVec,
/// how to compress the command when converting to packet
pub compression: CompressionCode,
}
impl TryFrom<Packet> for BitmapLinearAnd {
type Error = TryFromPacketError;
fn try_from(packet: Packet) -> Result<Self, Self::Error> {
let (offset, bitvec, compression) =
BitmapLinear::packet_into_linear_bitmap(packet)?;
Ok(Self {
offset,
bitvec,
compression,
})
}
}
impl From<BitmapLinearAnd> for Packet {
fn from(bitmap: BitmapLinearAnd) -> Self {
Packet::bitmap_linear_into_packet(
CommandCode::BitmapLinearAnd,
bitmap.offset,
bitmap.compression,
bitmap.bitvec.into(),
)
}
}
impl From<BitmapLinearAnd> for TypedCommand {
fn from(command: BitmapLinearAnd) -> Self {
Self::BitmapLinearAnd(command)
}
}

View file

@ -1,52 +0,0 @@
use crate::{
commands::{BitmapLinear, TryFromPacketError},
command_code::CommandCode,
BitVec, CompressionCode, Offset, Packet, TypedCommand,
};
/// Set pixel data according to an or-mask starting at the offset.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained [BitVec] is always uncompressed.
#[derive(Clone, PartialEq, Debug)]
pub struct BitmapLinearOr {
/// where to start overwriting pixel data
pub offset: Offset,
/// the pixels to send to the display as one long row
pub bitvec: BitVec,
/// how to compress the command when converting to packet
pub compression: CompressionCode,
}
impl TryFrom<Packet> for BitmapLinearOr {
type Error = TryFromPacketError;
fn try_from(packet: Packet) -> Result<Self, Self::Error> {
let (offset, bitvec, compression) =
BitmapLinear::packet_into_linear_bitmap(packet)?;
Ok(Self {
offset,
bitvec,
compression,
})
}
}
impl From<BitmapLinearOr> for Packet {
fn from(bitmap: BitmapLinearOr) -> Self {
Packet::bitmap_linear_into_packet(
CommandCode::BitmapLinearOr,
bitmap.offset,
bitmap.compression,
bitmap.bitvec.into(),
)
}
}
impl From<BitmapLinearOr> for TypedCommand {
fn from(command: BitmapLinearOr) -> Self {
Self::BitmapLinearOr(command)
}
}

View file

@ -1,52 +0,0 @@
use crate::{
commands::{BitmapLinear, TryFromPacketError},
command_code::CommandCode,
BitVec, CompressionCode, Offset, Packet, TypedCommand,
};
/// Set pixel data according to a xor-mask starting at the offset.
///
/// The screen will continuously overwrite more pixel data without regarding the offset, meaning
/// once the starting row is full, overwriting will continue on column 0.
///
/// The contained [BitVec] is always uncompressed.
#[derive(Clone, PartialEq, Debug)]
pub struct BitmapLinearXor {
/// where to start overwriting pixel data
pub offset: Offset,
/// the pixels to send to the display as one long row
pub bitvec: BitVec,
/// how to compress the command when converting to packet
pub compression: CompressionCode,
}
impl TryFrom<Packet> for BitmapLinearXor {
type Error = TryFromPacketError;
fn try_from(packet: Packet) -> Result<Self, Self::Error> {
let (offset, bitvec, compression) =
BitmapLinear::packet_into_linear_bitmap(packet)?;
Ok(Self {
offset,
bitvec,
compression,
})
}
}
impl From<BitmapLinearXor> for Packet {
fn from(bitmap: BitmapLinearXor) -> Self {
Packet::bitmap_linear_into_packet(
CommandCode::BitmapLinearXor,
bitmap.offset,
bitmap.compression,
bitmap.bitvec.into(),
)
}
}
impl From<BitmapLinearXor> for TypedCommand {
fn from(command: BitmapLinearXor) -> Self {
Self::BitmapLinearXor(command)
}
}

View file

@ -49,10 +49,7 @@
mod bitmap_legacy;
mod bitmap_linear;
mod bitmap_linear_and;
mod bitmap_linear_or;
mod bitmap_linear_win;
mod bitmap_linear_xor;
mod char_brightness;
mod clear;
mod cp437_data;
@ -67,10 +64,7 @@ use std::fmt::Debug;
pub use bitmap_legacy::*;
pub use bitmap_linear::*;
pub use bitmap_linear_and::*;
pub use bitmap_linear_or::*;
pub use bitmap_linear_win::*;
pub use bitmap_linear_xor::*;
pub use char_brightness::*;
pub use clear::*;
pub use cp437_data::*;
@ -105,12 +99,6 @@ pub enum TypedCommand {
BitmapLinear(BitmapLinear),
BitmapLinearAnd(BitmapLinearAnd),
BitmapLinearOr(BitmapLinearOr),
BitmapLinearXor(BitmapLinearXor),
HardReset(HardReset),
FadeOut(FadeOut),
@ -194,18 +182,12 @@ impl TryFrom<Packet> for TypedCommand {
CommandCode::BitmapLegacy => {
packet_to_command_case!(BitmapLegacy, packet)
}
CommandCode::BitmapLinear => {
CommandCode::BitmapLinear
| CommandCode::BitmapLinearOr
| CommandCode::BitmapLinearAnd
| CommandCode::BitmapLinearXor => {
packet_to_command_case!(BitmapLinear, packet)
}
CommandCode::BitmapLinearAnd => {
packet_to_command_case!(BitmapLinearAnd, packet)
}
CommandCode::BitmapLinearOr => {
packet_to_command_case!(BitmapLinearOr, packet)
}
CommandCode::BitmapLinearXor => {
packet_to_command_case!(BitmapLinearXor, packet)
}
CommandCode::BitmapLinearWinUncompressed => {
packet_to_command_case!(BitmapLinearWin, packet)
}
@ -239,9 +221,6 @@ impl From<TypedCommand> for Packet {
TypedCommand::GlobalBrightness(c) => c.into(),
TypedCommand::CharBrightness(c) => c.into(),
TypedCommand::BitmapLinear(c) => c.into(),
TypedCommand::BitmapLinearAnd(c) => c.into(),
TypedCommand::BitmapLinearOr(c) => c.into(),
TypedCommand::BitmapLinearXor(c) => c.into(),
TypedCommand::HardReset(c) => c.into(),
TypedCommand::FadeOut(c) => c.into(),
#[allow(deprecated)]
@ -250,20 +229,25 @@ impl From<TypedCommand> for Packet {
}
}
pub(self) fn check_command_code_only(packet: Packet, code: CommandCode) -> Option<TryFromPacketError> {
pub(self) fn check_command_code_only(
packet: Packet,
code: CommandCode,
) -> Option<TryFromPacketError> {
let Packet {
header:
Header {
command_code: _,
a,
b,
c,
d,
},
Header {
command_code: _,
a,
b,
c,
d,
},
payload,
} = packet;
if packet.header.command_code != u16::from(code) {
Some(TryFromPacketError::InvalidCommand(packet.header.command_code))
Some(TryFromPacketError::InvalidCommand(
packet.header.command_code,
))
} else if !payload.is_empty() {
Some(TryFromPacketError::UnexpectedPayloadSize(0, payload.len()))
} else if a != 0 || b != 0 || c != 0 || d != 0 {
@ -275,8 +259,8 @@ pub(self) fn check_command_code_only(packet: Packet, code: CommandCode) -> Optio
#[cfg(test)]
mod tests {
use crate::commands::{BitmapLinear, BitmapLinearWin, BitmapLinearXor, CharBrightness, GlobalBrightness, TryFromPacketError};
use crate::command_code::CommandCode;
use crate::commands::{BinaryOperation, TryFromPacketError};
use crate::*;
fn round_trip(original: TypedCommand) {
@ -319,9 +303,11 @@ mod tests {
#[test]
fn round_trip_brightness() {
round_trip(TypedCommand::GlobalBrightness(GlobalBrightness {
brightness: Brightness::try_from(6).unwrap(),
}));
round_trip(TypedCommand::GlobalBrightness(
commands::GlobalBrightness {
brightness: Brightness::try_from(6).unwrap(),
},
));
}
#[test]
@ -332,7 +318,7 @@ mod tests {
#[test]
fn round_trip_char_brightness() {
round_trip(TypedCommand::CharBrightness(CharBrightness {
round_trip(TypedCommand::CharBrightness(commands::CharBrightness {
origin: Origin::new(5, 2),
grid: BrightnessGrid::new(7, 5),
}));
@ -357,33 +343,28 @@ mod tests {
#[test]
fn round_trip_bitmap_linear() {
for compression in all_compressions().iter().copied() {
round_trip(TypedCommand::BitmapLinear(BitmapLinear {
offset: 23,
bitvec: BitVec::repeat(false, 40),
compression,
}));
round_trip(TypedCommand::BitmapLinearAnd(
commands::BitmapLinearAnd {
offset: 23,
bitvec: BitVec::repeat(false, 40),
for operation in [
BinaryOperation::Overwrite,
BinaryOperation::And,
BinaryOperation::Or,
BinaryOperation::Xor,
] {
round_trip(TypedCommand::BitmapLinear(
commands::BitmapLinear {
offset: 23,
bitvec: BitVec::repeat(false, 40),
compression,
operation,
},
));
}
round_trip(TypedCommand::BitmapLinearWin(
commands::BitmapLinearWin {
origin: Origin::ZERO,
bitmap: Bitmap::max_sized(),
compression,
},
));
round_trip(TypedCommand::BitmapLinearOr(commands::BitmapLinearOr {
offset: 23,
bitvec: BitVec::repeat(false, 40),
compression,
}));
round_trip(TypedCommand::BitmapLinearXor(BitmapLinearXor {
offset: 23,
bitvec: BitVec::repeat(false, 40),
compression,
}));
round_trip(TypedCommand::BitmapLinearWin(BitmapLinearWin {
origin: Origin::ZERO,
bitmap: Bitmap::max_sized(),
compression,
}));
}
}
@ -534,10 +515,11 @@ mod tests {
#[test]
fn error_decompression_failed_and() {
for compression in all_compressions().iter().copied() {
let p: Packet = commands::BitmapLinearAnd {
let p: Packet = commands::BitmapLinear {
offset: 0,
bitvec: BitVec::repeat(false, 8),
compression,
operation: BinaryOperation::Overwrite,
}
.into();
let Packet {
@ -598,6 +580,7 @@ mod tests {
offset: 0,
bitvec: BitVec::repeat(false, 8),
compression: CompressionCode::Uncompressed,
operation: BinaryOperation::Or,
}
.into();
let Header {
@ -629,6 +612,7 @@ mod tests {
offset: 0,
bitvec: BitVec::repeat(false, 8),
compression: CompressionCode::Uncompressed,
operation: BinaryOperation::And,
}
.into();
let Header {
@ -660,6 +644,7 @@ mod tests {
offset: 0,
bitvec: BitVec::repeat(false, 8),
compression: CompressionCode::Uncompressed,
operation: BinaryOperation::Xor,
}
.into();
let Header {
@ -699,7 +684,10 @@ mod tests {
#[test]
fn packet_into_char_brightness_invalid() {
let grid = BrightnessGrid::new(2, 2);
let command = commands::CharBrightness{origin: Origin::ZERO, grid};
let command = commands::CharBrightness {
origin: Origin::ZERO,
grid,
};
let mut packet: Packet = command.into();
let slot = packet.payload.get_mut(1).unwrap();
*slot = 23;
@ -711,7 +699,10 @@ mod tests {
#[test]
fn packet_into_brightness_invalid() {
let mut packet: Packet = commands::GlobalBrightness{brightness: Brightness::MAX}.into();
let mut packet: Packet = commands::GlobalBrightness {
brightness: Brightness::MAX,
}
.into();
let slot = packet.payload.get_mut(0).unwrap();
*slot = 42;
assert_eq!(

View file

@ -24,8 +24,7 @@
//! ```
use crate::command_code::CommandCode;
use crate::compression::into_compressed;
use crate::{CompressionCode, Grid, Offset, Origin, Tiles};
use crate::{Grid, Origin, Tiles};
use std::mem::size_of;
/// A raw header.
@ -136,28 +135,6 @@ impl TryFrom<Vec<u8>> for Packet {
}
impl Packet {
/// Helper method for `BitmapLinear*`-Commands into [Packet]
#[allow(clippy::cast_possible_truncation)]
pub(crate) fn bitmap_linear_into_packet(
command: CommandCode,
offset: Offset,
compression: CompressionCode,
payload: Vec<u8>,
) -> Packet {
let length = payload.len() as u16;
let payload = into_compressed(compression, payload);
Packet {
header: Header {
command_code: command.into(),
a: offset as u16,
b: length,
c: compression.into(),
d: 0,
},
payload,
}
}
fn u16_from_be_slice(slice: &[u8]) -> u16 {
let mut bytes = [0u8; 2];
bytes[0] = slice[0];