|
| 1 | +use std::cmp::{max, min}; |
| 2 | + |
| 3 | +use super::*; |
| 4 | +use super::background::*; |
| 5 | +use super::object::*; |
| 6 | + |
| 7 | +#[inline] |
| 8 | +fn colour_unpack(c: u16) -> (u8, u8, u8) { |
| 9 | + let c = c as u32; |
| 10 | + (extract(c, 0, 5) as u8, extract(c, 5, 5) as u8, extract(c, 10, 5) as u8) |
| 11 | +} |
| 12 | + |
| 13 | +#[inline] |
| 14 | +fn colour_repack(c: (u8, u8, u8)) -> u16 { |
| 15 | + (c.0 as u16) | |
| 16 | + ((c.1 as u16) << 5) | |
| 17 | + ((c.2 as u16) << 10) |
| 18 | +} |
| 19 | + |
| 20 | +#[inline] |
| 21 | +fn alpha_blend_component(eva: u32, evb: u32, c1: u8, c2: u8) -> u8 { |
| 22 | + let c1 = c1 as u32; |
| 23 | + let c2 = c2 as u32; |
| 24 | + min(31, (eva * c1 + evb * c2) / 16) as u8 |
| 25 | +} |
| 26 | + |
| 27 | +#[inline] |
| 28 | +fn alpha_blend(bldalpha: u16, c1: u32, c2: u32) -> u32 { |
| 29 | + let eva = extract(bldalpha as u32, 0, 5); |
| 30 | + let evb = extract(bldalpha as u32, 8, 5); |
| 31 | + |
| 32 | + let c1rgb = colour_unpack(c1 as u16); |
| 33 | + let c2rgb = colour_unpack(c2 as u16); |
| 34 | + |
| 35 | + colour_repack(( |
| 36 | + alpha_blend_component(eva, evb, c1rgb.0, c2rgb.0), |
| 37 | + alpha_blend_component(eva, evb, c1rgb.1, c2rgb.1), |
| 38 | + alpha_blend_component(eva, evb, c1rgb.2, c2rgb.2), |
| 39 | + )) as u32 |
| 40 | +} |
| 41 | + |
| 42 | +#[inline] |
| 43 | +fn brighten_component(evy: u32, c: u8) -> u8 { |
| 44 | + let c = c as u32; |
| 45 | + min(31, c + (evy * (31 - c)) / 16) as u8 |
| 46 | +} |
| 47 | + |
| 48 | +#[inline] |
| 49 | +fn darken_component(evy: u32, c: u8) -> u8 { |
| 50 | + let c = c as i32; |
| 51 | + max(31, c - (evy as i32 * c) / 16) as u8 |
| 52 | +} |
| 53 | + |
| 54 | +#[inline] |
| 55 | +fn brighten(bldy: u16, c: u32) -> u32 { |
| 56 | + let evy = extract(bldy as u32, 0, 5); |
| 57 | + |
| 58 | + let rgb = colour_unpack(c as u16); |
| 59 | + colour_repack(( |
| 60 | + brighten_component(evy, rgb.0), |
| 61 | + brighten_component(evy, rgb.1), |
| 62 | + brighten_component(evy, rgb.2), |
| 63 | + )) as u32 |
| 64 | +} |
| 65 | + |
| 66 | +#[inline] |
| 67 | +fn darken(bldy: u16, c: u32) -> u32 { |
| 68 | + let evy = extract(bldy as u32, 0, 5); |
| 69 | + |
| 70 | + let rgb = colour_unpack(c as u16); |
| 71 | + colour_repack(( |
| 72 | + darken_component(evy, rgb.0), |
| 73 | + darken_component(evy, rgb.1), |
| 74 | + darken_component(evy, rgb.2), |
| 75 | + )) as u32 |
| 76 | +} |
| 77 | + |
| 78 | +fn blend( |
| 79 | + effect: u32, |
| 80 | + bldcnt: u16, |
| 81 | + bldalpha: u16, |
| 82 | + bldy: u16, |
| 83 | + f: u8, |
| 84 | + fc: u32, |
| 85 | + s: u8, |
| 86 | + sc: u32, |
| 87 | +) -> u32 { |
| 88 | + if bit(bldcnt as u32, f) == 1 { |
| 89 | + match effect { |
| 90 | + 0 => fc, |
| 91 | + 1 => { |
| 92 | + if bit(bldcnt as u32, 8 + s) == 1 { |
| 93 | + alpha_blend(bldalpha, fc, sc) |
| 94 | + } else { |
| 95 | + fc |
| 96 | + } |
| 97 | + } |
| 98 | + 2 => brighten(bldy, fc), |
| 99 | + 3 => darken(bldy, fc), |
| 100 | + _ => unreachable!(), |
| 101 | + } |
| 102 | + } else { |
| 103 | + fc |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +fn blend_semitrans( |
| 108 | + effect: u32, |
| 109 | + bldcnt: u16, |
| 110 | + bldalpha: u16, |
| 111 | + bldy: u16, |
| 112 | + f: u8, |
| 113 | + fc: u32, |
| 114 | + s: u8, |
| 115 | + sc: u32, |
| 116 | +) -> u32 { |
| 117 | + if bit(bldcnt as u32, 8 + s) == 1 { |
| 118 | + alpha_blend(bldalpha, fc, sc) |
| 119 | + } else { |
| 120 | + match effect { |
| 121 | + 0 | 1 => fc, |
| 122 | + 2 => brighten(bldy, fc), |
| 123 | + 3 => darken(bldy, fc), |
| 124 | + _ => unreachable!(), |
| 125 | + } |
| 126 | + } |
| 127 | +} |
| 128 | + |
| 129 | +impl<'a> Ppu<'a> { |
| 130 | + fn bg0_drawline(&mut self, mode: u32, row: u32, dspcnt: u16) -> bool { |
| 131 | + let bg0en = mode <= 1 && bit(dspcnt as u32, 8) == 1; |
| 132 | + if bg0en { |
| 133 | + render_textmode_line(&mut self.state.line0, row, &self.mmu, 0); |
| 134 | + } |
| 135 | + bg0en |
| 136 | + } |
| 137 | + |
| 138 | + fn bg1_drawline(&mut self, mode: u32, row: u32, dspcnt: u16) -> bool { |
| 139 | + let bg1en = mode <= 1 && bit(dspcnt as u32, 9) == 1; |
| 140 | + if bg1en { |
| 141 | + render_textmode_line(&mut self.state.line1, row, &self.mmu, 1); |
| 142 | + } |
| 143 | + bg1en |
| 144 | + } |
| 145 | + |
| 146 | + fn bg2_drawline(&mut self, mode: u32, row: u32, dspcnt: u16) -> bool { |
| 147 | + let bg2en = bit(dspcnt as u32, 10) == 1; |
| 148 | + if bg2en { |
| 149 | + if mode == 0 { |
| 150 | + render_textmode_line(&mut self.state.line2, row, &self.mmu, 2); |
| 151 | + } else { |
| 152 | + let rparams = RotScaleParams::new( |
| 153 | + self.io.get_priv(0x20), |
| 154 | + self.io.get_priv(0x22), |
| 155 | + self.io.get_priv(0x24), |
| 156 | + self.io.get_priv(0x26), |
| 157 | + ); |
| 158 | + |
| 159 | + let ctrl = if mode < 3 { |
| 160 | + RotScaleCtrl::TileMap(self.io.get_priv(0xc)) |
| 161 | + } else { |
| 162 | + RotScaleCtrl::Bitmap(dspcnt) |
| 163 | + }; |
| 164 | + |
| 165 | + render_rotscale_line( |
| 166 | + &mut self.state.line2, |
| 167 | + row, |
| 168 | + &self.mmu, |
| 169 | + &mut self.state.bg2ref, |
| 170 | + rparams, |
| 171 | + ctrl, |
| 172 | + 2, |
| 173 | + ); |
| 174 | + } |
| 175 | + } |
| 176 | + bg2en |
| 177 | + } |
| 178 | + |
| 179 | + fn bg3_drawline(&mut self, mode: u32, row: u32, dspcnt: u16) -> bool { |
| 180 | + let bg3en = (mode == 0 || mode == 2) && bit(dspcnt as u32, 11) == 1; |
| 181 | + if bg3en { |
| 182 | + if mode == 0 { |
| 183 | + render_textmode_line(&mut self.state.line3, row, &self.mmu, 3); |
| 184 | + } else { |
| 185 | + let rparams = RotScaleParams::new( |
| 186 | + self.io.get_priv(0x30), |
| 187 | + self.io.get_priv(0x32), |
| 188 | + self.io.get_priv(0x34), |
| 189 | + self.io.get_priv(0x36), |
| 190 | + ); |
| 191 | + |
| 192 | + render_rotscale_line( |
| 193 | + &mut self.state.line3, |
| 194 | + row, |
| 195 | + &self.mmu, |
| 196 | + &mut self.state.bg3ref, |
| 197 | + rparams, |
| 198 | + RotScaleCtrl::TileMap(self.io.get_priv(0xe)), |
| 199 | + 3, |
| 200 | + ); |
| 201 | + } |
| 202 | + } |
| 203 | + bg3en |
| 204 | + } |
| 205 | + |
| 206 | + fn obj_drawline(&mut self, mode: u32, row: u32, dspcnt: u16) -> bool { |
| 207 | + let objen = bit(dspcnt as u32, 12) == 1; |
| 208 | + if objen { |
| 209 | + render_obj_line( |
| 210 | + &mut self.state.lineo, |
| 211 | + &mut self.state.line_objwindow, |
| 212 | + row, |
| 213 | + &self.mmu, |
| 214 | + dspcnt, |
| 215 | + ); |
| 216 | + } |
| 217 | + objen |
| 218 | + } |
| 219 | + |
| 220 | + pub(super) fn combine_line(&mut self, row: u32, dspcnt: u16) { |
| 221 | + let mode = extract(dspcnt as u32, 0, 3); |
| 222 | + |
| 223 | + let bg0en = self.bg0_drawline(mode, row, dspcnt); |
| 224 | + let bg1en = self.bg1_drawline(mode, row, dspcnt); |
| 225 | + let bg2en = self.bg2_drawline(mode, row, dspcnt); |
| 226 | + let bg3en = self.bg3_drawline(mode, row, dspcnt); |
| 227 | + let objen = self.obj_drawline(mode, row, dspcnt); |
| 228 | + |
| 229 | + let win_enable = extract(dspcnt as u32, 13, 3) != 0; |
| 230 | + let in_win0 = bit(dspcnt as u32, 13) == 1 && in_win_vert(self.io.get_priv(0x44), row); |
| 231 | + let in_win1 = bit(dspcnt as u32, 14) == 1 && in_win_vert(self.io.get_priv(0x46), row); |
| 232 | + let in_wino = bit(dspcnt as u32, 15) == 1; |
| 233 | + |
| 234 | + let winin = self.io.get_priv(0x48); |
| 235 | + let winout = self.io.get_priv(0x4a); |
| 236 | + |
| 237 | + let win0h = if in_win0 { self.io.get_priv(0x40) } else { 0 }; |
| 238 | + let win1h = if in_win1 { self.io.get_priv(0x42) } else { 0 }; |
| 239 | + |
| 240 | + let bldcnt = self.io.get_priv(0x50); |
| 241 | + let effect = extract(bldcnt as u32, 6, 2); |
| 242 | + let bldalpha = self.io.get_priv(0x52); |
| 243 | + let bldy = self.io.get_priv(0x54); |
| 244 | + |
| 245 | + let backdrop = (self.mmu.pram.load16(0) as u32) | (0xe << 28); |
| 246 | + |
| 247 | + for x in 0..COLS { |
| 248 | + let ux = x as usize; |
| 249 | + let en_mask = if win_enable { |
| 250 | + if in_win0 && in_win_hori(win0h, x) { |
| 251 | + winin & 0xff |
| 252 | + } else if in_win1 && in_win_hori(win1h, x) { |
| 253 | + winin >> 8 |
| 254 | + } else if in_wino && self.state.line_objwindow[ux] != 0 { |
| 255 | + winout >> 8 |
| 256 | + } else { |
| 257 | + winout & 0xff |
| 258 | + } |
| 259 | + } else { |
| 260 | + 0xff |
| 261 | + } as u32; |
| 262 | + |
| 263 | + let bg0en = bg0en && bit(en_mask, 0) == 1; |
| 264 | + let bg1en = bg1en && bit(en_mask, 1) == 1; |
| 265 | + let bg2en = bg2en && bit(en_mask, 2) == 1; |
| 266 | + let bg3en = bg3en && bit(en_mask, 3) == 1; |
| 267 | + let objen = objen && bit(en_mask, 4) == 1; |
| 268 | + |
| 269 | + // FIXME: special blend effects |
| 270 | + let semitrans = objen && (self.state.lineo[ux] & SEMITRANS != 0); |
| 271 | + |
| 272 | + let (first, fc) = { |
| 273 | + let mut fc = backdrop; |
| 274 | + let mut f = 5; |
| 275 | + macro_rules! check { |
| 276 | + ($c: expr, $i: expr) => { |
| 277 | + { |
| 278 | + let val = $c; |
| 279 | + if val < fc { |
| 280 | + f = $i; |
| 281 | + fc = val; |
| 282 | + } |
| 283 | + } |
| 284 | + }; |
| 285 | + } |
| 286 | + if objen { |
| 287 | + check!(self.state.lineo[ux], 4); |
| 288 | + } |
| 289 | + if bg0en { |
| 290 | + check!(self.state.line0[ux], 0); |
| 291 | + } |
| 292 | + if bg1en { |
| 293 | + check!(self.state.line1[ux], 1); |
| 294 | + } |
| 295 | + if bg2en { |
| 296 | + check!(self.state.line2[ux], 2); |
| 297 | + } |
| 298 | + if bg3en { |
| 299 | + check!(self.state.line3[ux], 3); |
| 300 | + } |
| 301 | + (f, fc) |
| 302 | + }; |
| 303 | + let (second, sc) = |
| 304 | + if (fc & SEMITRANS != 0) || (bit(en_mask, 5) == 1 && effect == 1) { |
| 305 | + let mut sc = backdrop; |
| 306 | + let mut s = 5; |
| 307 | + macro_rules! check { |
| 308 | + ($c: expr, $i: expr) => { |
| 309 | + { |
| 310 | + let val = $c; |
| 311 | + if val < sc { |
| 312 | + s = $i; |
| 313 | + sc = val; |
| 314 | + } |
| 315 | + } |
| 316 | + }; |
| 317 | + } |
| 318 | + if objen && first != 4 { |
| 319 | + check!(self.state.lineo[ux], 4) |
| 320 | + } |
| 321 | + if bg0en && first != 0 { |
| 322 | + check!(self.state.line0[ux], 0) |
| 323 | + } |
| 324 | + if bg1en && first != 1 { |
| 325 | + check!(self.state.line1[ux], 1) |
| 326 | + } |
| 327 | + if bg2en && first != 2 { |
| 328 | + check!(self.state.line2[ux], 2) |
| 329 | + } |
| 330 | + if bg3en && first != 3 { |
| 331 | + check!(self.state.line3[ux], 3) |
| 332 | + } |
| 333 | + (s, sc) |
| 334 | + } else { |
| 335 | + // bldcnt will be converted to u32, |
| 336 | + // so when we check if the bit is enabled for second target, |
| 337 | + // this will always be a 0 (0 + 16 and 8 + 16 will be checked) |
| 338 | + (16, TRANSPARENT) |
| 339 | + }; |
| 340 | + |
| 341 | + self.state.line[ux] = if fc & SEMITRANS != 0 { |
| 342 | + blend_semitrans(effect, bldcnt, bldalpha, bldy, first, fc, second, sc) |
| 343 | + } else if bit(en_mask, 5) == 1 && effect != 0 { |
| 344 | + blend(effect, bldcnt, bldalpha, bldy, first, fc, second, sc) |
| 345 | + } else { |
| 346 | + fc |
| 347 | + }; |
| 348 | + } |
| 349 | + } |
| 350 | +} |
0 commit comments