feat: port non-8bpp blit and introduce PixelGridMut for sound FFI
Browse filesPort all remaining paths in BitGrid__BlitSpriteRect (0x4F6910) to Rust,
eliminating the fallthrough to the original WA function:
- 1-bit byte-aligned fast path: memcpy/OR/nop for modes 0-3 (collision masks)
- Generic per-pixel fallback (port of FUN_004f80c0): modes 0-5 for any
cells_per_unit, handling unaligned 1-bit, erase, and collision test
- Orientation adjustment logic ported from the original function
Also introduce PixelGridMut<'a> (data: &mut [u8]) to replace the unsound
Vec::from_raw_parts hack used to wrap WA-allocated memory in PixelGrid.
All blit functions now accept PixelGridMut, and the three DLL bridge sites
use slice::from_raw_parts_mut instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/display/line_draw.rs
CHANGED
|
@@ -46,6 +46,41 @@ pub struct PixelGrid {
|
|
| 46 |
pub clip_bottom: u32,
|
| 47 |
}
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
impl PixelGrid {
|
| 50 |
/// Create a new zeroed pixel grid with default clip rect covering the full surface.
|
| 51 |
pub fn new(width: u32, height: u32) -> Self {
|
|
@@ -63,6 +98,20 @@ impl PixelGrid {
|
|
| 63 |
}
|
| 64 |
}
|
| 65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
/// Clear all pixels to zero.
|
| 67 |
pub fn clear(&mut self) {
|
| 68 |
self.data.fill(0);
|
|
|
|
| 46 |
pub clip_bottom: u32,
|
| 47 |
}
|
| 48 |
|
| 49 |
+
/// Borrowed mutable view into an 8bpp pixel buffer.
|
| 50 |
+
///
|
| 51 |
+
/// Same layout as `PixelGrid`, but `data` is a `&mut [u8]` instead of `Vec<u8>`.
|
| 52 |
+
/// Used when the buffer is owned externally (e.g., WA's BitGrid data) and wrapping
|
| 53 |
+
/// it in a `Vec` would be unsound. All blit functions accept this type.
|
| 54 |
+
pub struct PixelGridMut<'a> {
|
| 55 |
+
pub data: &'a mut [u8],
|
| 56 |
+
pub width: u32,
|
| 57 |
+
pub height: u32,
|
| 58 |
+
pub row_stride: u32,
|
| 59 |
+
pub clip_left: u32,
|
| 60 |
+
pub clip_top: u32,
|
| 61 |
+
pub clip_right: u32,
|
| 62 |
+
pub clip_bottom: u32,
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
impl<'a> PixelGridMut<'a> {
|
| 66 |
+
/// Create a new `PixelGridMut` with a shorter lifetime, allowing the
|
| 67 |
+
/// original to be reused afterward. Equivalent to `&mut *self` for
|
| 68 |
+
/// a by-value type — needed when passing to functions that consume
|
| 69 |
+
/// `PixelGridMut` inside a loop.
|
| 70 |
+
pub fn reborrow(&mut self) -> PixelGridMut<'_> {
|
| 71 |
+
PixelGridMut {
|
| 72 |
+
data: self.data,
|
| 73 |
+
width: self.width,
|
| 74 |
+
height: self.height,
|
| 75 |
+
row_stride: self.row_stride,
|
| 76 |
+
clip_left: self.clip_left,
|
| 77 |
+
clip_top: self.clip_top,
|
| 78 |
+
clip_right: self.clip_right,
|
| 79 |
+
clip_bottom: self.clip_bottom,
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
impl PixelGrid {
|
| 85 |
/// Create a new zeroed pixel grid with default clip rect covering the full surface.
|
| 86 |
pub fn new(width: u32, height: u32) -> Self {
|
|
|
|
| 98 |
}
|
| 99 |
}
|
| 100 |
|
| 101 |
+
/// Borrow this grid as a `PixelGridMut` — the type accepted by blit functions.
|
| 102 |
+
pub fn as_grid_mut(&mut self) -> PixelGridMut<'_> {
|
| 103 |
+
PixelGridMut {
|
| 104 |
+
data: &mut self.data,
|
| 105 |
+
width: self.width,
|
| 106 |
+
height: self.height,
|
| 107 |
+
row_stride: self.row_stride,
|
| 108 |
+
clip_left: self.clip_left,
|
| 109 |
+
clip_top: self.clip_top,
|
| 110 |
+
clip_right: self.clip_right,
|
| 111 |
+
clip_bottom: self.clip_bottom,
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
/// Clear all pixels to zero.
|
| 116 |
pub fn clear(&mut self) {
|
| 117 |
self.data.fill(0);
|
crates/openwa-core/src/display/sprite_blit.rs
CHANGED
|
@@ -22,7 +22,7 @@
|
|
| 22 |
//! └── Mode 3: subtractive color mix
|
| 23 |
//! ```
|
| 24 |
|
| 25 |
-
use super::line_draw::PixelGrid;
|
| 26 |
|
| 27 |
/// Source surface for blit operations.
|
| 28 |
///
|
|
@@ -135,7 +135,7 @@ impl BlitBlend {
|
|
| 135 |
///
|
| 136 |
/// Returns `true` if any pixels were drawn.
|
| 137 |
pub fn blit_sprite_rect(
|
| 138 |
-
dst:
|
| 139 |
src: &BlitSource,
|
| 140 |
dst_x: i32,
|
| 141 |
dst_y: i32,
|
|
@@ -205,7 +205,7 @@ pub fn blit_sprite_rect(
|
|
| 205 |
vis_bottom,
|
| 206 |
);
|
| 207 |
blit_copy(
|
| 208 |
-
dst, src, vis_left, vis_top, vis_w, vis_h, sx_start, sy_start, 1, 1,
|
| 209 |
);
|
| 210 |
}
|
| 211 |
BlitBlend::ColorTable => {
|
|
@@ -228,12 +228,12 @@ pub fn blit_sprite_rect(
|
|
| 228 |
let table = color_table.unwrap_or(&IDENTITY_TABLE);
|
| 229 |
if swap_axes {
|
| 230 |
blit_color_table_swapped(
|
| 231 |
-
dst, src, vis_left, vis_top, vis_w, vis_h, sx_start, sy_start, sx_step,
|
| 232 |
sy_step, table,
|
| 233 |
);
|
| 234 |
} else {
|
| 235 |
blit_color_table(
|
| 236 |
-
dst, src, vis_left, vis_top, vis_w, vis_h, sx_start, sy_start, sx_step,
|
| 237 |
sy_step, table,
|
| 238 |
);
|
| 239 |
}
|
|
@@ -254,7 +254,7 @@ pub fn blit_sprite_rect(
|
|
| 254 |
vis_bottom,
|
| 255 |
);
|
| 256 |
blit_masked(
|
| 257 |
-
dst,
|
| 258 |
src,
|
| 259 |
vis_left,
|
| 260 |
vis_top,
|
|
@@ -284,7 +284,7 @@ pub fn blit_sprite_rect(
|
|
| 284 |
vis_bottom,
|
| 285 |
);
|
| 286 |
blit_masked(
|
| 287 |
-
dst,
|
| 288 |
src,
|
| 289 |
vis_left,
|
| 290 |
vis_top,
|
|
@@ -381,7 +381,7 @@ fn orientation_steps(orientation: BlitOrientation) -> (i32, i32, bool) {
|
|
| 381 |
///
|
| 382 |
/// Source is scanned with sx advancing per dst X, sy advancing per dst Y.
|
| 383 |
fn blit_copy(
|
| 384 |
-
dst: &mut
|
| 385 |
src: &BlitSource,
|
| 386 |
vis_left: i32,
|
| 387 |
vis_top: i32,
|
|
@@ -423,7 +423,7 @@ fn blit_copy(
|
|
| 423 |
/// Each non-zero source pixel is looked up in the color table before writing.
|
| 424 |
/// Zero (transparent) pixels are skipped.
|
| 425 |
fn blit_color_table(
|
| 426 |
-
dst: &mut
|
| 427 |
src: &BlitSource,
|
| 428 |
vis_left: i32,
|
| 429 |
vis_top: i32,
|
|
@@ -458,7 +458,7 @@ fn blit_color_table(
|
|
| 458 |
|
| 459 |
/// Color-table blend blit — 90° rotated orientations (axes swapped).
|
| 460 |
fn blit_color_table_swapped(
|
| 461 |
-
dst: &mut
|
| 462 |
src: &BlitSource,
|
| 463 |
vis_left: i32,
|
| 464 |
vis_top: i32,
|
|
@@ -495,7 +495,7 @@ fn blit_color_table_swapped(
|
|
| 495 |
/// Copies source pixels to destination where the predicate `should_write(src, dst)` is true.
|
| 496 |
/// Used by modes 2 (additive) and 3 (subtractive).
|
| 497 |
fn blit_masked(
|
| 498 |
-
dst: &mut
|
| 499 |
src: &BlitSource,
|
| 500 |
vis_left: i32,
|
| 501 |
vis_top: i32,
|
|
@@ -551,6 +551,262 @@ pub fn pixel_grid_from_indexed(width: u32, height: u32, pixels: &[u8]) -> PixelG
|
|
| 551 |
grid
|
| 552 |
}
|
| 553 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
// ---------------------------------------------------------------------------
|
| 555 |
// Stippled blit (checkerboard pattern)
|
| 556 |
// ---------------------------------------------------------------------------
|
|
@@ -567,7 +823,7 @@ pub fn pixel_grid_from_indexed(width: u32, height: u32, pixels: &[u8]) -> PixelG
|
|
| 567 |
/// Only non-zero source pixels are drawn (transparent = 0).
|
| 568 |
/// Pixels outside the destination clip rect are skipped.
|
| 569 |
pub fn blit_stippled(
|
| 570 |
-
dst: &mut
|
| 571 |
src: &BlitSource<'_>,
|
| 572 |
dst_x: i32,
|
| 573 |
dst_y: i32,
|
|
@@ -636,7 +892,7 @@ pub fn blit_stippled(
|
|
| 636 |
/// `flags` is the blit flags word (orientation + blend mode) passed through
|
| 637 |
/// to each individual `blit_sprite_rect` call.
|
| 638 |
pub fn blit_tiled(
|
| 639 |
-
dst: &mut
|
| 640 |
src: &BlitSource<'_>,
|
| 641 |
initial_x: i32,
|
| 642 |
dst_y: i32,
|
|
@@ -666,7 +922,7 @@ pub fn blit_tiled(
|
|
| 666 |
// Tile rightward until past clip_right
|
| 667 |
while x < clip_right {
|
| 668 |
blit_sprite_rect(
|
| 669 |
-
dst,
|
| 670 |
src,
|
| 671 |
x,
|
| 672 |
dst_y,
|
|
@@ -711,7 +967,7 @@ mod tests {
|
|
| 711 |
let mut dst = PixelGrid::new(16, 16);
|
| 712 |
|
| 713 |
blit_sprite_rect(
|
| 714 |
-
|
| 715 |
&src,
|
| 716 |
4,
|
| 717 |
4, // dst position
|
|
@@ -744,7 +1000,7 @@ mod tests {
|
|
| 744 |
let mut dst_mirror = PixelGrid::new(16, 16);
|
| 745 |
|
| 746 |
blit_sprite_rect(
|
| 747 |
-
|
| 748 |
&src,
|
| 749 |
4,
|
| 750 |
4,
|
|
@@ -757,7 +1013,7 @@ mod tests {
|
|
| 757 |
BlitBlend::Copy,
|
| 758 |
);
|
| 759 |
blit_sprite_rect(
|
| 760 |
-
|
| 761 |
&src,
|
| 762 |
4,
|
| 763 |
4,
|
|
@@ -784,7 +1040,7 @@ mod tests {
|
|
| 784 |
let mut dst = PixelGrid::new(16, 16);
|
| 785 |
|
| 786 |
blit_sprite_rect(
|
| 787 |
-
|
| 788 |
&src,
|
| 789 |
4,
|
| 790 |
4,
|
|
@@ -811,7 +1067,7 @@ mod tests {
|
|
| 811 |
let mut dst = PixelGrid::new(16, 16);
|
| 812 |
|
| 813 |
blit_sprite_rect(
|
| 814 |
-
|
| 815 |
&src,
|
| 816 |
4,
|
| 817 |
4,
|
|
@@ -841,7 +1097,7 @@ mod tests {
|
|
| 841 |
dst.clip_top = 8;
|
| 842 |
|
| 843 |
blit_sprite_rect(
|
| 844 |
-
|
| 845 |
&src,
|
| 846 |
4,
|
| 847 |
4,
|
|
@@ -877,7 +1133,7 @@ mod tests {
|
|
| 877 |
table[0] = 0; // transparent stays 0
|
| 878 |
|
| 879 |
blit_sprite_rect(
|
| 880 |
-
|
| 881 |
&src,
|
| 882 |
4,
|
| 883 |
4,
|
|
@@ -912,7 +1168,7 @@ mod tests {
|
|
| 912 |
dst.data.fill(99); // background
|
| 913 |
|
| 914 |
blit_sprite_rect(
|
| 915 |
-
|
| 916 |
&src,
|
| 917 |
0,
|
| 918 |
0,
|
|
@@ -948,7 +1204,7 @@ mod tests {
|
|
| 948 |
lut[4] = 40;
|
| 949 |
|
| 950 |
blit_sprite_rect(
|
| 951 |
-
|
| 952 |
&src,
|
| 953 |
4,
|
| 954 |
4,
|
|
@@ -983,7 +1239,7 @@ mod tests {
|
|
| 983 |
lut[4] = 40;
|
| 984 |
|
| 985 |
blit_sprite_rect(
|
| 986 |
-
|
| 987 |
&src,
|
| 988 |
4,
|
| 989 |
4,
|
|
@@ -1017,7 +1273,7 @@ mod tests {
|
|
| 1017 |
}
|
| 1018 |
|
| 1019 |
blit_sprite_rect(
|
| 1020 |
-
|
| 1021 |
&src,
|
| 1022 |
4,
|
| 1023 |
4,
|
|
@@ -1055,7 +1311,7 @@ mod tests {
|
|
| 1055 |
}
|
| 1056 |
|
| 1057 |
blit_sprite_rect(
|
| 1058 |
-
|
| 1059 |
&src,
|
| 1060 |
4,
|
| 1061 |
4,
|
|
@@ -1102,7 +1358,7 @@ mod tests {
|
|
| 1102 |
dst.data.fill(99); // all non-zero
|
| 1103 |
|
| 1104 |
blit_sprite_rect(
|
| 1105 |
-
|
| 1106 |
&src,
|
| 1107 |
0,
|
| 1108 |
0,
|
|
@@ -1241,7 +1497,7 @@ mod tests {
|
|
| 1241 |
let mut dst = PixelGrid::new(canvas_w, canvas_h);
|
| 1242 |
|
| 1243 |
blit_sprite_rect(
|
| 1244 |
-
|
| 1245 |
&src,
|
| 1246 |
16,
|
| 1247 |
16,
|
|
@@ -1279,7 +1535,7 @@ mod tests {
|
|
| 1279 |
|
| 1280 |
let mut dst = PixelGrid::new(sprite.width, sprite.height);
|
| 1281 |
blit_sprite_rect(
|
| 1282 |
-
|
| 1283 |
&src,
|
| 1284 |
0,
|
| 1285 |
0,
|
|
@@ -1311,7 +1567,7 @@ mod tests {
|
|
| 1311 |
// Blit at (-16, -16) so only bottom-right portion is visible
|
| 1312 |
let mut dst = PixelGrid::new(sprite.width, sprite.height);
|
| 1313 |
blit_sprite_rect(
|
| 1314 |
-
|
| 1315 |
&src,
|
| 1316 |
-16,
|
| 1317 |
-16,
|
|
@@ -1344,7 +1600,7 @@ mod tests {
|
|
| 1344 |
dst.data.fill(42); // background color
|
| 1345 |
|
| 1346 |
blit_sprite_rect(
|
| 1347 |
-
|
| 1348 |
&src,
|
| 1349 |
0,
|
| 1350 |
0,
|
|
@@ -1397,7 +1653,7 @@ mod tests {
|
|
| 1397 |
dst.data.fill(42);
|
| 1398 |
|
| 1399 |
blit_sprite_rect(
|
| 1400 |
-
|
| 1401 |
&src,
|
| 1402 |
0,
|
| 1403 |
0,
|
|
@@ -1504,7 +1760,7 @@ mod tests {
|
|
| 1504 |
dst.data.fill(bg_fill);
|
| 1505 |
|
| 1506 |
blit_sprite_rect(
|
| 1507 |
-
|
| 1508 |
&src,
|
| 1509 |
dst_x,
|
| 1510 |
dst_y,
|
|
@@ -1759,7 +2015,18 @@ mod tests {
|
|
| 1759 |
let mut dst = PixelGrid::new(canvas_w, canvas_h);
|
| 1760 |
dst.data.fill(77); // background matching DLL capture
|
| 1761 |
|
| 1762 |
-
blit_stippled(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1763 |
|
| 1764 |
assert_matches_snapshot(&dst, snap_name);
|
| 1765 |
}
|
|
@@ -1803,8 +2070,16 @@ mod tests {
|
|
| 1803 |
let clip_right = canvas_w as i32;
|
| 1804 |
|
| 1805 |
blit_tiled(
|
| 1806 |
-
&mut dst,
|
| 1807 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1808 |
);
|
| 1809 |
|
| 1810 |
assert_matches_snapshot(&dst, snap_name);
|
|
|
|
| 22 |
//! └── Mode 3: subtractive color mix
|
| 23 |
//! ```
|
| 24 |
|
| 25 |
+
use super::line_draw::{PixelGrid, PixelGridMut};
|
| 26 |
|
| 27 |
/// Source surface for blit operations.
|
| 28 |
///
|
|
|
|
| 135 |
///
|
| 136 |
/// Returns `true` if any pixels were drawn.
|
| 137 |
pub fn blit_sprite_rect(
|
| 138 |
+
mut dst: PixelGridMut<'_>,
|
| 139 |
src: &BlitSource,
|
| 140 |
dst_x: i32,
|
| 141 |
dst_y: i32,
|
|
|
|
| 205 |
vis_bottom,
|
| 206 |
);
|
| 207 |
blit_copy(
|
| 208 |
+
&mut dst, src, vis_left, vis_top, vis_w, vis_h, sx_start, sy_start, 1, 1,
|
| 209 |
);
|
| 210 |
}
|
| 211 |
BlitBlend::ColorTable => {
|
|
|
|
| 228 |
let table = color_table.unwrap_or(&IDENTITY_TABLE);
|
| 229 |
if swap_axes {
|
| 230 |
blit_color_table_swapped(
|
| 231 |
+
&mut dst, src, vis_left, vis_top, vis_w, vis_h, sx_start, sy_start, sx_step,
|
| 232 |
sy_step, table,
|
| 233 |
);
|
| 234 |
} else {
|
| 235 |
blit_color_table(
|
| 236 |
+
&mut dst, src, vis_left, vis_top, vis_w, vis_h, sx_start, sy_start, sx_step,
|
| 237 |
sy_step, table,
|
| 238 |
);
|
| 239 |
}
|
|
|
|
| 254 |
vis_bottom,
|
| 255 |
);
|
| 256 |
blit_masked(
|
| 257 |
+
&mut dst,
|
| 258 |
src,
|
| 259 |
vis_left,
|
| 260 |
vis_top,
|
|
|
|
| 284 |
vis_bottom,
|
| 285 |
);
|
| 286 |
blit_masked(
|
| 287 |
+
&mut dst,
|
| 288 |
src,
|
| 289 |
vis_left,
|
| 290 |
vis_top,
|
|
|
|
| 381 |
///
|
| 382 |
/// Source is scanned with sx advancing per dst X, sy advancing per dst Y.
|
| 383 |
fn blit_copy(
|
| 384 |
+
dst: &mut PixelGridMut<'_>,
|
| 385 |
src: &BlitSource,
|
| 386 |
vis_left: i32,
|
| 387 |
vis_top: i32,
|
|
|
|
| 423 |
/// Each non-zero source pixel is looked up in the color table before writing.
|
| 424 |
/// Zero (transparent) pixels are skipped.
|
| 425 |
fn blit_color_table(
|
| 426 |
+
dst: &mut PixelGridMut<'_>,
|
| 427 |
src: &BlitSource,
|
| 428 |
vis_left: i32,
|
| 429 |
vis_top: i32,
|
|
|
|
| 458 |
|
| 459 |
/// Color-table blend blit — 90° rotated orientations (axes swapped).
|
| 460 |
fn blit_color_table_swapped(
|
| 461 |
+
dst: &mut PixelGridMut<'_>,
|
| 462 |
src: &BlitSource,
|
| 463 |
vis_left: i32,
|
| 464 |
vis_top: i32,
|
|
|
|
| 495 |
/// Copies source pixels to destination where the predicate `should_write(src, dst)` is true.
|
| 496 |
/// Used by modes 2 (additive) and 3 (subtractive).
|
| 497 |
fn blit_masked(
|
| 498 |
+
dst: &mut PixelGridMut<'_>,
|
| 499 |
src: &BlitSource,
|
| 500 |
vis_left: i32,
|
| 501 |
vis_top: i32,
|
|
|
|
| 551 |
grid
|
| 552 |
}
|
| 553 |
|
| 554 |
+
// ---------------------------------------------------------------------------
|
| 555 |
+
// 1-bit blit (collision masks)
|
| 556 |
+
// ---------------------------------------------------------------------------
|
| 557 |
+
|
| 558 |
+
/// Get a single bit from a 1-bit BitGrid data buffer.
|
| 559 |
+
///
|
| 560 |
+
/// Bit layout matches WA's CollisionBitGrid (0x4F5D70):
|
| 561 |
+
/// byte = data[y * stride + (x >> 3)], bit = (byte >> (x & 7)) & 1
|
| 562 |
+
#[inline]
|
| 563 |
+
fn bit_get(data: &[u8], stride: u32, x: i32, y: i32) -> u8 {
|
| 564 |
+
let byte = data[(y as u32 * stride + (x as u32 >> 3)) as usize];
|
| 565 |
+
(byte >> (x & 7)) & 1
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
/// Set a single bit in a 1-bit BitGrid data buffer.
|
| 569 |
+
///
|
| 570 |
+
/// Matches WA's CollisionBitGrid__Put (0x4F5DA0):
|
| 571 |
+
/// clear the bit, then OR in the new value.
|
| 572 |
+
#[inline]
|
| 573 |
+
fn bit_put(data: &mut [u8], stride: u32, x: i32, y: i32, value: u8) {
|
| 574 |
+
let idx = (y as u32 * stride + (x as u32 >> 3)) as usize;
|
| 575 |
+
let bit = (x & 7) as u8;
|
| 576 |
+
data[idx] = (data[idx] & !(1 << bit)) | (((value != 0) as u8) << bit);
|
| 577 |
+
}
|
| 578 |
+
|
| 579 |
+
/// Byte-aligned 1-bit blit fast path.
|
| 580 |
+
///
|
| 581 |
+
/// Port of the optimized 1-bit path in BitGrid__BlitSpriteRect (0x4F6910).
|
| 582 |
+
/// Requires: both surfaces cpp==1, clip_left/clip_right/src_x all byte-aligned
|
| 583 |
+
/// (divisible by 8), no color table, Normal orientation.
|
| 584 |
+
///
|
| 585 |
+
/// Blend modes:
|
| 586 |
+
/// - 0 (Copy): memcpy bytes
|
| 587 |
+
/// - 1 (ColorTable) / 3 (Subtractive): bitwise OR (both simplify to OR for 1-bit)
|
| 588 |
+
/// - 2 (Additive): no-op (AND of single bits is identity when both are 1)
|
| 589 |
+
pub fn blit_1bit_aligned(
|
| 590 |
+
dst: &mut [u8],
|
| 591 |
+
dst_stride: u32,
|
| 592 |
+
src: &[u8],
|
| 593 |
+
src_stride: u32,
|
| 594 |
+
clip_left: i32,
|
| 595 |
+
clip_top: i32,
|
| 596 |
+
clip_right: i32,
|
| 597 |
+
clip_bottom: i32,
|
| 598 |
+
src_x: i32,
|
| 599 |
+
src_y: i32,
|
| 600 |
+
blend_mode: u32,
|
| 601 |
+
) {
|
| 602 |
+
let byte_width = ((clip_right - clip_left) >> 3) as usize;
|
| 603 |
+
let dst_byte_x = (clip_left >> 3) as usize;
|
| 604 |
+
let src_byte_x = (src_x >> 3) as usize;
|
| 605 |
+
|
| 606 |
+
match blend_mode {
|
| 607 |
+
0 => {
|
| 608 |
+
// Mode 0 (Copy): memcpy per row
|
| 609 |
+
for row in clip_top..clip_bottom {
|
| 610 |
+
let dst_off = row as usize * dst_stride as usize + dst_byte_x;
|
| 611 |
+
let src_row = src_y + (row - clip_top);
|
| 612 |
+
let src_off = src_row as usize * src_stride as usize + src_byte_x;
|
| 613 |
+
dst[dst_off..dst_off + byte_width]
|
| 614 |
+
.copy_from_slice(&src[src_off..src_off + byte_width]);
|
| 615 |
+
}
|
| 616 |
+
}
|
| 617 |
+
2 => {
|
| 618 |
+
// Mode 2 (Additive): no-op for 1-bit
|
| 619 |
+
}
|
| 620 |
+
_ => {
|
| 621 |
+
// Mode 1 (ColorTable) and 3 (Subtractive): bitwise OR
|
| 622 |
+
// Both simplify to OR for single-bit values.
|
| 623 |
+
for row in clip_top..clip_bottom {
|
| 624 |
+
let dst_off = row as usize * dst_stride as usize + dst_byte_x;
|
| 625 |
+
let src_row = src_y + (row - clip_top);
|
| 626 |
+
let src_off = src_row as usize * src_stride as usize + src_byte_x;
|
| 627 |
+
for i in 0..byte_width {
|
| 628 |
+
dst[dst_off + i] |= src[src_off + i];
|
| 629 |
+
}
|
| 630 |
+
}
|
| 631 |
+
}
|
| 632 |
+
}
|
| 633 |
+
}
|
| 634 |
+
|
| 635 |
+
/// Generic per-pixel blit fallback.
|
| 636 |
+
///
|
| 637 |
+
/// Port of FUN_004f80c0 — handles all blend modes (0-5) for any cells_per_unit
|
| 638 |
+
/// by operating one pixel at a time. Slower than the specialized fast paths but
|
| 639 |
+
/// handles unaligned 1-bit blits, oriented blits that fell through, and blend
|
| 640 |
+
/// modes 4 (erase) and 5 (collision test).
|
| 641 |
+
///
|
| 642 |
+
/// Parameters match the original's calling convention:
|
| 643 |
+
/// - clip_left..clip_right, clip_top..clip_bottom: destination region (already clipped)
|
| 644 |
+
/// - src_x: source X corresponding to clip_left
|
| 645 |
+
/// - src_y: source Y corresponding to clip_top
|
| 646 |
+
/// - blend_mode: 0=copy, 1=color_table, 2=additive, 3=subtractive, 4=erase, 5=collision_test
|
| 647 |
+
///
|
| 648 |
+
/// Returns 1 normally, or for mode 5: 1 if collision detected, 0 if not.
|
| 649 |
+
pub fn blit_generic_perpixel(
|
| 650 |
+
dst: &mut [u8],
|
| 651 |
+
dst_stride: u32,
|
| 652 |
+
dst_cpp: u32,
|
| 653 |
+
src: &[u8],
|
| 654 |
+
src_stride: u32,
|
| 655 |
+
src_cpp: u32,
|
| 656 |
+
clip_left: i32,
|
| 657 |
+
clip_top: i32,
|
| 658 |
+
clip_right: i32,
|
| 659 |
+
clip_bottom: i32,
|
| 660 |
+
src_x: i32,
|
| 661 |
+
src_y: i32,
|
| 662 |
+
color_table: Option<&[u8; 256]>,
|
| 663 |
+
blend_mode: u32,
|
| 664 |
+
) -> u32 {
|
| 665 |
+
// Pixel accessors based on cells_per_unit
|
| 666 |
+
#[inline]
|
| 667 |
+
fn get_pixel(data: &[u8], stride: u32, cpp: u32, x: i32, y: i32) -> u8 {
|
| 668 |
+
if cpp == 1 {
|
| 669 |
+
bit_get(data, stride, x, y)
|
| 670 |
+
} else {
|
| 671 |
+
data[y as usize * stride as usize + x as usize]
|
| 672 |
+
}
|
| 673 |
+
}
|
| 674 |
+
|
| 675 |
+
#[inline]
|
| 676 |
+
fn put_pixel(data: &mut [u8], stride: u32, cpp: u32, x: i32, y: i32, value: u8) {
|
| 677 |
+
if cpp == 1 {
|
| 678 |
+
bit_put(data, stride, x, y, value);
|
| 679 |
+
} else {
|
| 680 |
+
data[y as usize * stride as usize + x as usize] = value;
|
| 681 |
+
}
|
| 682 |
+
}
|
| 683 |
+
|
| 684 |
+
let src_x_offset = src_x - clip_left;
|
| 685 |
+
|
| 686 |
+
match blend_mode {
|
| 687 |
+
0 => {
|
| 688 |
+
// Copy: get from src, put to dst
|
| 689 |
+
for y in clip_top..clip_bottom {
|
| 690 |
+
for x in clip_left..clip_right {
|
| 691 |
+
let pixel = get_pixel(
|
| 692 |
+
src,
|
| 693 |
+
src_stride,
|
| 694 |
+
src_cpp,
|
| 695 |
+
src_x_offset + x,
|
| 696 |
+
src_y + (y - clip_top),
|
| 697 |
+
);
|
| 698 |
+
put_pixel(dst, dst_stride, dst_cpp, x, y, pixel);
|
| 699 |
+
}
|
| 700 |
+
}
|
| 701 |
+
1
|
| 702 |
+
}
|
| 703 |
+
1 => {
|
| 704 |
+
// Color table blend — per-pixel with optional LUT
|
| 705 |
+
for y in clip_top..clip_bottom {
|
| 706 |
+
for x in clip_left..clip_right {
|
| 707 |
+
let pixel = get_pixel(
|
| 708 |
+
src,
|
| 709 |
+
src_stride,
|
| 710 |
+
src_cpp,
|
| 711 |
+
src_x_offset + x,
|
| 712 |
+
src_y + (y - clip_top),
|
| 713 |
+
);
|
| 714 |
+
if pixel != 0 {
|
| 715 |
+
let value = match color_table {
|
| 716 |
+
Some(table) => table[pixel as usize],
|
| 717 |
+
None => pixel,
|
| 718 |
+
};
|
| 719 |
+
put_pixel(dst, dst_stride, dst_cpp, x, y, value);
|
| 720 |
+
}
|
| 721 |
+
}
|
| 722 |
+
}
|
| 723 |
+
1
|
| 724 |
+
}
|
| 725 |
+
2 => {
|
| 726 |
+
// Additive: copy src where both src and dst non-zero
|
| 727 |
+
for y in clip_top..clip_bottom {
|
| 728 |
+
for x in clip_left..clip_right {
|
| 729 |
+
let src_pixel = get_pixel(
|
| 730 |
+
src,
|
| 731 |
+
src_stride,
|
| 732 |
+
src_cpp,
|
| 733 |
+
src_x_offset + x,
|
| 734 |
+
src_y + (y - clip_top),
|
| 735 |
+
);
|
| 736 |
+
if src_pixel != 0 {
|
| 737 |
+
let dst_pixel = get_pixel(dst, dst_stride, dst_cpp, x, y);
|
| 738 |
+
if dst_pixel != 0 {
|
| 739 |
+
put_pixel(dst, dst_stride, dst_cpp, x, y, src_pixel);
|
| 740 |
+
}
|
| 741 |
+
}
|
| 742 |
+
}
|
| 743 |
+
}
|
| 744 |
+
1
|
| 745 |
+
}
|
| 746 |
+
3 => {
|
| 747 |
+
// Subtractive: copy src where src non-zero and dst zero
|
| 748 |
+
for y in clip_top..clip_bottom {
|
| 749 |
+
for x in clip_left..clip_right {
|
| 750 |
+
let src_pixel = get_pixel(
|
| 751 |
+
src,
|
| 752 |
+
src_stride,
|
| 753 |
+
src_cpp,
|
| 754 |
+
src_x_offset + x,
|
| 755 |
+
src_y + (y - clip_top),
|
| 756 |
+
);
|
| 757 |
+
if src_pixel != 0 {
|
| 758 |
+
let dst_pixel = get_pixel(dst, dst_stride, dst_cpp, x, y);
|
| 759 |
+
if dst_pixel == 0 {
|
| 760 |
+
put_pixel(dst, dst_stride, dst_cpp, x, y, src_pixel);
|
| 761 |
+
}
|
| 762 |
+
}
|
| 763 |
+
}
|
| 764 |
+
}
|
| 765 |
+
1
|
| 766 |
+
}
|
| 767 |
+
4 => {
|
| 768 |
+
// Erase: clear dst where src non-zero
|
| 769 |
+
for y in clip_top..clip_bottom {
|
| 770 |
+
for x in clip_left..clip_right {
|
| 771 |
+
let src_pixel = get_pixel(
|
| 772 |
+
src,
|
| 773 |
+
src_stride,
|
| 774 |
+
src_cpp,
|
| 775 |
+
src_x_offset + x,
|
| 776 |
+
src_y + (y - clip_top),
|
| 777 |
+
);
|
| 778 |
+
if src_pixel != 0 {
|
| 779 |
+
put_pixel(dst, dst_stride, dst_cpp, x, y, 0);
|
| 780 |
+
}
|
| 781 |
+
}
|
| 782 |
+
}
|
| 783 |
+
1
|
| 784 |
+
}
|
| 785 |
+
5 => {
|
| 786 |
+
// Collision test: return 1 if any pixel overlaps (both non-zero)
|
| 787 |
+
for y in clip_top..clip_bottom {
|
| 788 |
+
for x in clip_left..clip_right {
|
| 789 |
+
let dst_pixel = get_pixel(dst, dst_stride, dst_cpp, x, y);
|
| 790 |
+
if dst_pixel != 0 {
|
| 791 |
+
let src_pixel = get_pixel(
|
| 792 |
+
src,
|
| 793 |
+
src_stride,
|
| 794 |
+
src_cpp,
|
| 795 |
+
src_x_offset + x,
|
| 796 |
+
src_y + (y - clip_top),
|
| 797 |
+
);
|
| 798 |
+
if src_pixel != 0 {
|
| 799 |
+
return 1;
|
| 800 |
+
}
|
| 801 |
+
}
|
| 802 |
+
}
|
| 803 |
+
}
|
| 804 |
+
0
|
| 805 |
+
}
|
| 806 |
+
_ => 1,
|
| 807 |
+
}
|
| 808 |
+
}
|
| 809 |
+
|
| 810 |
// ---------------------------------------------------------------------------
|
| 811 |
// Stippled blit (checkerboard pattern)
|
| 812 |
// ---------------------------------------------------------------------------
|
|
|
|
| 823 |
/// Only non-zero source pixels are drawn (transparent = 0).
|
| 824 |
/// Pixels outside the destination clip rect are skipped.
|
| 825 |
pub fn blit_stippled(
|
| 826 |
+
dst: &mut PixelGridMut<'_>,
|
| 827 |
src: &BlitSource<'_>,
|
| 828 |
dst_x: i32,
|
| 829 |
dst_y: i32,
|
|
|
|
| 892 |
/// `flags` is the blit flags word (orientation + blend mode) passed through
|
| 893 |
/// to each individual `blit_sprite_rect` call.
|
| 894 |
pub fn blit_tiled(
|
| 895 |
+
dst: &mut PixelGridMut<'_>,
|
| 896 |
src: &BlitSource<'_>,
|
| 897 |
initial_x: i32,
|
| 898 |
dst_y: i32,
|
|
|
|
| 922 |
// Tile rightward until past clip_right
|
| 923 |
while x < clip_right {
|
| 924 |
blit_sprite_rect(
|
| 925 |
+
dst.reborrow(),
|
| 926 |
src,
|
| 927 |
x,
|
| 928 |
dst_y,
|
|
|
|
| 967 |
let mut dst = PixelGrid::new(16, 16);
|
| 968 |
|
| 969 |
blit_sprite_rect(
|
| 970 |
+
dst.as_grid_mut(),
|
| 971 |
&src,
|
| 972 |
4,
|
| 973 |
4, // dst position
|
|
|
|
| 1000 |
let mut dst_mirror = PixelGrid::new(16, 16);
|
| 1001 |
|
| 1002 |
blit_sprite_rect(
|
| 1003 |
+
dst_normal.as_grid_mut(),
|
| 1004 |
&src,
|
| 1005 |
4,
|
| 1006 |
4,
|
|
|
|
| 1013 |
BlitBlend::Copy,
|
| 1014 |
);
|
| 1015 |
blit_sprite_rect(
|
| 1016 |
+
dst_mirror.as_grid_mut(),
|
| 1017 |
&src,
|
| 1018 |
4,
|
| 1019 |
4,
|
|
|
|
| 1040 |
let mut dst = PixelGrid::new(16, 16);
|
| 1041 |
|
| 1042 |
blit_sprite_rect(
|
| 1043 |
+
dst.as_grid_mut(),
|
| 1044 |
&src,
|
| 1045 |
4,
|
| 1046 |
4,
|
|
|
|
| 1067 |
let mut dst = PixelGrid::new(16, 16);
|
| 1068 |
|
| 1069 |
blit_sprite_rect(
|
| 1070 |
+
dst.as_grid_mut(),
|
| 1071 |
&src,
|
| 1072 |
4,
|
| 1073 |
4,
|
|
|
|
| 1097 |
dst.clip_top = 8;
|
| 1098 |
|
| 1099 |
blit_sprite_rect(
|
| 1100 |
+
dst.as_grid_mut(),
|
| 1101 |
&src,
|
| 1102 |
4,
|
| 1103 |
4,
|
|
|
|
| 1133 |
table[0] = 0; // transparent stays 0
|
| 1134 |
|
| 1135 |
blit_sprite_rect(
|
| 1136 |
+
dst.as_grid_mut(),
|
| 1137 |
&src,
|
| 1138 |
4,
|
| 1139 |
4,
|
|
|
|
| 1168 |
dst.data.fill(99); // background
|
| 1169 |
|
| 1170 |
blit_sprite_rect(
|
| 1171 |
+
dst.as_grid_mut(),
|
| 1172 |
&src,
|
| 1173 |
0,
|
| 1174 |
0,
|
|
|
|
| 1204 |
lut[4] = 40;
|
| 1205 |
|
| 1206 |
blit_sprite_rect(
|
| 1207 |
+
dst.as_grid_mut(),
|
| 1208 |
&src,
|
| 1209 |
4,
|
| 1210 |
4,
|
|
|
|
| 1239 |
lut[4] = 40;
|
| 1240 |
|
| 1241 |
blit_sprite_rect(
|
| 1242 |
+
dst.as_grid_mut(),
|
| 1243 |
&src,
|
| 1244 |
4,
|
| 1245 |
4,
|
|
|
|
| 1273 |
}
|
| 1274 |
|
| 1275 |
blit_sprite_rect(
|
| 1276 |
+
dst.as_grid_mut(),
|
| 1277 |
&src,
|
| 1278 |
4,
|
| 1279 |
4,
|
|
|
|
| 1311 |
}
|
| 1312 |
|
| 1313 |
blit_sprite_rect(
|
| 1314 |
+
dst.as_grid_mut(),
|
| 1315 |
&src,
|
| 1316 |
4,
|
| 1317 |
4,
|
|
|
|
| 1358 |
dst.data.fill(99); // all non-zero
|
| 1359 |
|
| 1360 |
blit_sprite_rect(
|
| 1361 |
+
dst.as_grid_mut(),
|
| 1362 |
&src,
|
| 1363 |
0,
|
| 1364 |
0,
|
|
|
|
| 1497 |
let mut dst = PixelGrid::new(canvas_w, canvas_h);
|
| 1498 |
|
| 1499 |
blit_sprite_rect(
|
| 1500 |
+
dst.as_grid_mut(),
|
| 1501 |
&src,
|
| 1502 |
16,
|
| 1503 |
16,
|
|
|
|
| 1535 |
|
| 1536 |
let mut dst = PixelGrid::new(sprite.width, sprite.height);
|
| 1537 |
blit_sprite_rect(
|
| 1538 |
+
dst.as_grid_mut(),
|
| 1539 |
&src,
|
| 1540 |
0,
|
| 1541 |
0,
|
|
|
|
| 1567 |
// Blit at (-16, -16) so only bottom-right portion is visible
|
| 1568 |
let mut dst = PixelGrid::new(sprite.width, sprite.height);
|
| 1569 |
blit_sprite_rect(
|
| 1570 |
+
dst.as_grid_mut(),
|
| 1571 |
&src,
|
| 1572 |
-16,
|
| 1573 |
-16,
|
|
|
|
| 1600 |
dst.data.fill(42); // background color
|
| 1601 |
|
| 1602 |
blit_sprite_rect(
|
| 1603 |
+
dst.as_grid_mut(),
|
| 1604 |
&src,
|
| 1605 |
0,
|
| 1606 |
0,
|
|
|
|
| 1653 |
dst.data.fill(42);
|
| 1654 |
|
| 1655 |
blit_sprite_rect(
|
| 1656 |
+
dst.as_grid_mut(),
|
| 1657 |
&src,
|
| 1658 |
0,
|
| 1659 |
0,
|
|
|
|
| 1760 |
dst.data.fill(bg_fill);
|
| 1761 |
|
| 1762 |
blit_sprite_rect(
|
| 1763 |
+
dst.as_grid_mut(),
|
| 1764 |
&src,
|
| 1765 |
dst_x,
|
| 1766 |
dst_y,
|
|
|
|
| 2015 |
let mut dst = PixelGrid::new(canvas_w, canvas_h);
|
| 2016 |
dst.data.fill(77); // background matching DLL capture
|
| 2017 |
|
| 2018 |
+
blit_stippled(
|
| 2019 |
+
&mut dst.as_grid_mut(),
|
| 2020 |
+
&src,
|
| 2021 |
+
16,
|
| 2022 |
+
16,
|
| 2023 |
+
sw,
|
| 2024 |
+
sh,
|
| 2025 |
+
0,
|
| 2026 |
+
0,
|
| 2027 |
+
stipple_mode,
|
| 2028 |
+
parity,
|
| 2029 |
+
);
|
| 2030 |
|
| 2031 |
assert_matches_snapshot(&dst, snap_name);
|
| 2032 |
}
|
|
|
|
| 2070 |
let clip_right = canvas_w as i32;
|
| 2071 |
|
| 2072 |
blit_tiled(
|
| 2073 |
+
&mut dst.as_grid_mut(),
|
| 2074 |
+
&src,
|
| 2075 |
+
8, // initial_x matching DLL capture
|
| 2076 |
+
16,
|
| 2077 |
+
sw,
|
| 2078 |
+
sh,
|
| 2079 |
+
clip_left,
|
| 2080 |
+
clip_right,
|
| 2081 |
+
None,
|
| 2082 |
+
flags,
|
| 2083 |
);
|
| 2084 |
|
| 2085 |
assert_matches_snapshot(&dst, snap_name);
|
crates/openwa-dll/src/replacements/bitgrid.rs
CHANGED
|
@@ -854,10 +854,13 @@ unsafe extern "C" fn blit_hook_trampoline() {
|
|
| 854 |
);
|
| 855 |
}
|
| 856 |
|
| 857 |
-
/// Rust implementation of the core sprite blit.
|
| 858 |
///
|
| 859 |
-
/// Handles all
|
| 860 |
-
///
|
|
|
|
|
|
|
|
|
|
| 861 |
pub(crate) unsafe extern "cdecl" fn blit_impl(
|
| 862 |
dst: *mut openwa_core::bitgrid::DisplayBitGrid,
|
| 863 |
dst_x: i32,
|
|
@@ -870,10 +873,6 @@ pub(crate) unsafe extern "cdecl" fn blit_impl(
|
|
| 870 |
color_table: *const u8,
|
| 871 |
flags: u32,
|
| 872 |
) -> u32 {
|
| 873 |
-
use openwa_core::display::sprite_blit::{
|
| 874 |
-
blit_sprite_rect, BlitBlend, BlitOrientation, BlitSource,
|
| 875 |
-
};
|
| 876 |
-
|
| 877 |
if width == 0 || height == 0 {
|
| 878 |
return 0;
|
| 879 |
}
|
|
@@ -882,9 +881,9 @@ pub(crate) unsafe extern "cdecl" fn blit_impl(
|
|
| 882 |
let src_cpp = (*src).cells_per_unit;
|
| 883 |
let dst_cpp = (*dst).cells_per_unit;
|
| 884 |
|
| 885 |
-
//
|
| 886 |
-
if dst_cpp
|
| 887 |
-
return
|
| 888 |
dst,
|
| 889 |
dst_x,
|
| 890 |
dst_y,
|
|
@@ -898,7 +897,196 @@ pub(crate) unsafe extern "cdecl" fn blit_impl(
|
|
| 898 |
);
|
| 899 |
}
|
| 900 |
|
| 901 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 902 |
let color_table_ref: Option<&[u8; 256]> = if !color_table.is_null() {
|
| 903 |
Some(&*(color_table as *const [u8; 256]))
|
| 904 |
} else {
|
|
@@ -908,7 +1096,6 @@ pub(crate) unsafe extern "cdecl" fn blit_impl(
|
|
| 908 |
let orientation = BlitOrientation::from_flags(flags);
|
| 909 |
let blend = BlitBlend::from_flags(flags);
|
| 910 |
|
| 911 |
-
// Build source view
|
| 912 |
let src_data =
|
| 913 |
core::slice::from_raw_parts((*src).data, ((*src).row_stride * (*src).height) as usize);
|
| 914 |
let blit_src = BlitSource {
|
|
@@ -918,29 +1105,20 @@ pub(crate) unsafe extern "cdecl" fn blit_impl(
|
|
| 918 |
row_stride: (*src).row_stride,
|
| 919 |
};
|
| 920 |
|
| 921 |
-
|
| 922 |
-
let
|
| 923 |
-
|
| 924 |
-
|
| 925 |
-
|
| 926 |
-
|
| 927 |
-
// Create a temporary PixelGrid wrapping the destination BitGrid's memory
|
| 928 |
-
let mut dst_grid = openwa_core::display::line_draw::PixelGrid {
|
| 929 |
-
data: Vec::new(), // placeholder — we'll swap in the real data
|
| 930 |
-
width: dst_w,
|
| 931 |
-
height: dst_h,
|
| 932 |
-
row_stride: dst_stride,
|
| 933 |
clip_left: (*dst).clip_left,
|
| 934 |
clip_top: (*dst).clip_top,
|
| 935 |
clip_right: (*dst).clip_right,
|
| 936 |
clip_bottom: (*dst).clip_bottom,
|
| 937 |
};
|
| 938 |
-
// Swap the real data in (avoids allocation)
|
| 939 |
-
let mut real_data = Vec::from_raw_parts((*dst).data, dst_data_len, dst_data_len);
|
| 940 |
-
core::mem::swap(&mut dst_grid.data, &mut real_data);
|
| 941 |
|
| 942 |
-
|
| 943 |
-
|
| 944 |
&blit_src,
|
| 945 |
dst_x,
|
| 946 |
dst_y,
|
|
@@ -951,13 +1129,7 @@ pub(crate) unsafe extern "cdecl" fn blit_impl(
|
|
| 951 |
color_table_ref,
|
| 952 |
orientation,
|
| 953 |
blend,
|
| 954 |
-
)
|
| 955 |
-
|
| 956 |
-
// Swap data back — we must NOT let Vec drop the BitGrid's data
|
| 957 |
-
core::mem::swap(&mut dst_grid.data, &mut real_data);
|
| 958 |
-
core::mem::forget(real_data); // BitGrid owns this memory
|
| 959 |
-
|
| 960 |
-
result as u32
|
| 961 |
}
|
| 962 |
|
| 963 |
/// Stippled blit on raw BitGrid pointers.
|
|
@@ -987,23 +1159,17 @@ pub(crate) unsafe fn blit_stippled_raw(
|
|
| 987 |
row_stride: (*src).row_stride,
|
| 988 |
};
|
| 989 |
|
| 990 |
-
let
|
| 991 |
-
let
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
|
| 995 |
-
|
| 996 |
-
data: Vec::new(),
|
| 997 |
-
width: dst_w,
|
| 998 |
-
height: dst_h,
|
| 999 |
-
row_stride: dst_stride,
|
| 1000 |
clip_left: (*dst).clip_left,
|
| 1001 |
clip_top: (*dst).clip_top,
|
| 1002 |
clip_right: (*dst).clip_right,
|
| 1003 |
clip_bottom: (*dst).clip_bottom,
|
| 1004 |
};
|
| 1005 |
-
let mut real_data = Vec::from_raw_parts((*dst).data, dst_data_len, dst_data_len);
|
| 1006 |
-
core::mem::swap(&mut dst_grid.data, &mut real_data);
|
| 1007 |
|
| 1008 |
blit_stippled(
|
| 1009 |
&mut dst_grid,
|
|
@@ -1017,9 +1183,6 @@ pub(crate) unsafe fn blit_stippled_raw(
|
|
| 1017 |
stipple_mode,
|
| 1018 |
parity,
|
| 1019 |
);
|
| 1020 |
-
|
| 1021 |
-
core::mem::swap(&mut dst_grid.data, &mut real_data);
|
| 1022 |
-
core::mem::forget(real_data);
|
| 1023 |
}
|
| 1024 |
|
| 1025 |
/// Tiled blit on raw BitGrid pointers.
|
|
@@ -1048,23 +1211,17 @@ pub(crate) unsafe fn blit_tiled_raw(
|
|
| 1048 |
row_stride: (*src).row_stride,
|
| 1049 |
};
|
| 1050 |
|
| 1051 |
-
let
|
| 1052 |
-
let
|
| 1053 |
-
|
| 1054 |
-
|
| 1055 |
-
|
| 1056 |
-
|
| 1057 |
-
data: Vec::new(),
|
| 1058 |
-
width: dst_w,
|
| 1059 |
-
height: dst_h,
|
| 1060 |
-
row_stride: dst_stride,
|
| 1061 |
clip_left: (*dst).clip_left,
|
| 1062 |
clip_top: (*dst).clip_top,
|
| 1063 |
clip_right: (*dst).clip_right,
|
| 1064 |
clip_bottom: (*dst).clip_bottom,
|
| 1065 |
};
|
| 1066 |
-
let mut real_data = Vec::from_raw_parts((*dst).data, dst_data_len, dst_data_len);
|
| 1067 |
-
core::mem::swap(&mut dst_grid.data, &mut real_data);
|
| 1068 |
|
| 1069 |
blit_tiled(
|
| 1070 |
&mut dst_grid,
|
|
@@ -1078,12 +1235,11 @@ pub(crate) unsafe fn blit_tiled_raw(
|
|
| 1078 |
None, // color_table — tiled mode doesn't use one
|
| 1079 |
flags,
|
| 1080 |
);
|
| 1081 |
-
|
| 1082 |
-
core::mem::swap(&mut dst_grid.data, &mut real_data);
|
| 1083 |
-
core::mem::forget(real_data);
|
| 1084 |
}
|
| 1085 |
|
| 1086 |
/// Call the original WA blit function via trampoline.
|
|
|
|
|
|
|
| 1087 |
unsafe fn call_original_blit(
|
| 1088 |
dst: *mut openwa_core::bitgrid::DisplayBitGrid,
|
| 1089 |
dst_x: i32,
|
|
@@ -1113,6 +1269,7 @@ unsafe fn call_original_blit(
|
|
| 1113 |
}
|
| 1114 |
|
| 1115 |
/// Naked asm to call the original blit with ESI set correctly.
|
|
|
|
| 1116 |
#[unsafe(naked)]
|
| 1117 |
unsafe extern "cdecl" fn call_original_blit_asm(
|
| 1118 |
_dst: *mut openwa_core::bitgrid::DisplayBitGrid,
|
|
|
|
| 854 |
);
|
| 855 |
}
|
| 856 |
|
| 857 |
+
/// Rust implementation of the core sprite blit (BitGrid__BlitSpriteRect, 0x4F6910).
|
| 858 |
///
|
| 859 |
+
/// Handles all blend modes for both 8bpp and 1-bit surfaces:
|
| 860 |
+
/// - 8bpp modes 0-3: via `blit_sprite_rect` (orientation-aware, PixelGrid wrapper)
|
| 861 |
+
/// - 1-bit byte-aligned fast path: memcpy/OR/nop for modes 0-3 with Normal orientation
|
| 862 |
+
/// - Generic per-pixel fallback: handles all remaining cases (unaligned 1-bit, modes 4-5,
|
| 863 |
+
/// mixed cpp, etc.) via `blit_generic_perpixel`
|
| 864 |
pub(crate) unsafe extern "cdecl" fn blit_impl(
|
| 865 |
dst: *mut openwa_core::bitgrid::DisplayBitGrid,
|
| 866 |
dst_x: i32,
|
|
|
|
| 873 |
color_table: *const u8,
|
| 874 |
flags: u32,
|
| 875 |
) -> u32 {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 876 |
if width == 0 || height == 0 {
|
| 877 |
return 0;
|
| 878 |
}
|
|
|
|
| 881 |
let src_cpp = (*src).cells_per_unit;
|
| 882 |
let dst_cpp = (*dst).cells_per_unit;
|
| 883 |
|
| 884 |
+
// Fast path: 8bpp surfaces with blend modes 0-3
|
| 885 |
+
if dst_cpp == 8 && src_cpp == 8 && blend_mode <= 3 {
|
| 886 |
+
return blit_8bpp(
|
| 887 |
dst,
|
| 888 |
dst_x,
|
| 889 |
dst_y,
|
|
|
|
| 897 |
);
|
| 898 |
}
|
| 899 |
|
| 900 |
+
// --- Clipping (shared by 1-bit fast path and generic fallback) ---
|
| 901 |
+
// This mirrors the clipping logic at the top of BitGrid__BlitSpriteRect.
|
| 902 |
+
let dst_right = dst_x + width;
|
| 903 |
+
let dst_bottom = dst_y + height;
|
| 904 |
+
|
| 905 |
+
let clip_left = (*dst).clip_left as i32;
|
| 906 |
+
let clip_top = (*dst).clip_top as i32;
|
| 907 |
+
let clip_right = (*dst).clip_right as i32;
|
| 908 |
+
let clip_bottom = (*dst).clip_bottom as i32;
|
| 909 |
+
|
| 910 |
+
// Early-out: completely outside clip rect
|
| 911 |
+
if dst_x >= clip_right
|
| 912 |
+
|| dst_right <= clip_left
|
| 913 |
+
|| dst_y >= clip_bottom
|
| 914 |
+
|| dst_bottom <= clip_top
|
| 915 |
+
{
|
| 916 |
+
return 0;
|
| 917 |
+
}
|
| 918 |
+
|
| 919 |
+
// Clamp visible region to clip rect
|
| 920 |
+
let vis_left = dst_x.max(clip_left);
|
| 921 |
+
let vis_right = dst_right.min(clip_right);
|
| 922 |
+
let vis_top = dst_y.max(clip_top);
|
| 923 |
+
let vis_bottom = dst_bottom.min(clip_bottom);
|
| 924 |
+
|
| 925 |
+
if vis_left >= vis_right || vis_top >= vis_bottom {
|
| 926 |
+
return 0;
|
| 927 |
+
}
|
| 928 |
+
|
| 929 |
+
// Adjust source coordinates for clipping and orientation.
|
| 930 |
+
// The orientation switch in the original adjusts src_x/src_y before the cpp check.
|
| 931 |
+
let orientation_code = (flags >> 16) & 0xFFFF;
|
| 932 |
+
let (adj_src_x, adj_src_y) = adjust_src_for_orientation(
|
| 933 |
+
src_x,
|
| 934 |
+
src_y,
|
| 935 |
+
dst_x,
|
| 936 |
+
dst_y,
|
| 937 |
+
width,
|
| 938 |
+
height,
|
| 939 |
+
vis_left,
|
| 940 |
+
vis_top,
|
| 941 |
+
vis_right,
|
| 942 |
+
vis_bottom,
|
| 943 |
+
orientation_code,
|
| 944 |
+
);
|
| 945 |
+
|
| 946 |
+
// Build slices from raw BitGrid data pointers
|
| 947 |
+
let dst_data =
|
| 948 |
+
core::slice::from_raw_parts_mut((*dst).data, ((*dst).row_stride * (*dst).height) as usize);
|
| 949 |
+
let src_data =
|
| 950 |
+
core::slice::from_raw_parts((*src).data, ((*src).row_stride * (*src).height) as usize);
|
| 951 |
+
|
| 952 |
+
// 1-bit byte-aligned fast path
|
| 953 |
+
if dst_cpp == 1 && src_cpp == 1 {
|
| 954 |
+
let left_aligned = (vis_left & 7) == 0;
|
| 955 |
+
let right_aligned = (vis_right & 7) == 0;
|
| 956 |
+
let src_aligned = (adj_src_x & 7) == 0;
|
| 957 |
+
|
| 958 |
+
if left_aligned && right_aligned && src_aligned && color_table.is_null() && flags < 4 {
|
| 959 |
+
openwa_core::display::sprite_blit::blit_1bit_aligned(
|
| 960 |
+
dst_data,
|
| 961 |
+
(*dst).row_stride,
|
| 962 |
+
src_data,
|
| 963 |
+
(*src).row_stride,
|
| 964 |
+
vis_left,
|
| 965 |
+
vis_top,
|
| 966 |
+
vis_right,
|
| 967 |
+
vis_bottom,
|
| 968 |
+
adj_src_x,
|
| 969 |
+
adj_src_y,
|
| 970 |
+
blend_mode,
|
| 971 |
+
);
|
| 972 |
+
return 1;
|
| 973 |
+
}
|
| 974 |
+
}
|
| 975 |
+
|
| 976 |
+
// Convert color_table pointer to typed reference for the generic path
|
| 977 |
+
let color_table_ref: Option<&[u8; 256]> = if !color_table.is_null() {
|
| 978 |
+
Some(&*(color_table as *const [u8; 256]))
|
| 979 |
+
} else {
|
| 980 |
+
None
|
| 981 |
+
};
|
| 982 |
+
|
| 983 |
+
// Generic per-pixel fallback — handles all remaining cases
|
| 984 |
+
openwa_core::display::sprite_blit::blit_generic_perpixel(
|
| 985 |
+
dst_data,
|
| 986 |
+
(*dst).row_stride,
|
| 987 |
+
dst_cpp,
|
| 988 |
+
src_data,
|
| 989 |
+
(*src).row_stride,
|
| 990 |
+
src_cpp,
|
| 991 |
+
vis_left,
|
| 992 |
+
vis_top,
|
| 993 |
+
vis_right,
|
| 994 |
+
vis_bottom,
|
| 995 |
+
adj_src_x,
|
| 996 |
+
adj_src_y,
|
| 997 |
+
color_table_ref,
|
| 998 |
+
blend_mode,
|
| 999 |
+
)
|
| 1000 |
+
}
|
| 1001 |
+
|
| 1002 |
+
/// Adjust source coordinates for clipping and orientation.
|
| 1003 |
+
///
|
| 1004 |
+
/// Port of the orientation switch in BitGrid__BlitSpriteRect (0x4F69D3..0x4F6A9D).
|
| 1005 |
+
/// Maps the 16 orientation codes (0-15) to 8 unique source adjustments.
|
| 1006 |
+
fn adjust_src_for_orientation(
|
| 1007 |
+
src_x: i32,
|
| 1008 |
+
src_y: i32,
|
| 1009 |
+
dst_x: i32,
|
| 1010 |
+
dst_y: i32,
|
| 1011 |
+
width: i32,
|
| 1012 |
+
height: i32,
|
| 1013 |
+
vis_left: i32,
|
| 1014 |
+
vis_top: i32,
|
| 1015 |
+
vis_right: i32,
|
| 1016 |
+
vis_bottom: i32,
|
| 1017 |
+
orientation: u32,
|
| 1018 |
+
) -> (i32, i32) {
|
| 1019 |
+
match orientation {
|
| 1020 |
+
1 | 10 => {
|
| 1021 |
+
// MirrorX: src_x from right side, src_y from top
|
| 1022 |
+
let sx = src_x + (dst_x - vis_right) + width;
|
| 1023 |
+
let sy = src_y + (vis_top - dst_y);
|
| 1024 |
+
(sx, sy)
|
| 1025 |
+
}
|
| 1026 |
+
2 | 9 => {
|
| 1027 |
+
// MirrorY: src_x from left, src_y from bottom
|
| 1028 |
+
let sx = src_x + (vis_left - dst_x);
|
| 1029 |
+
let sy = src_y + (dst_y - vis_bottom) + height;
|
| 1030 |
+
(sx, sy)
|
| 1031 |
+
}
|
| 1032 |
+
3 | 8 => {
|
| 1033 |
+
// MirrorXY: src_x from right, src_y from bottom
|
| 1034 |
+
let sx = src_x + (dst_x - vis_right) + width;
|
| 1035 |
+
let sy = src_y + (dst_y - vis_bottom) + height;
|
| 1036 |
+
(sx, sy)
|
| 1037 |
+
}
|
| 1038 |
+
4 | 15 => {
|
| 1039 |
+
// Rotate90: src_x tracks dst_y from bottom, src_y tracks dst_x from left
|
| 1040 |
+
let sx = src_x + (dst_y - vis_bottom) + height;
|
| 1041 |
+
let sy = src_y + (vis_left - dst_x);
|
| 1042 |
+
(sx, sy)
|
| 1043 |
+
}
|
| 1044 |
+
5 | 14 => {
|
| 1045 |
+
// Rotate90+MirrorX
|
| 1046 |
+
let sx = src_x + (dst_y - vis_bottom) + height;
|
| 1047 |
+
let sy = src_y + (dst_x - vis_right) + width;
|
| 1048 |
+
(sx, sy)
|
| 1049 |
+
}
|
| 1050 |
+
6 | 13 => {
|
| 1051 |
+
// Rotate90+MirrorY
|
| 1052 |
+
let sx = src_x + (vis_top - dst_y);
|
| 1053 |
+
let sy = src_y + (vis_left - dst_x);
|
| 1054 |
+
(sx, sy)
|
| 1055 |
+
}
|
| 1056 |
+
7 | 12 => {
|
| 1057 |
+
// Rotate90+MirrorXY
|
| 1058 |
+
let sx = src_x + (vis_top - dst_y);
|
| 1059 |
+
let sy = src_y + (dst_x - vis_right) + width;
|
| 1060 |
+
(sx, sy)
|
| 1061 |
+
}
|
| 1062 |
+
_ => {
|
| 1063 |
+
// Normal (0, 11, or any default)
|
| 1064 |
+
let sx = src_x + (vis_left - dst_x);
|
| 1065 |
+
let sy = src_y + (vis_top - dst_y);
|
| 1066 |
+
(sx, sy)
|
| 1067 |
+
}
|
| 1068 |
+
}
|
| 1069 |
+
}
|
| 1070 |
+
|
| 1071 |
+
/// 8bpp blit path — extracted from the original blit_impl for clarity.
|
| 1072 |
+
///
|
| 1073 |
+
/// Handles blend modes 0-3 on 8bpp surfaces via PixelGrid wrapper.
|
| 1074 |
+
unsafe fn blit_8bpp(
|
| 1075 |
+
dst: *mut openwa_core::bitgrid::DisplayBitGrid,
|
| 1076 |
+
dst_x: i32,
|
| 1077 |
+
dst_y: i32,
|
| 1078 |
+
width: i32,
|
| 1079 |
+
height: i32,
|
| 1080 |
+
src: *mut openwa_core::bitgrid::DisplayBitGrid,
|
| 1081 |
+
src_x: i32,
|
| 1082 |
+
src_y: i32,
|
| 1083 |
+
color_table: *const u8,
|
| 1084 |
+
flags: u32,
|
| 1085 |
+
) -> u32 {
|
| 1086 |
+
use openwa_core::display::sprite_blit::{
|
| 1087 |
+
blit_sprite_rect, BlitBlend, BlitOrientation, BlitSource,
|
| 1088 |
+
};
|
| 1089 |
+
|
| 1090 |
let color_table_ref: Option<&[u8; 256]> = if !color_table.is_null() {
|
| 1091 |
Some(&*(color_table as *const [u8; 256]))
|
| 1092 |
} else {
|
|
|
|
| 1096 |
let orientation = BlitOrientation::from_flags(flags);
|
| 1097 |
let blend = BlitBlend::from_flags(flags);
|
| 1098 |
|
|
|
|
| 1099 |
let src_data =
|
| 1100 |
core::slice::from_raw_parts((*src).data, ((*src).row_stride * (*src).height) as usize);
|
| 1101 |
let blit_src = BlitSource {
|
|
|
|
| 1105 |
row_stride: (*src).row_stride,
|
| 1106 |
};
|
| 1107 |
|
| 1108 |
+
let dst_data_len = ((*dst).row_stride * (*dst).height) as usize;
|
| 1109 |
+
let dst_grid = openwa_core::display::line_draw::PixelGridMut {
|
| 1110 |
+
data: core::slice::from_raw_parts_mut((*dst).data, dst_data_len),
|
| 1111 |
+
width: (*dst).width,
|
| 1112 |
+
height: (*dst).height,
|
| 1113 |
+
row_stride: (*dst).row_stride,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1114 |
clip_left: (*dst).clip_left,
|
| 1115 |
clip_top: (*dst).clip_top,
|
| 1116 |
clip_right: (*dst).clip_right,
|
| 1117 |
clip_bottom: (*dst).clip_bottom,
|
| 1118 |
};
|
|
|
|
|
|
|
|
|
|
| 1119 |
|
| 1120 |
+
blit_sprite_rect(
|
| 1121 |
+
dst_grid,
|
| 1122 |
&blit_src,
|
| 1123 |
dst_x,
|
| 1124 |
dst_y,
|
|
|
|
| 1129 |
color_table_ref,
|
| 1130 |
orientation,
|
| 1131 |
blend,
|
| 1132 |
+
) as u32
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1133 |
}
|
| 1134 |
|
| 1135 |
/// Stippled blit on raw BitGrid pointers.
|
|
|
|
| 1159 |
row_stride: (*src).row_stride,
|
| 1160 |
};
|
| 1161 |
|
| 1162 |
+
let dst_data_len = ((*dst).row_stride * (*dst).height) as usize;
|
| 1163 |
+
let mut dst_grid = openwa_core::display::line_draw::PixelGridMut {
|
| 1164 |
+
data: core::slice::from_raw_parts_mut((*dst).data, dst_data_len),
|
| 1165 |
+
width: (*dst).width,
|
| 1166 |
+
height: (*dst).height,
|
| 1167 |
+
row_stride: (*dst).row_stride,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1168 |
clip_left: (*dst).clip_left,
|
| 1169 |
clip_top: (*dst).clip_top,
|
| 1170 |
clip_right: (*dst).clip_right,
|
| 1171 |
clip_bottom: (*dst).clip_bottom,
|
| 1172 |
};
|
|
|
|
|
|
|
| 1173 |
|
| 1174 |
blit_stippled(
|
| 1175 |
&mut dst_grid,
|
|
|
|
| 1183 |
stipple_mode,
|
| 1184 |
parity,
|
| 1185 |
);
|
|
|
|
|
|
|
|
|
|
| 1186 |
}
|
| 1187 |
|
| 1188 |
/// Tiled blit on raw BitGrid pointers.
|
|
|
|
| 1211 |
row_stride: (*src).row_stride,
|
| 1212 |
};
|
| 1213 |
|
| 1214 |
+
let dst_data_len = ((*dst).row_stride * (*dst).height) as usize;
|
| 1215 |
+
let mut dst_grid = openwa_core::display::line_draw::PixelGridMut {
|
| 1216 |
+
data: core::slice::from_raw_parts_mut((*dst).data, dst_data_len),
|
| 1217 |
+
width: (*dst).width,
|
| 1218 |
+
height: (*dst).height,
|
| 1219 |
+
row_stride: (*dst).row_stride,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1220 |
clip_left: (*dst).clip_left,
|
| 1221 |
clip_top: (*dst).clip_top,
|
| 1222 |
clip_right: (*dst).clip_right,
|
| 1223 |
clip_bottom: (*dst).clip_bottom,
|
| 1224 |
};
|
|
|
|
|
|
|
| 1225 |
|
| 1226 |
blit_tiled(
|
| 1227 |
&mut dst_grid,
|
|
|
|
| 1235 |
None, // color_table — tiled mode doesn't use one
|
| 1236 |
flags,
|
| 1237 |
);
|
|
|
|
|
|
|
|
|
|
| 1238 |
}
|
| 1239 |
|
| 1240 |
/// Call the original WA blit function via trampoline.
|
| 1241 |
+
/// No longer used — kept for debugging if fallthrough is temporarily needed.
|
| 1242 |
+
#[allow(dead_code)]
|
| 1243 |
unsafe fn call_original_blit(
|
| 1244 |
dst: *mut openwa_core::bitgrid::DisplayBitGrid,
|
| 1245 |
dst_x: i32,
|
|
|
|
| 1269 |
}
|
| 1270 |
|
| 1271 |
/// Naked asm to call the original blit with ESI set correctly.
|
| 1272 |
+
#[allow(dead_code)]
|
| 1273 |
#[unsafe(naked)]
|
| 1274 |
unsafe extern "cdecl" fn call_original_blit_asm(
|
| 1275 |
_dst: *mut openwa_core::bitgrid::DisplayBitGrid,
|