Skip to content

Commit 5717e3d

Browse files
Merge pull request #51 from Decodetalkers/area_fix
chore: make the clip size correct
2 parents ed519e4 + b9f0dd4 commit 5717e3d

File tree

4 files changed

+138
-41
lines changed

4 files changed

+138
-41
lines changed

libharuhishot/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ mod state;
66
mod utils;
77

88
pub use screenshot::{
9-
AreaSelectCallback, CaptureOption, ClipImageViewInfo, ImageInfo, ImageViewInfo,
9+
AreaSelectCallback, CaptureOption, ClipImageViewInfo, ClipImageViewInfoArea, ImageInfo,
10+
ImageViewInfo,
1011
};
1112
pub use state::*;
1213
pub use utils::*;

libharuhishot/src/screenshot.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ pub struct ImageInfo {
125125
pub width: u32,
126126
pub height: u32,
127127
pub color_type: ColorType,
128+
pub transform: wl_output::Transform,
128129
}
129130

130131
#[allow(unused)]
@@ -179,6 +180,12 @@ pub struct ImageViewInfo {
179180

180181
#[derive(Debug, Clone)]
181182
pub struct ClipImageViewInfo {
183+
pub region: Region,
184+
pub areas: Vec<ClipImageViewInfoArea>,
185+
}
186+
187+
#[derive(Debug, Clone)]
188+
pub struct ClipImageViewInfoArea {
182189
pub info: ImageInfo,
183190
pub region: ClipRegion,
184191
}
@@ -465,6 +472,7 @@ impl HaruhiShotState {
465472
width,
466473
height,
467474
frame_format,
475+
transform,
468476
..
469477
} = self.capture_output_inner(output, option, mem_file.as_fd(), Some(&mem_file))?;
470478

@@ -478,6 +486,7 @@ impl HaruhiShotState {
478486
width,
479487
height,
480488
color_type,
489+
transform,
481490
})
482491
}
483492

@@ -493,6 +502,7 @@ impl HaruhiShotState {
493502
width,
494503
height,
495504
frame_format,
505+
transform,
496506
..
497507
} = self.capture_toplevel_inner(toplevel, option, mem_file.as_fd(), Some(&mem_file))?;
498508

@@ -506,6 +516,7 @@ impl HaruhiShotState {
506516
width,
507517
height,
508518
color_type,
519+
transform,
509520
})
510521
}
511522

@@ -514,7 +525,7 @@ impl HaruhiShotState {
514525
&mut self,
515526
option: CaptureOption,
516527
callback: F,
517-
) -> Result<Vec<ClipImageViewInfo>, HaruhiError>
528+
) -> Result<ClipImageViewInfo, HaruhiError>
518529
where
519530
F: AreaSelectCallback,
520531
{
@@ -606,17 +617,21 @@ impl HaruhiShotState {
606617
let converter = crate::convert::create_converter(shotdata.data.frame_format).unwrap();
607618
let color_type = converter.convert_inplace(&mut frame_mmap);
608619

609-
areas.push(ClipImageViewInfo {
620+
areas.push(ClipImageViewInfoArea {
610621
info: ImageInfo {
611622
data: frame_mmap.deref().into(),
612623
width: shotdata.data.width,
613624
height: shotdata.data.height,
614625
color_type,
626+
transform: shotdata.data.transform,
615627
},
616628
region: area,
617629
})
618630
}
619-
Ok(areas)
631+
Ok(ClipImageViewInfo {
632+
region,
633+
areas,
634+
})
620635
}
621636
}
622637

@@ -693,6 +708,21 @@ impl AreaShotInfo {
693708
position: point,
694709
size,
695710
} = region;
711+
let mut size_width = size.width;
712+
let mut size_height = size.height;
713+
714+
// top_right
715+
{
716+
let mut point = point;
717+
point.x += size.width;
718+
size_width = size_width.min(point.x - screen_position.x);
719+
}
720+
// bottom left
721+
{
722+
let mut point = point;
723+
point.y += size.height;
724+
size_height = size_height.min(point.y - screen_position.y);
725+
}
696726
let mut relative_point_real = point - screen_position;
697727
relative_point_real.x = relative_point_real.x.max(0);
698728
relative_point_real.y = relative_point_real.y.max(0);
@@ -709,17 +739,17 @@ impl AreaShotInfo {
709739
relative_region_real: Region {
710740
position: relative_point_real,
711741
size: Size {
712-
width: (size.width as f64 * width as f64 / real_width as f64)
742+
width: (size_width as f64 * width as f64 / real_width as f64)
713743
.min(max_width as f64) as i32,
714-
height: (size.height as f64 * height as f64 / real_height as f64)
744+
height: (size_height as f64 * height as f64 / real_height as f64)
715745
.min(max_height as f64) as i32,
716746
},
717747
},
718748
relative_region_wl: Region {
719749
position,
720750
size: Size {
721-
width: size.width.min(max_width_wl as i32),
722-
height: size.height.min(max_height_wl as i32),
751+
width: size_width.min(max_width_wl as i32),
752+
height: size_height.min(max_height_wl as i32),
723753
},
724754
},
725755
display_region: self.data.region_real(),

libharuhishot/src/utils.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ pub struct ClipRegion {
7373
}
7474

7575
impl ClipRegion {
76+
pub fn display_position_real(&self) -> Position {
77+
self.display_region.position
78+
}
79+
pub fn display_logical_size(&self) -> Size {
80+
self.display_region.size
81+
}
7682
/// the the real absolute position
7783
/// NOTE: no wayland version, because the screen position is real
7884
pub fn absolute_position_real(&self) -> Position {

src/main.rs

Lines changed: 93 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use clap::Parser;
44
use dialoguer::FuzzySelect;
55
use dialoguer::theme::ColorfulTheme;
66
use image::codecs::png::PngEncoder;
7-
use image::{GenericImageView, ImageEncoder, ImageError};
7+
use image::{GenericImageView, ImageEncoder, ImageError, Rgba};
88
pub use libharuhishot::HaruhiShotState;
9+
use libharuhishot::reexport::Transform;
910
use libharuhishot::{
10-
CaptureOption, ClipImageViewInfo, ClipRegion, ImageInfo, Position, Region, Size,
11+
CaptureOption, ClipImageViewInfoArea, ClipRegion, ImageInfo, Position, Region, Size,
1112
};
1213

1314
use std::io::{BufWriter, Write, stdout};
@@ -148,9 +149,9 @@ fn capture_area(
148149
let mut min_y = i32::MAX;
149150
let mut max_x = i32::MIN;
150151
let mut max_y = i32::MIN;
151-
for view in &views {
152-
let Position { x, y } = view.region.absolute_position_real();
153-
let Size { width, height } = view.region.clip_size_real();
152+
for view in &views.areas {
153+
let Position { x, y } = view.region.display_position_real();
154+
let Size { width, height } = view.region.display_logical_size();
154155

155156
min_x = min_x.min(x);
156157
min_y = min_y.min(y);
@@ -161,16 +162,17 @@ fn capture_area(
161162
let total_height = (max_y - min_y) as u32;
162163

163164
let mut combined_image = image::RgbaImage::new(total_width, total_height);
164-
for ClipImageViewInfo {
165+
for ClipImageViewInfoArea {
165166
info:
166167
ImageInfo {
167168
data,
168169
width: img_width,
169170
height: img_height,
171+
transform,
170172
..
171173
},
172174
region,
173-
} in views
175+
} in views.areas
174176
{
175177
// Load the captured image
176178
let img = image::ImageBuffer::from_raw(img_width, img_height, data).ok_or(
@@ -181,43 +183,66 @@ fn capture_area(
181183
)),
182184
)?;
183185

184-
// we use the relative position to make image
185-
let Position { x, y } = region.relative_position_wl();
186-
187-
let Size { width, height } = region.clip_size_real();
188-
189-
let subimage = img
190-
.view(x as u32, y as u32, width as u32, height as u32)
191-
.to_image();
192-
let rgba_img: image::RgbaImage = subimage;
193-
186+
let img = match transform {
187+
Transform::Normal => img,
188+
Transform::_90 => image::imageops::rotate90(&img),
189+
Transform::_180 => image::imageops::rotate180(&img),
190+
Transform::_270 => image::imageops::rotate270(&img),
191+
Transform::Flipped => image::imageops::flip_vertical(&img),
192+
Transform::Flipped90 => {
193+
image::imageops::flip_vertical(&image::imageops::rotate90(&img))
194+
}
195+
Transform::Flipped180 => {
196+
image::imageops::flip_vertical(&image::imageops::rotate180(&img))
197+
}
198+
Transform::Flipped270 => {
199+
image::imageops::flip_vertical(&image::imageops::rotate270(&img))
200+
}
201+
_ => unreachable!(),
202+
};
203+
let Size { width, height } = region.display_logical_size();
204+
let img = image::imageops::resize(
205+
&img,
206+
width as u32,
207+
height as u32,
208+
image::imageops::FilterType::Gaussian,
209+
);
194210
// we use the real position to calculate the position
195-
let Position { x, y } = region.absolute_position_real();
211+
let Position { x, y } = region.display_position_real();
196212
// Calculate the position in he combined image
197213
let offset_x = (x - min_x) as u32;
198214
let offset_y = (y - min_y) as u32;
199215

200216
// Copy the output image to the combined image
201-
for (x, y, pixel) in rgba_img.enumerate_pixels() {
217+
for (x, y, pixel) in img.enumerate_pixels() {
202218
let target_x = offset_x + x;
203219
let target_y = offset_y + y;
204220
if target_x < total_width && target_y < total_height {
205221
combined_image.put_pixel(target_x, target_y, *pixel);
206222
}
207223
}
208224
}
225+
let clip_region = views.region;
226+
let image = combined_image
227+
.view(
228+
clip_region.position.x as u32,
229+
clip_region.position.y as u32,
230+
clip_region.size.width as u32,
231+
clip_region.size.height as u32,
232+
)
233+
.to_image();
209234

210235
if use_stdout {
211236
let mut buff = std::io::Cursor::new(Vec::new());
212-
combined_image.write_to(&mut buff, image::ImageFormat::Png)?;
237+
image.write_to(&mut buff, image::ImageFormat::Png)?;
213238
let content = buff.get_ref();
214239
let stdout = stdout();
215240
let mut writer = BufWriter::new(stdout.lock());
216241
writer.write_all(content)?;
217242
Ok(HaruhiShotResult::StdoutSucceeded)
218243
} else {
219244
let file = random_file_path();
220-
combined_image.save(&file)?;
245+
image.save(&file)?;
221246
Ok(HaruhiShotResult::SaveToFile(file))
222247
}
223248
}
@@ -233,13 +258,14 @@ fn get_color(state: &mut HaruhiShotState) -> Result<HaruhiShotResult, HaruhiImag
233258
))?;
234259
waysip_to_region(info.size(), info.left_top_point())
235260
})?;
236-
let ClipImageViewInfo {
261+
let ClipImageViewInfoArea {
237262
info:
238263
ImageInfo {
239264
data,
240265
width: img_width,
241266
height: img_height,
242-
color_type,
267+
transform,
268+
..
243269
},
244270
region:
245271
ClipRegion {
@@ -250,11 +276,24 @@ fn get_color(state: &mut HaruhiShotState) -> Result<HaruhiShotResult, HaruhiImag
250276
},
251277
..
252278
},
253-
} = views.remove(0);
254-
255-
let mut buff = std::io::Cursor::new(Vec::new());
256-
PngEncoder::new(&mut buff).write_image(&data, img_width, img_height, color_type.into())?;
257-
let img = image::load_from_memory_with_format(buff.get_ref(), image::ImageFormat::Png).unwrap();
279+
} = views.areas.remove(0);
280+
let image: image::ImageBuffer<Rgba<u8>, Vec<u8>> =
281+
image::ImageBuffer::from_raw(img_width, img_height, data).unwrap();
282+
let img = match transform {
283+
Transform::Normal => image,
284+
Transform::_90 => image::imageops::rotate90(&image),
285+
Transform::_180 => image::imageops::rotate180(&image),
286+
Transform::_270 => image::imageops::rotate270(&image),
287+
Transform::Flipped => image::imageops::flip_vertical(&image),
288+
Transform::Flipped90 => image::imageops::flip_vertical(&image::imageops::rotate90(&image)),
289+
Transform::Flipped180 => {
290+
image::imageops::flip_vertical(&image::imageops::rotate180(&image))
291+
}
292+
Transform::Flipped270 => {
293+
image::imageops::flip_vertical(&image::imageops::rotate270(&image))
294+
}
295+
_ => unreachable!(),
296+
};
258297

259298
let clipimage = img.view(x as u32, y as u32, width as u32, height as u32);
260299
let pixel = clipimage.get_pixel(0, 0);
@@ -342,15 +381,33 @@ fn capture_fullscreen(
342381
let Size { width, height } = output.logical_size();
343382
let image_info =
344383
state.capture_single_output(pointer.to_capture_option(), output.clone())?;
345-
346-
// Load the captured image
347-
let img = image::imageops::resize(
348-
&image::ImageBuffer::from_raw(image_info.width, image_info.height, image_info.data)
384+
let image =
385+
image::ImageBuffer::from_raw(image_info.width, image_info.height, image_info.data)
349386
.ok_or(HaruhiImageWriteError::ImageError(ImageError::Parameter(
350387
image::error::ParameterError::from_kind(
351388
image::error::ParameterErrorKind::DimensionMismatch,
352389
),
353-
)))?,
390+
)))?;
391+
let image = match image_info.transform {
392+
Transform::Normal => image,
393+
Transform::_90 => image::imageops::rotate90(&image),
394+
Transform::_180 => image::imageops::rotate180(&image),
395+
Transform::_270 => image::imageops::rotate270(&image),
396+
Transform::Flipped => image::imageops::flip_vertical(&image),
397+
Transform::Flipped90 => {
398+
image::imageops::flip_vertical(&image::imageops::rotate90(&image))
399+
}
400+
Transform::Flipped180 => {
401+
image::imageops::flip_vertical(&image::imageops::rotate180(&image))
402+
}
403+
Transform::Flipped270 => {
404+
image::imageops::flip_vertical(&image::imageops::rotate270(&image))
405+
}
406+
_ => unreachable!(),
407+
};
408+
// Load the captured image
409+
let img = image::imageops::resize(
410+
&image,
354411
width as u32,
355412
height as u32,
356413
image::imageops::FilterType::Gaussian,
@@ -378,6 +435,7 @@ fn capture_fullscreen(
378435
width: total_width,
379436
height: total_height,
380437
color_type: image::ColorType::Rgba8,
438+
transform: libharuhishot::reexport::Transform::Normal,
381439
};
382440

383441
write_to_image(combined_image_info, use_stdout)
@@ -453,6 +511,7 @@ fn write_to_stdout(
453511
width,
454512
height,
455513
color_type,
514+
..
456515
}: ImageInfo,
457516
) -> Result<HaruhiShotResult, HaruhiImageWriteError> {
458517
let stdout = stdout();
@@ -467,6 +526,7 @@ fn write_to_file(
467526
width,
468527
height,
469528
color_type,
529+
..
470530
}: ImageInfo,
471531
) -> Result<HaruhiShotResult, HaruhiImageWriteError> {
472532
let file = random_file_path();

0 commit comments

Comments
 (0)