1use image::{DynamicImage, GenericImageView};
2
3use crate::{Vec2d, ZoomError};
4use crate::dezoomer::{PostProcessFn, TileReference};
5use crate::errors::BufferToImageError;
6use crate::network::fetch_uri;
7
8#[derive(Clone)]
9pub struct Tile {
10 pub image: image::DynamicImage,
11 pub position: Vec2d,
12}
13
14impl Tile {
15 pub fn size(&self) -> Vec2d { self.image.dimensions().into() }
16 pub fn bottom_right(&self) -> Vec2d {
17 self.size() + self.position
18 }
19 pub async fn download(
20 post_process_fn: PostProcessFn,
21 tile_reference: &TileReference,
22 client: &reqwest::Client,
23 ) -> Result<Tile, ZoomError> {
24 let bytes = fetch_uri(&tile_reference.url, client).await?;
25 let tile_reference = tile_reference.clone();
26
27 let tile: Result<Tile, BufferToImageError> = tokio::spawn(async move {
28 tokio::task::block_in_place(move || {
29 let transformed_bytes =
30 if let PostProcessFn::Fn(post_process) = post_process_fn {
31 post_process(&tile_reference, bytes)
32 .map_err(|e| BufferToImageError::PostProcessing { e })?
33 } else {
34 bytes
35 };
36
37 Ok(Tile {
38 image: image::load_from_memory(&transformed_bytes)?,
39 position: tile_reference.position,
40 })
41 })
42 }).await?;
43 Ok(tile?)
44 }
45 pub fn empty(position: Vec2d, size: Vec2d) -> Tile {
46 Tile { image: DynamicImage::new_rgba8(size.x, size.y), position }
47 }
48 pub fn position(&self) -> Vec2d {
49 self.position
50 }
51}
52
53impl std::fmt::Debug for Tile {
54 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
55 f.debug_struct("Tile")
56 .field("x", &self.position.x)
57 .field("y", &self.position.y)
58 .field("width", &self.image.width())
59 .field("height", &self.image.height())
60 .finish()
61 }
62}
63
64impl PartialEq for Tile {
65 fn eq(&self, other: &Self) -> bool {
66 self.position == other.position &&
67 self.size() == other.size() &&
68 self.image.pixels().all(|(x, y, pix)| {
69 other.image.get_pixel(x, y) == pix
70 })
71 }
72}