atmos/os_lib/web_engine/
image_budget.rs1extern crate alloc;
19
20pub const MAX_PIXELS: u32 = 2_000_000;
23
24pub const MAX_IMAGE_BYTES: usize = 2 * 1024 * 1024;
26
27#[derive(Debug, PartialEq, Eq, Clone, Copy)]
29pub enum ImageDecision {
30 Accept,
32 Skip(SkipReason),
34}
35
36#[derive(Debug, PartialEq, Eq, Clone, Copy)]
38pub enum SkipReason {
39 TooManyPixels { pixels: u32 },
41 TooManyBytes { bytes: usize },
43 InvalidDimensions { width: u32, height: u32 },
45}
46
47pub fn pixel_count(width: u32, height: u32) -> Option<u32> {
49 width.checked_mul(height)
50}
51
52pub fn decide_by_dimensions(width: u32, height: u32) -> ImageDecision {
54 if width == 0 || height == 0 {
55 return ImageDecision::Skip(SkipReason::InvalidDimensions { width, height });
56 }
57 match pixel_count(width, height) {
58 None => ImageDecision::Skip(SkipReason::TooManyPixels { pixels: u32::MAX }),
60 Some(p) if p > MAX_PIXELS => ImageDecision::Skip(SkipReason::TooManyPixels { pixels: p }),
61 Some(_) => ImageDecision::Accept,
62 }
63}
64
65pub fn decide_by_bytes(bytes: usize) -> ImageDecision {
67 if bytes > MAX_IMAGE_BYTES {
68 return ImageDecision::Skip(SkipReason::TooManyBytes { bytes });
69 }
70 ImageDecision::Accept
71}
72
73pub fn jpeg_dimensions(data: &[u8]) -> Option<(u32, u32)> {
79 if data.len() < 4 || data[0] != 0xFF || data[1] != 0xD8 {
80 return None;
81 }
82 let mut i = 2usize;
83 while i + 3 < data.len() {
84 if data[i] != 0xFF {
85 i += 1;
86 continue;
87 }
88 let marker = data[i + 1];
89 if marker == 0xD8 || marker == 0xD9 || (0xD0..=0xD7).contains(&marker) || marker == 0x01 {
91 i += 2;
92 continue;
93 }
94 let is_sof = matches!(marker, 0xC0..=0xC3 | 0xC5..=0xC7 | 0xC9..=0xCB | 0xCD..=0xCF);
95 if is_sof {
96 if i + 9 >= data.len() {
98 return None;
99 }
100 let h = ((data[i + 5] as u32) << 8) | data[i + 6] as u32;
101 let w = ((data[i + 7] as u32) << 8) | data[i + 8] as u32;
102 return Some((w, h));
103 }
104 let seg_len = ((data[i + 2] as usize) << 8) | data[i + 3] as usize;
105 if seg_len < 2 {
106 return None;
107 }
108 i += 2 + seg_len;
109 }
110 None
111}
112
113pub fn describe(reason: SkipReason) -> &'static str {
115 match reason {
116 SkipReason::TooManyPixels { .. } => "画素数が多すぎる",
117 SkipReason::TooManyBytes { .. } => "ファイルが大きすぎる",
118 SkipReason::InvalidDimensions { .. } => "幅または高さが 0",
119 }
120}