paavohtl Claude Opus 4.6 (1M context) commited on
Commit
cd82e9c
·
1 Parent(s): 929bb6a

feat: complete 8bpp blit — color-table LUT, additive & subtractive modes

Browse files

Port all remaining 8bpp blend modes in the core sprite blit hook:
- Mode 1 with non-null color_table pointer now passes the LUT through
instead of falling back to native WA
- Mode 2 (additive mask): write src where both src and dst are non-zero
- Mode 3 (subtractive mask): write src where src non-zero and dst is zero
- Update color_table parameter type from u32 to *const u8 (FFI style)
- Snapshot capture now uses ORIG_BLIT to call unhooked native function

34 tests (up from 25): 5 new unit tests + 4 new snapshot tests verified
byte-exact against native WA output.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-core/src/display/sprite_blit.rs CHANGED
@@ -98,9 +98,11 @@ pub enum BlitBlend {
98
  /// Color-table blend — each source pixel is remapped through a 256-byte LUT.
99
  /// If `color_table` is None, uses direct copy with transparency.
100
  ColorTable,
101
- /// Additive color mix — source pixel index used as a fixed-point X scale.
 
102
  Additive,
103
- /// Subtractive color mix.
 
104
  Subtractive,
105
  }
106
 
@@ -237,10 +239,64 @@ pub fn blit_sprite_rect(
237
  }
238
  }
239
  BlitBlend::Additive => {
240
- // TODO: port additive blend
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  }
242
  BlitBlend::Subtractive => {
243
- // TODO: port subtractive blend
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  }
245
  }
246
 
@@ -434,6 +490,43 @@ fn blit_color_table_swapped(
434
  }
435
  }
436
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  // ---------------------------------------------------------------------------
438
  // Test sprite creation from images
439
  // ---------------------------------------------------------------------------
@@ -708,6 +801,198 @@ mod tests {
708
  assert_eq!(p(4, 3), 99, "transparent pixel preserved background");
709
  }
710
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
711
  // =======================================================================
712
  // Image loading helpers (for snapshot tests with real images)
713
  // =======================================================================
@@ -1269,4 +1554,60 @@ mod tests {
1269
  77,
1270
  );
1271
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1272
  }
 
98
  /// Color-table blend — each source pixel is remapped through a 256-byte LUT.
99
  /// If `color_table` is None, uses direct copy with transparency.
100
  ColorTable,
101
+ /// Additive mask — copy source pixel only where both src and dst are non-zero.
102
+ /// Used for overlay effects (paint only on existing content).
103
  Additive,
104
+ /// Subtractive mask — copy source pixel only where src is non-zero and dst is zero.
105
+ /// Used for gap-fill effects (paint only where destination is empty).
106
  Subtractive,
107
  }
108
 
 
239
  }
240
  }
241
  BlitBlend::Additive => {
242
+ // WA's mode 2: forward scan only (orientation ignored in inner loop).
243
+ let (sx_start, sy_start) = adjust_source_for_clip(
244
+ BlitOrientation::Normal,
245
+ src_x,
246
+ src_y,
247
+ dst_x,
248
+ dst_y,
249
+ width,
250
+ height,
251
+ vis_left,
252
+ vis_top,
253
+ vis_right,
254
+ vis_bottom,
255
+ );
256
+ blit_masked(
257
+ dst,
258
+ src,
259
+ vis_left,
260
+ vis_top,
261
+ vis_w,
262
+ vis_h,
263
+ sx_start,
264
+ sy_start,
265
+ |s, d| {
266
+ // Write src where both src and dst are non-zero
267
+ s != 0 && d != 0
268
+ },
269
+ );
270
  }
271
  BlitBlend::Subtractive => {
272
+ // WA's mode 3: forward scan only (orientation ignored in inner loop).
273
+ let (sx_start, sy_start) = adjust_source_for_clip(
274
+ BlitOrientation::Normal,
275
+ src_x,
276
+ src_y,
277
+ dst_x,
278
+ dst_y,
279
+ width,
280
+ height,
281
+ vis_left,
282
+ vis_top,
283
+ vis_right,
284
+ vis_bottom,
285
+ );
286
+ blit_masked(
287
+ dst,
288
+ src,
289
+ vis_left,
290
+ vis_top,
291
+ vis_w,
292
+ vis_h,
293
+ sx_start,
294
+ sy_start,
295
+ |s, d| {
296
+ // Write src where src is non-zero and dst is zero
297
+ s != 0 && d == 0
298
+ },
299
+ );
300
  }
301
  }
302
 
 
490
  }
491
  }
492
 
493
+ /// Masked blit — forward scan only (no orientation).
494
+ ///
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 PixelGrid,
499
+ src: &BlitSource,
500
+ vis_left: i32,
501
+ vis_top: i32,
502
+ vis_w: i32,
503
+ vis_h: i32,
504
+ sx_start: i32,
505
+ sy_start: i32,
506
+ should_write: impl Fn(u8, u8) -> bool,
507
+ ) {
508
+ let dst_stride = dst.row_stride as usize;
509
+ let src_stride = src.row_stride as usize;
510
+
511
+ let mut sy = sy_start;
512
+ for dy in 0..vis_h {
513
+ let dst_row = (vis_top + dy) as usize * dst_stride + vis_left as usize;
514
+ let src_row = sy as usize * src_stride;
515
+
516
+ let mut sx = sx_start;
517
+ for dx in 0..vis_w {
518
+ let src_pixel = src.data[src_row + sx as usize];
519
+ let dst_pixel = dst.data[dst_row + dx as usize];
520
+ if should_write(src_pixel, dst_pixel) {
521
+ dst.data[dst_row + dx as usize] = src_pixel;
522
+ }
523
+ sx += 1;
524
+ }
525
+
526
+ sy += 1;
527
+ }
528
+ }
529
+
530
  // ---------------------------------------------------------------------------
531
  // Test sprite creation from images
532
  // ---------------------------------------------------------------------------
 
801
  assert_eq!(p(4, 3), 99, "transparent pixel preserved background");
802
  }
803
 
804
+ #[test]
805
+ fn blit_color_table_lut_remaps_pixels() {
806
+ // Verify that a non-identity LUT actually remaps pixel values.
807
+ let sprite = make_test_sprite(); // quadrants: TL=1, TR=2, BL=3, BR=4
808
+ let src = BlitSource::from(&sprite);
809
+ let mut dst = PixelGrid::new(16, 16);
810
+ dst.data.fill(99); // background
811
+
812
+ // LUT: remap 1->10, 2->20, 3->30, 4->40, 0->0 (transparent)
813
+ let mut lut = [0u8; 256];
814
+ lut[1] = 10;
815
+ lut[2] = 20;
816
+ lut[3] = 30;
817
+ lut[4] = 40;
818
+
819
+ blit_sprite_rect(
820
+ &mut dst,
821
+ &src,
822
+ 4,
823
+ 4,
824
+ 8,
825
+ 8,
826
+ 0,
827
+ 0,
828
+ Some(&lut),
829
+ BlitOrientation::Normal,
830
+ BlitBlend::ColorTable,
831
+ );
832
+
833
+ let p = |x: i32, y: i32| dst.data[(y as u32 * dst.row_stride + x as u32) as usize];
834
+ assert_eq!(p(4, 4), 10, "TL remapped 1->10");
835
+ assert_eq!(p(8, 4), 20, "TR remapped 2->20");
836
+ assert_eq!(p(4, 8), 30, "BL remapped 3->30");
837
+ assert_eq!(p(8, 8), 40, "BR remapped 4->40");
838
+ // Background outside sprite preserved
839
+ assert_eq!(p(3, 4), 99, "background unchanged");
840
+ }
841
+
842
+ #[test]
843
+ fn blit_color_table_lut_with_mirror() {
844
+ let sprite = make_test_sprite();
845
+ let src = BlitSource::from(&sprite);
846
+ let mut dst = PixelGrid::new(16, 16);
847
+
848
+ let mut lut = [0u8; 256];
849
+ lut[1] = 10;
850
+ lut[2] = 20;
851
+ lut[3] = 30;
852
+ lut[4] = 40;
853
+
854
+ blit_sprite_rect(
855
+ &mut dst,
856
+ &src,
857
+ 4,
858
+ 4,
859
+ 8,
860
+ 8,
861
+ 0,
862
+ 0,
863
+ Some(&lut),
864
+ BlitOrientation::MirrorX,
865
+ BlitBlend::ColorTable,
866
+ );
867
+
868
+ let p = |x: i32, y: i32| dst.data[(y as u32 * dst.row_stride + x as u32) as usize];
869
+ // MirrorX flips left/right: TL<->TR, BL<->BR
870
+ assert_eq!(p(4, 4), 20, "mirror: TR->TL remapped 2->20");
871
+ assert_eq!(p(8, 4), 10, "mirror: TL->TR remapped 1->10");
872
+ assert_eq!(p(4, 8), 40, "mirror: BR->BL remapped 4->40");
873
+ assert_eq!(p(8, 8), 30, "mirror: BL->BR remapped 3->30");
874
+ }
875
+
876
+ #[test]
877
+ fn blit_additive_writes_only_where_both_nonzero() {
878
+ let sprite = make_test_sprite(); // all pixels 1-4 (non-zero)
879
+ let src = BlitSource::from(&sprite);
880
+ let mut dst = PixelGrid::new(16, 16);
881
+ // Fill only the right half of the destination with non-zero
882
+ for y in 0..16u32 {
883
+ for x in 8..16u32 {
884
+ dst.data[(y * dst.row_stride + x) as usize] = 99;
885
+ }
886
+ }
887
+
888
+ blit_sprite_rect(
889
+ &mut dst,
890
+ &src,
891
+ 4,
892
+ 4,
893
+ 8,
894
+ 8,
895
+ 0,
896
+ 0,
897
+ None,
898
+ BlitOrientation::Normal,
899
+ BlitBlend::Additive,
900
+ );
901
+
902
+ let p = |x: i32, y: i32| dst.data[(y as u32 * dst.row_stride + x as u32) as usize];
903
+ // Left half of sprite (dst_x 4-7): dst was 0, so additive skips
904
+ assert_eq!(p(4, 4), 0, "dst was 0, additive should not write");
905
+ assert_eq!(p(7, 7), 0, "dst was 0, additive should not write");
906
+ // Right half of sprite (dst_x 8-11): dst was 99, so additive writes src
907
+ assert_eq!(p(8, 4), 2, "dst was non-zero, additive writes src (TR)");
908
+ assert_eq!(p(8, 8), 4, "dst was non-zero, additive writes src (BR)");
909
+ // Outside sprite: unchanged
910
+ assert_eq!(p(12, 4), 99, "outside sprite, right half preserved");
911
+ assert_eq!(p(3, 4), 0, "outside sprite, left half preserved");
912
+ }
913
+
914
+ #[test]
915
+ fn blit_subtractive_writes_only_where_dst_zero() {
916
+ let sprite = make_test_sprite(); // all pixels 1-4 (non-zero)
917
+ let src = BlitSource::from(&sprite);
918
+ let mut dst = PixelGrid::new(16, 16);
919
+ // Fill only the right half with non-zero
920
+ for y in 0..16u32 {
921
+ for x in 8..16u32 {
922
+ dst.data[(y * dst.row_stride + x) as usize] = 99;
923
+ }
924
+ }
925
+
926
+ blit_sprite_rect(
927
+ &mut dst,
928
+ &src,
929
+ 4,
930
+ 4,
931
+ 8,
932
+ 8,
933
+ 0,
934
+ 0,
935
+ None,
936
+ BlitOrientation::Normal,
937
+ BlitBlend::Subtractive,
938
+ );
939
+
940
+ let p = |x: i32, y: i32| dst.data[(y as u32 * dst.row_stride + x as u32) as usize];
941
+ // Left half of sprite (dst_x 4-7): dst was 0, so subtractive writes
942
+ assert_eq!(p(4, 4), 1, "dst was 0, subtractive writes src pixel");
943
+ assert_eq!(
944
+ p(7, 7),
945
+ 1,
946
+ "dst was 0, subtractive writes src (TL quadrant)"
947
+ );
948
+ // Right half of sprite (dst_x 8-11): dst was 99, so subtractive skips
949
+ assert_eq!(
950
+ p(8, 4),
951
+ 99,
952
+ "dst was non-zero, subtractive should not write"
953
+ );
954
+ assert_eq!(
955
+ p(8, 8),
956
+ 99,
957
+ "dst was non-zero, subtractive should not write"
958
+ );
959
+ }
960
+
961
+ #[test]
962
+ fn blit_additive_skips_transparent_src() {
963
+ // Sprite with transparent (0) pixels should never write them
964
+ let mut pixels = vec![0u8; 8 * 8];
965
+ for i in 0..8 {
966
+ pixels[i * 8 + i] = 5; // diagonal only
967
+ }
968
+ let sprite = pixel_grid_from_indexed(8, 8, &pixels);
969
+ let src = BlitSource::from(&sprite);
970
+ let mut dst = PixelGrid::new(8, 8);
971
+ dst.data.fill(99); // all non-zero
972
+
973
+ blit_sprite_rect(
974
+ &mut dst,
975
+ &src,
976
+ 0,
977
+ 0,
978
+ 8,
979
+ 8,
980
+ 0,
981
+ 0,
982
+ None,
983
+ BlitOrientation::Normal,
984
+ BlitBlend::Additive,
985
+ );
986
+
987
+ let p = |x: i32, y: i32| dst.data[(y as u32 * dst.row_stride + x as u32) as usize];
988
+ // Diagonal: src=5, dst=99 → both non-zero → writes 5
989
+ assert_eq!(p(0, 0), 5, "diagonal written");
990
+ assert_eq!(p(3, 3), 5, "diagonal written");
991
+ // Off-diagonal: src=0 → skipped even though dst is non-zero
992
+ assert_eq!(p(1, 0), 99, "transparent src not written");
993
+ assert_eq!(p(0, 1), 99, "transparent src not written");
994
+ }
995
+
996
  // =======================================================================
997
  // Image loading helpers (for snapshot tests with real images)
998
  // =======================================================================
 
1554
  77,
1555
  );
1556
  }
1557
+ #[test]
1558
+ fn snap_blit_opaque_additive() {
1559
+ run_snapshot_test(
1560
+ "sprite_test.gif",
1561
+ "blit_opaque_additive",
1562
+ 16,
1563
+ 16,
1564
+ None,
1565
+ (0, 0),
1566
+ BlitOrientation::Normal,
1567
+ BlitBlend::Additive,
1568
+ 77,
1569
+ );
1570
+ }
1571
+ #[test]
1572
+ fn snap_blit_opaque_subtractive() {
1573
+ run_snapshot_test(
1574
+ "sprite_test.gif",
1575
+ "blit_opaque_subtractive",
1576
+ 16,
1577
+ 16,
1578
+ None,
1579
+ (0, 0),
1580
+ BlitOrientation::Normal,
1581
+ BlitBlend::Subtractive,
1582
+ 0,
1583
+ );
1584
+ }
1585
+ #[test]
1586
+ fn snap_blit_transparent_additive() {
1587
+ run_snapshot_test(
1588
+ "sprite_transparent_test.gif",
1589
+ "blit_transparent_additive",
1590
+ 16,
1591
+ 16,
1592
+ None,
1593
+ (0, 0),
1594
+ BlitOrientation::Normal,
1595
+ BlitBlend::Additive,
1596
+ 77,
1597
+ );
1598
+ }
1599
+ #[test]
1600
+ fn snap_blit_transparent_subtractive() {
1601
+ run_snapshot_test(
1602
+ "sprite_transparent_test.gif",
1603
+ "blit_transparent_subtractive",
1604
+ 16,
1605
+ 16,
1606
+ None,
1607
+ (0, 0),
1608
+ BlitOrientation::Normal,
1609
+ BlitBlend::Subtractive,
1610
+ 0,
1611
+ );
1612
+ }
1613
  }
crates/openwa-dll/src/replacements/bitgrid.rs CHANGED
@@ -382,9 +382,21 @@ pub unsafe fn capture_blit_snapshots() {
382
  0x0000_0000,
383
  0,
384
  ),
 
 
 
 
385
  ];
386
 
387
- let blit_target = rb(va::BLIT_SPRITE_RECT);
 
 
 
 
 
 
 
 
388
 
389
  for &(suffix, dx, dy, w, h, sx, sy, flags, bg) in test_cases {
390
  // Clear destination
@@ -400,7 +412,7 @@ pub unsafe fn capture_blit_snapshots() {
400
  src,
401
  sx,
402
  sy,
403
- 0, // color_table = 0 (none) for mode 0; ignored for mode 1 without table
404
  flags,
405
  blit_target,
406
  );
@@ -478,7 +490,7 @@ unsafe extern "cdecl" fn wa_blit_sprite_rect(
478
  _src: *mut openwa_core::bitgrid::DisplayBitGrid,
479
  _src_x: i32,
480
  _src_y: i32,
481
- _color_table: u32,
482
  _flags: u32,
483
  _target: u32,
484
  ) {
@@ -539,8 +551,8 @@ unsafe extern "C" fn blit_hook_trampoline() {
539
 
540
  /// Rust implementation of the core sprite blit.
541
  ///
542
- /// Handles 8bpp blend modes 0 (copy) and 1 (color table / transparency).
543
- /// Falls through to the original WA function for unsupported modes.
544
  unsafe extern "cdecl" fn blit_impl(
545
  dst: *mut openwa_core::bitgrid::DisplayBitGrid,
546
  dst_x: i32,
@@ -550,7 +562,7 @@ unsafe extern "cdecl" fn blit_impl(
550
  src: *mut openwa_core::bitgrid::DisplayBitGrid,
551
  src_x: i32,
552
  src_y: i32,
553
- color_table: u32,
554
  flags: u32,
555
  ) -> u32 {
556
  use openwa_core::display::sprite_blit::{
@@ -565,8 +577,8 @@ unsafe extern "cdecl" fn blit_impl(
565
  let src_cpp = (*src).cells_per_unit;
566
  let dst_cpp = (*dst).cells_per_unit;
567
 
568
- // Only handle 8bpp surfaces with blend modes 0 and 1
569
- if dst_cpp != 8 || src_cpp != 8 || blend_mode > 1 {
570
  return call_original_blit(
571
  dst,
572
  dst_x,
@@ -581,22 +593,12 @@ unsafe extern "cdecl" fn blit_impl(
581
  );
582
  }
583
 
584
- // For mode 1 with a color table pointer, fall through for now
585
- // (we'd need to read 256 bytes from the pointer, which could be a mixing LUT)
586
- if blend_mode == 1 && color_table != 0 {
587
- return call_original_blit(
588
- dst,
589
- dst_x,
590
- dst_y,
591
- width,
592
- height,
593
- src,
594
- src_x,
595
- src_y,
596
- color_table,
597
- flags,
598
- );
599
- }
600
 
601
  let orientation = BlitOrientation::from_flags(flags);
602
  let blend = BlitBlend::from_flags(flags);
@@ -641,7 +643,7 @@ unsafe extern "cdecl" fn blit_impl(
641
  height,
642
  src_x,
643
  src_y,
644
- None, // no color table for modes we handle
645
  orientation,
646
  blend,
647
  );
@@ -663,7 +665,7 @@ unsafe fn call_original_blit(
663
  src: *mut openwa_core::bitgrid::DisplayBitGrid,
664
  src_x: i32,
665
  src_y: i32,
666
- color_table: u32,
667
  flags: u32,
668
  ) -> u32 {
669
  let orig = ORIG_BLIT.load(Ordering::Relaxed);
@@ -693,7 +695,7 @@ unsafe extern "cdecl" fn call_original_blit_asm(
693
  _src: *mut openwa_core::bitgrid::DisplayBitGrid,
694
  _src_x: i32,
695
  _src_y: i32,
696
- _color_table: u32,
697
  _flags: u32,
698
  _target: u32,
699
  ) -> u32 {
 
382
  0x0000_0000,
383
  0,
384
  ),
385
+ // Additive mask (mode 2) — writes only where both src and dst are non-zero
386
+ ("additive", 16, 16, sw, sh, 0, 0, 0x0000_0002, 77),
387
+ // Subtractive mask (mode 3) — writes only where src != 0 and dst == 0
388
+ ("subtractive", 16, 16, sw, sh, 0, 0, 0x0000_0003, 0),
389
  ];
390
 
391
+ // Use the original (unhooked) blit function for ground-truth captures.
392
+ // ORIG_BLIT is set by MinHook when the hook is installed; if not hooked
393
+ // yet (shouldn't happen), fall back to the raw address.
394
+ let orig = ORIG_BLIT.load(core::sync::atomic::Ordering::Relaxed);
395
+ let blit_target = if orig != 0 {
396
+ orig
397
+ } else {
398
+ rb(va::BLIT_SPRITE_RECT)
399
+ };
400
 
401
  for &(suffix, dx, dy, w, h, sx, sy, flags, bg) in test_cases {
402
  // Clear destination
 
412
  src,
413
  sx,
414
  sy,
415
+ core::ptr::null(), // no color table
416
  flags,
417
  blit_target,
418
  );
 
490
  _src: *mut openwa_core::bitgrid::DisplayBitGrid,
491
  _src_x: i32,
492
  _src_y: i32,
493
+ _color_table: *const u8,
494
  _flags: u32,
495
  _target: u32,
496
  ) {
 
551
 
552
  /// Rust implementation of the core sprite blit.
553
  ///
554
+ /// Handles all 8bpp blend modes: 0 (copy), 1 (color table), 2 (additive mask), 3 (subtractive mask).
555
+ /// Falls through to the original WA function for non-8bpp surfaces.
556
  unsafe extern "cdecl" fn blit_impl(
557
  dst: *mut openwa_core::bitgrid::DisplayBitGrid,
558
  dst_x: i32,
 
562
  src: *mut openwa_core::bitgrid::DisplayBitGrid,
563
  src_x: i32,
564
  src_y: i32,
565
+ color_table: *const u8,
566
  flags: u32,
567
  ) -> u32 {
568
  use openwa_core::display::sprite_blit::{
 
577
  let src_cpp = (*src).cells_per_unit;
578
  let dst_cpp = (*dst).cells_per_unit;
579
 
580
+ // Only handle 8bpp surfaces with blend modes 0-3
581
+ if dst_cpp != 8 || src_cpp != 8 || blend_mode > 3 {
582
  return call_original_blit(
583
  dst,
584
  dst_x,
 
593
  );
594
  }
595
 
596
+ // Convert color_table from raw pointer to typed reference
597
+ let color_table_ref: Option<&[u8; 256]> = if !color_table.is_null() {
598
+ Some(&*(color_table as *const [u8; 256]))
599
+ } else {
600
+ None
601
+ };
 
 
 
 
 
 
 
 
 
 
602
 
603
  let orientation = BlitOrientation::from_flags(flags);
604
  let blend = BlitBlend::from_flags(flags);
 
643
  height,
644
  src_x,
645
  src_y,
646
+ color_table_ref,
647
  orientation,
648
  blend,
649
  );
 
665
  src: *mut openwa_core::bitgrid::DisplayBitGrid,
666
  src_x: i32,
667
  src_y: i32,
668
+ color_table: *const u8,
669
  flags: u32,
670
  ) -> u32 {
671
  let orig = ORIG_BLIT.load(Ordering::Relaxed);
 
695
  _src: *mut openwa_core::bitgrid::DisplayBitGrid,
696
  _src_x: i32,
697
  _src_y: i32,
698
+ _color_table: *const u8,
699
  _flags: u32,
700
  _target: u32,
701
  ) -> u32 {
testdata/snapshots/blit_opaque_additive.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:674b3978de2a4221bc70426270b04d787919b514a9d4d936862eac33c5708088
3
+ size 25612
testdata/snapshots/blit_opaque_subtractive.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:373212b13209d949dc01ec5da36d7ce2cd20f62420f35fded1c5fb351e50898c
3
+ size 25612
testdata/snapshots/blit_transparent_additive.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:943144ddded70c2c54ece1b7b5713247d4c36d042f9e8cd301e4355b2646b519
3
+ size 25612
testdata/snapshots/blit_transparent_subtractive.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f892414f33d882e46788ba903eb5ebb9b29f3548d7a946b140e230ba1db37ce3
3
+ size 25612