add color offset blit method variants
these allow a per-pixel color index offset to be applied during blits
This commit is contained in:
parent
6bb52e57e1
commit
71556c6d65
|
@ -6,8 +6,12 @@ pub enum BlitMethod {
|
|||
Solid,
|
||||
Transparent(u8),
|
||||
TransparentSingle(u8, u8), // transparent color, single visible color
|
||||
SolidOffset(u8),
|
||||
TransparentOffset(u8, u8),
|
||||
RotoZoom { angle: f32, scale_x: f32, scale_y: f32 },
|
||||
RotoZoomTransparent { angle: f32, scale_x: f32, scale_y: f32, transparent_color: u8 },
|
||||
RotoZoomOffset { angle: f32, scale_x: f32, scale_y: f32, offset: u8 },
|
||||
RotoZoomTransparentOffset { angle: f32, scale_x: f32, scale_y: f32, transparent_color: u8, offset: u8 },
|
||||
}
|
||||
|
||||
/// Clips the region for a source bitmap to be used in a subsequent blit operation. The source
|
||||
|
@ -100,6 +104,24 @@ impl Bitmap {
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn solid_blit_palette_offset(&mut self, src: &Bitmap, src_region: &Rect, dest_x: i32, dest_y: i32, offset: u8) {
|
||||
let src_next_row_inc = (src.width - src_region.width) as usize;
|
||||
let dest_next_row_inc = (self.width - src_region.width) as usize;
|
||||
let mut src_pixels = src.pixels_at_ptr_unchecked(src_region.x, src_region.y);
|
||||
let mut dest_pixels = self.pixels_at_mut_ptr_unchecked(dest_x, dest_y);
|
||||
|
||||
for _ in 0..src_region.height {
|
||||
for _ in 0..src_region.width {
|
||||
*dest_pixels = (*src_pixels).wrapping_add(offset);
|
||||
src_pixels = src_pixels.add(1);
|
||||
dest_pixels = dest_pixels.add(1);
|
||||
}
|
||||
|
||||
src_pixels = src_pixels.add(src_next_row_inc);
|
||||
dest_pixels = dest_pixels.add(dest_next_row_inc);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn transparent_blit(
|
||||
&mut self,
|
||||
src: &Bitmap,
|
||||
|
@ -129,6 +151,36 @@ impl Bitmap {
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn transparent_blit_palette_offset(
|
||||
&mut self,
|
||||
src: &Bitmap,
|
||||
src_region: &Rect,
|
||||
dest_x: i32,
|
||||
dest_y: i32,
|
||||
transparent_color: u8,
|
||||
offset: u8,
|
||||
) {
|
||||
let src_next_row_inc = (src.width - src_region.width) as usize;
|
||||
let dest_next_row_inc = (self.width - src_region.width) as usize;
|
||||
let mut src_pixels = src.pixels_at_ptr_unchecked(src_region.x, src_region.y);
|
||||
let mut dest_pixels = self.pixels_at_mut_ptr_unchecked(dest_x, dest_y);
|
||||
|
||||
for _ in 0..src_region.height {
|
||||
for _ in 0..src_region.width {
|
||||
let pixel = *src_pixels;
|
||||
if pixel != transparent_color {
|
||||
*dest_pixels = pixel.wrapping_add(offset);
|
||||
}
|
||||
|
||||
src_pixels = src_pixels.add(1);
|
||||
dest_pixels = dest_pixels.add(1);
|
||||
}
|
||||
|
||||
src_pixels = src_pixels.add(src_next_row_inc);
|
||||
dest_pixels = dest_pixels.add(dest_next_row_inc);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn transparent_single_color_blit(
|
||||
&mut self,
|
||||
src: &Bitmap,
|
||||
|
@ -224,6 +276,74 @@ impl Bitmap {
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn rotozoom_blit_palette_offset(
|
||||
&mut self,
|
||||
src: &Bitmap,
|
||||
src_region: &Rect,
|
||||
dest_x: i32,
|
||||
dest_y: i32,
|
||||
angle: f32,
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
transparent_color: Option<u8>,
|
||||
offset: u8,
|
||||
) {
|
||||
// TODO: this isn't the best rotozoom algorithm i guess. it has some floating point issues
|
||||
// that result in missing pixels/rows still in a few places. also the double pixel
|
||||
// write exists to mask that issue (even worse without it).
|
||||
// need to re-do this with a better rotozoom algorithm!
|
||||
|
||||
let new_width = src_region.width as f32 * scale_x;
|
||||
let new_height = src_region.height as f32 * scale_y;
|
||||
if new_width as i32 <= 0 || new_height as i32 <= 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let angle_cos = angle.cos();
|
||||
let angle_sin = angle.sin();
|
||||
|
||||
let src_delta_x = src_region.width as f32 / new_width;
|
||||
let src_delta_y = src_region.height as f32 / new_height;
|
||||
|
||||
let mut src_x = 0.0;
|
||||
let mut src_y = 0.0;
|
||||
|
||||
let dest_center_x = dest_x as f32 + (new_width / 2.0);
|
||||
let dest_center_y = dest_y as f32 + (new_height / 2.0);
|
||||
|
||||
for point_y in 0..new_height as i32 {
|
||||
let src_pixels = src.pixels_at_unchecked(src_region.x, src_region.y + src_y as i32);
|
||||
|
||||
for point_x in 0..new_width as i32 {
|
||||
let pixel = src_pixels[src_x as usize];
|
||||
if transparent_color.is_none() || transparent_color != Some(pixel) {
|
||||
let draw_x = (
|
||||
(angle_cos * (point_x as f32 - (new_width / 2.0)))
|
||||
- (angle_sin * (point_y as f32 - (new_height / 2.0)))
|
||||
+ dest_center_x
|
||||
) as i32;
|
||||
let draw_y = (
|
||||
(angle_cos * (point_y as f32 - (new_height / 2.0)))
|
||||
+ (angle_sin * (point_x as f32 - (new_width / 2.0)))
|
||||
+ dest_center_y
|
||||
) as i32;
|
||||
|
||||
let pixel = pixel.wrapping_add(offset);
|
||||
|
||||
// write the same pixel twice to mask some floating point issues (?) which would
|
||||
// manifest as "gap" pixels on the destination. ugh!
|
||||
self.set_pixel(draw_x, draw_y, pixel);
|
||||
self.set_pixel(draw_x + 1, draw_y, pixel);
|
||||
}
|
||||
|
||||
src_x += src_delta_x;
|
||||
}
|
||||
|
||||
src_x = 0.0;
|
||||
src_y += src_delta_y;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blit_region(
|
||||
&mut self,
|
||||
method: BlitMethod,
|
||||
|
@ -276,18 +396,28 @@ impl Bitmap {
|
|||
use BlitMethod::*;
|
||||
match method {
|
||||
Solid => self.solid_blit(src, src_region, dest_x, dest_y),
|
||||
SolidOffset(offset) => self.solid_blit_palette_offset(src, src_region, dest_x, dest_y, offset),
|
||||
Transparent(transparent_color) => {
|
||||
self.transparent_blit(src, src_region, dest_x, dest_y, transparent_color)
|
||||
},
|
||||
TransparentOffset(transparent_color, offset) => {
|
||||
self.transparent_blit_palette_offset(src, src_region, dest_x, dest_y, transparent_color, offset)
|
||||
},
|
||||
TransparentSingle(transparent_color, single_color) => {
|
||||
self.transparent_single_color_blit(src, src_region, dest_x, dest_y, transparent_color, single_color)
|
||||
},
|
||||
RotoZoom { angle, scale_x, scale_y } => {
|
||||
self.rotozoom_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, None)
|
||||
},
|
||||
RotoZoomOffset { angle, scale_x, scale_y, offset } => {
|
||||
self.rotozoom_blit_palette_offset(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, None, offset)
|
||||
},
|
||||
RotoZoomTransparent { angle, scale_x, scale_y, transparent_color } => {
|
||||
self.rotozoom_blit(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, Some(transparent_color))
|
||||
}
|
||||
},
|
||||
RotoZoomTransparentOffset { angle, scale_x, scale_y, transparent_color, offset } => {
|
||||
self.rotozoom_blit_palette_offset(src, src_region, dest_x, dest_y, angle, scale_x, scale_y, Some(transparent_color), offset)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue