1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use super::SwRenderer;
use super::blit_fast::{blit_1to1_fast, blit_2to2_fast, blit_composite_dda, blit_dda};
use crate::render::command::CompositeMode;
use crate::render::texture::Texture;
use crate::types::{Fixed, Point, Rect};
impl SwRenderer<'_> {
#[allow(clippy::too_many_arguments)]
pub(super) fn blit_inner(
&mut self,
src: &Texture,
src_rect: &Rect,
dst: Point,
dst_size: Point,
clip: &Rect,
opa: u8,
radius: Fixed,
composite: CompositeMode,
) {
let phys_dst = self.viewport.point_to_physical(dst);
let phys_dst_size = self.viewport.point_to_physical(dst_size);
let phys_clip = self.viewport.rect_to_physical(*clip);
let phys_radius = radius * self.viewport.scale();
let (sx0, sy0, sw, sh) = src_rect.to_px();
// Negative clip x flowing through to the byte-offset math
// (`dx0 as usize * 4`) wraps; clamp to target bounds first.
let target_w = self.target.width as i32;
let target_h = self.target.height as i32;
let (raw_x0, raw_y0, raw_x1, raw_y1) = phys_clip.pixel_bounds();
let clip_x0 = raw_x0.max(0);
let clip_y0 = raw_y0.max(0);
let clip_x1 = raw_x1.min(target_w);
let clip_y1 = raw_y1.min(target_h);
let dx0 = phys_dst.x.to_int();
let dy0 = phys_dst.y.to_int();
let dw = phys_dst_size.x.to_int();
let dh = phys_dst_size.y.to_int();
if dw <= 0 || dh <= 0 || sw == 0 || sh == 0 {
return;
}
if opa == 0 {
return;
}
let needs_composite_path =
!matches!(composite, CompositeMode::SourceOver) || phys_radius != Fixed::ZERO;
let clip_mask = self.clip_stack.last().map(|m| m.alpha.as_slice());
if needs_composite_path {
blit_composite_dda(
&mut self.target,
src,
sx0,
sy0,
sw,
sh,
dx0,
dy0,
dw,
dh,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
opa,
composite,
phys_radius,
clip_mask,
);
return;
}
// opa < 255 → bypass per-format fast paths and go through the
// per-pixel DDA so the alpha gets folded into each blend; the
// fast paths' memcpy-ish row copies have no per-pixel hook.
if opa < 255 || clip_mask.is_some() {
blit_dda(
&mut self.target,
src,
sx0,
sy0,
sw,
sh,
dx0,
dy0,
dw,
dh,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
opa,
clip_mask,
);
return;
}
let sw_i = sw as i32;
let sh_i = sh as i32;
// 1× → format-specialized 1to1 fast path; 2× → 2to2; arbitrary → DDA.
#[allow(clippy::if_same_then_else)]
if dw == sw_i && dh == sh_i {
blit_1to1_fast(
&mut self.target,
src,
sx0,
sy0,
sw,
sh,
dx0,
dy0,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
);
} else if dw == sw_i * 2 && dh == sh_i * 2 {
blit_2to2_fast(
&mut self.target,
src,
sx0,
sy0,
sw,
sh,
dx0,
dy0,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
);
} else {
blit_dda(
&mut self.target,
src,
sx0,
sy0,
sw,
sh,
dx0,
dy0,
dw,
dh,
clip_x0,
clip_y0,
clip_x1,
clip_y1,
255,
None,
);
}
}
}