From: Kostya Shishkov Date: Mon, 2 Nov 2026 14:47:34 +0000 (+0100) Subject: qdraw: implement some more painting opcodes X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=bbf4d260e5330d3ac84868d67145d4f5a6b76f66;p=nihav.git qdraw: implement some more painting opcodes --- diff --git a/nihav-qt/src/codecs/qdraw.rs b/nihav-qt/src/codecs/qdraw.rs index bee8d43..394143c 100644 --- a/nihav-qt/src/codecs/qdraw.rs +++ b/nihav-qt/src/codecs/qdraw.rs @@ -35,6 +35,145 @@ impl ReadQDTypes for T { } } +#[derive(Clone,Copy,Default)] +struct Rect { + sx: isize, + sy: isize, + dx: isize, + dy: isize, +} + +impl Rect { + fn read(br: &mut dyn ByteIO) -> DecoderResult { + let sy = isize::from(br.read_u16be()? as i16); + let sx = isize::from(br.read_u16be()? as i16); + let dy = isize::from(br.read_u16be()? as i16); + let dx = isize::from(br.read_u16be()? as i16); + validate!(sy < dy && sx < dx); + Ok(Self { sx, sy, dx, dy }) + } + fn paint(&self, dst: &mut [u8], stride: usize, width: usize, height: usize, clr: &[u8; 3]) { + let sx = self.sx.max(0) as usize; + let sy = self.sy.max(0) as usize; + let dx = self.dx.max(0) as usize; + let dy = self.dy.max(0) as usize; + for dline in dst.chunks_exact_mut(stride).take(dy.min(height)).skip(sy) { + for pix in dline.chunks_exact_mut(3).take(dx.min(width)).skip(sx) { + pix.copy_from_slice(clr); + } + } + } + fn paint_ellipse(&self, dst: &mut [u8], stride: usize, width: usize, height: usize, clr: &[u8; 3]) { + let (cx, cy) = ((self.sx + self.dx) as f32 * 0.5, (self.sy + self.dy) as f32 * 0.5); + let ell_x = 1.0 / ((self.dx as f32 - cx) * (self.dx as f32 - cx)); + let ell_y = 1.0 / ((self.dy as f32 - cy) * (self.dy as f32 - cy)); + + let sx = self.sx.max(0) as usize; + let sy = self.sy.max(0) as usize; + let dx = self.dx.max(0) as usize; + let dy = self.dy.max(0) as usize; + for (y, dline) in dst.chunks_exact_mut(stride).enumerate().take(dy.min(height)).skip(sy) { + for (x, pix) in dline.chunks_exact_mut(3).enumerate().take(dx.min(width)).skip(sx) { + let dx = (x as f32 - cx).abs(); + let dy = (y as f32 - cy).abs(); + if dx * dx * ell_x + dy * dy * ell_y <= 1.0 { + pix.copy_from_slice(clr); + } + } + } + } +} + +#[derive(Default)] +struct Painter { + width: usize, + height: usize, + stride: usize, + pos_x: isize, + pos_y: isize, + fg_clr: [u8; 3], + bg_clr: [u8; 3], + fill_clr: [u8; 3], + last_rect: Option, + last_oval: Option, +} + +impl Painter { + fn line_to(&mut self, dst: &mut [u8], mut dx: isize, mut dy: isize) { + let mut sx = self.pos_x; + let mut sy = self.pos_y; + if sx > dx { + std::mem::swap(&mut sx, &mut dx); + std::mem::swap(&mut sy, &mut dy); + } + let bound_x = 0..(self.width as isize); + let bound_y = 0..(self.height as isize); + let xdiff = dx - sx; + let mut ydiff = dy - sy; + let mut yinc = 1; + if ydiff < 0 { + ydiff = -ydiff; + yinc = -yinc; + } + + if xdiff >= ydiff { + let mut delta = 2 * ydiff - xdiff; + let mut y = sy; + for x in sx..dy { + if bound_x.contains(&x) && bound_y.contains(&y) { + dst[(x as usize) * 3 + (y as usize) * self.stride..][..3].copy_from_slice(&self.fg_clr); + } + if delta > 0 { + y += yinc; + delta += 2 * (ydiff - xdiff); + } else { + delta += 2 * ydiff; + } + } + } else { + let mut delta = 2 * xdiff - ydiff; + let mut x = sx; + for y in sy..dy { + if bound_x.contains(&x) && bound_y.contains(&y) { + dst[(x as usize) * 3 + (y as usize) * self.stride..][..3].copy_from_slice(&self.fg_clr); + } + if delta > 0 { + x += 1; + delta += 2 * (xdiff - ydiff); + } else { + delta += 2 * xdiff; + } + } + } + } + fn paint_rect(&mut self, dst: &mut [u8], rect: Rect) { + rect.paint(dst, self.stride, self.width, self.height, &self.fg_clr); + self.last_rect = Some(rect); + } + fn paint_same_rect(&mut self, dst: &mut [u8]) { + if let Some(ref rect) = self.last_rect { + rect.paint(dst, self.stride, self.width, self.height, &self.fg_clr); + } + } + fn erase_rect(&mut self, dst: &mut [u8], rect: Rect) { + rect.paint(dst, self.stride, self.width, self.height, &self.bg_clr); + self.last_rect = Some(rect); + } + fn paint_oval(&mut self, dst: &mut [u8], oval: Rect) { + oval.paint_ellipse(dst, self.stride, self.width, self.height, &self.fg_clr); + self.last_oval = Some(oval); + } + fn paint_same_oval(&mut self, dst: &mut [u8]) { + if let Some(ref oval) = self.last_oval { + oval.paint_ellipse(dst, self.stride, self.width, self.height, &self.fg_clr); + } + } + fn erase_oval(&mut self, dst: &mut [u8], oval: Rect) { + oval.paint_ellipse(dst, self.stride, self.width, self.height, &self.fg_clr); + self.last_oval = Some(oval); + } +} + #[derive(Default)] struct QDrawDecoder { info: NACodecInfoRef, @@ -349,9 +488,8 @@ impl NADecoder for QDrawDecoder { } let mut frm = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap(); - let mut fg_clr = [0; 3]; - let mut bg_clr = [0; 3]; - let mut fill_clr = [0; 3]; + let mut painter = Painter { width: self.width, height: self.height, stride: frm.stride[0], ..Default::default() }; + let mut dst = &mut frm.data[frm.offset[0]..]; while br.left() >= 2 { let op = br.read_u16be()?; match op { @@ -404,65 +542,82 @@ impl NADecoder for QDrawDecoder { return Err(DecoderError::InvalidData); }, 0x001A => { // fore color - fg_clr = br.read_colour()?; + painter.fg_clr = br.read_colour()?; }, 0x001B => { // back color - bg_clr = br.read_colour()?; + painter.bg_clr = br.read_colour()?; }, 0x001E => {}, // def hilite 0x001F => { // fill color - fill_clr = br.read_colour()?; + painter.fill_clr = br.read_colour()?; }, 0x0020 => { // line - let sx = br.read_u16be()?; - let sy = br.read_u16be()?; - let dx = br.read_u16be()?; - let dy = br.read_u16be()?; + painter.pos_y = isize::from(br.read_u16be()? as i16); + painter.pos_x = isize::from(br.read_u16be()? as i16); + let dy = isize::from(br.read_u16be()? as i16); + let dx = isize::from(br.read_u16be()? as i16); + painter.line_to(dst, dx, dy); }, 0x0021 => { // line from - let dx = br.read_u16be()?; - let dy = br.read_u16be()?; + let dy = isize::from(br.read_u16be()? as i16); + let dx = isize::from(br.read_u16be()? as i16); + painter.line_to(dst, dx, dy); }, 0x0022 => { // short line - let xpos = br.read_u16be()?; - let ypos = br.read_u16be()?; - let dx = br.read_byte()? as i8; - let dy = br.read_byte()? as i8; + painter.pos_y = isize::from(br.read_u16be()? as i16); + painter.pos_x = isize::from(br.read_u16be()? as i16); + let dy = isize::from(br.read_byte()? as i8); + let dx = isize::from(br.read_byte()? as i8); + painter.line_to(dst, dx, dy); }, 0x0023 => { // short line from - let dx = br.read_byte()? as i8; - let dy = br.read_byte()? as i8; + let dy = isize::from(br.read_byte()? as i8); + let dx = isize::from(br.read_byte()? as i8); + painter.line_to(dst, dx, dy); }, 0x0028 => { // long text - let _xpos = br.read_u16be()?; let _ypos = br.read_u16be()?; + let _xpos = br.read_u16be()?; let count = usize::from(br.read_byte()?); br.read_skip(count)?; }, 0x0031 => { // paint rect - let sy = usize::from(br.read_u16be()?); - let sx = usize::from(br.read_u16be()?); - let dy = usize::from(br.read_u16be()?); - let dx = usize::from(br.read_u16be()?); - validate!(sy < dy && sx < dx); - for dline in frm.data[frm.offset[0]..].chunks_exact_mut(frm.stride[0]) - .take(dy.min(self.height)).skip(sy) { - for pix in dline.chunks_exact_mut(3).take(dx.min(self.width)).skip(sx) { - pix.copy_from_slice(&fg_clr); - } - } + let rect = Rect::read(&mut br)?; + painter.paint_rect(dst, rect); + }, + 0x0032 => { // erase rect + let rect = Rect::read(&mut br)?; + painter.erase_rect(dst, rect); + }, + 0x0039 => { // paint same rect + painter.paint_same_rect(dst); + }, + 0x0051 => { // paint oval + let oval = Rect::read(&mut br)?; + painter.paint_oval(dst, oval); + }, + 0x0052 => { // erase oval + let oval = Rect::read(&mut br)?; + painter.erase_oval(dst, oval); + }, + 0x0059 => { // paint same oval + painter.paint_same_oval(dst); }, 0x0098 => { // PackBits rect decode_bits(&mut br, &mut frm, self.width, self.height, false, false)?; + dst = &mut frm.data[frm.offset[0]..]; }, 0x0099 => { // PackBits region decode_bits(&mut br, &mut frm, self.width, self.height, true, false)?; + dst = &mut frm.data[frm.offset[0]..]; }, 0x009A => { // DirectBits rect decode_bits(&mut br, &mut frm, self.width, self.height, false, true)?; + dst = &mut frm.data[frm.offset[0]..]; }, 0x009B => { // DirectBits region decode_bits(&mut br, &mut frm, self.width, self.height, true, true)?; + dst = &mut frm.data[frm.offset[0]..]; }, 0x00A0 => { // short comment br.read_u16be()?;