1use crate::error::FlacError;
2use crate::prelude::*;
3use crate::utils::*;
4use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
5use num_traits::FromPrimitive;
6use std::borrow::Cow;
7use std::fmt;
8use std::io::{Read, Write};
9use std::path::Path;
10use std::str::FromStr;
11
12pub struct BlockPicture {
13 pub picture_type: PictureType,
17 pub mime_type: String,
21 pub description: String,
24 pub width: u32,
26 pub height: u32,
28 pub depth: u32,
30 pub colors: u32,
32 pub data: Vec<u8>,
35}
36
37impl Decode for BlockPicture {
38 fn from_reader<R: Read>(reader: &mut R) -> Result<Self> {
39 let picture_type: PictureType = FromPrimitive::from_u32(reader.read_u32::<BigEndian>()?)
40 .unwrap_or(PictureType::Unknown);
41 let mime_type_length = reader.read_u32::<BigEndian>()?;
42 let mime_type = take_string(reader, mime_type_length as usize)?;
43 let description_length = reader.read_u32::<BigEndian>()?;
44 let description = take_string(reader, description_length as usize)?;
45
46 let width = reader.read_u32::<BigEndian>()?;
47 let height = reader.read_u32::<BigEndian>()?;
48
49 let depth = reader.read_u32::<BigEndian>()?;
50 let colors = reader.read_u32::<BigEndian>()?;
51
52 let picture_length = reader.read_u32::<BigEndian>()?;
53 let data = take(reader, picture_length as usize)?;
54 Ok(BlockPicture {
55 picture_type,
56 mime_type,
57 description,
58 width,
59 height,
60 depth,
61 colors,
62 data,
63 })
64 }
65}
66
67#[cfg(feature = "async")]
68#[async_trait::async_trait]
69impl AsyncDecode for BlockPicture {
70 async fn from_async_reader<R>(reader: &mut R) -> Result<Self>
71 where
72 R: AsyncRead + Unpin + Send,
73 {
74 let picture_type: PictureType =
75 FromPrimitive::from_u32(reader.read_u32().await?).unwrap_or(PictureType::Unknown);
76 let mime_type_length = reader.read_u32().await?;
77 let mime_type = take_string_async(reader, mime_type_length as usize).await?;
78 let description_length = reader.read_u32().await?;
79 let description = take_string_async(reader, description_length as usize).await?;
80
81 let width = reader.read_u32().await?;
82 let height = reader.read_u32().await?;
83
84 let depth = reader.read_u32().await?;
85 let colors = reader.read_u32().await?;
86
87 let picture_length = reader.read_u32().await?;
88 let data = take_async(reader, picture_length as usize).await?;
89 Ok(BlockPicture {
90 picture_type,
91 mime_type,
92 description,
93 width,
94 height,
95 depth,
96 colors,
97 data,
98 })
99 }
100}
101
102impl Encode for BlockPicture {
103 fn write_to<W: Write>(&self, writer: &mut W) -> Result<()> {
104 writer.write_u32::<BigEndian>(self.picture_type as u32)?;
105
106 writer.write_u32::<BigEndian>(self.mime_type.len() as u32)?;
107 writer.write_all(self.mime_type.as_bytes())?;
108
109 writer.write_u32::<BigEndian>(self.description.len() as u32)?;
110 writer.write_all(self.description.as_bytes())?;
111
112 writer.write_u32::<BigEndian>(self.width)?;
113 writer.write_u32::<BigEndian>(self.height)?;
114
115 writer.write_u32::<BigEndian>(self.depth)?;
116 writer.write_u32::<BigEndian>(self.colors)?;
117
118 writer.write_u32::<BigEndian>(self.data.len() as u32)?;
119 writer.write_all(&self.data)?;
120 Ok(())
121 }
122}
123
124impl fmt::Debug for BlockPicture {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 let mut prefix = "".to_owned();
127 if let Some(width) = f.width() {
128 prefix = " ".repeat(width);
129 }
130 writeln!(
131 f,
132 "{prefix}type: {} ({})",
133 self.picture_type as u8,
134 self.picture_type.as_str(),
135 prefix = prefix
136 )?;
137 writeln!(f, "{prefix}MIME type: {}", self.mime_type, prefix = prefix)?;
138 writeln!(
139 f,
140 "{prefix}description: {}",
141 self.description,
142 prefix = prefix
143 )?;
144 writeln!(f, "{prefix}width: {}", self.width, prefix = prefix)?;
145 writeln!(f, "{prefix}height: {}", self.height, prefix = prefix)?;
146 writeln!(f, "{prefix}depth: {}", self.depth, prefix = prefix)?;
147 writeln!(
148 f,
149 "{prefix}colors: {}{}",
150 self.colors,
151 if self.color_indexed() {
152 ""
153 } else {
154 " (unindexed)"
155 },
156 prefix = prefix
157 )?;
158 writeln!(
159 f,
160 "{prefix}data length: {}",
161 self.data.len(),
162 prefix = prefix
163 )?;
164 Ok(())
165 }
166}
167
168impl BlockPicture {
169 pub fn new<P: AsRef<Path>>(
170 file: P,
171 picture_type: PictureType,
172 description: String,
173 ) -> Result<Self> {
174 let img = image::open(file.as_ref())?;
175 let mut data = Vec::new();
176 std::fs::File::open(file.as_ref())?.read_to_end(&mut data)?;
177
178 let mut ext = file.as_ref().extension().unwrap().to_string_lossy();
179 if ext == "jpg" {
180 ext = Cow::Borrowed("jpeg");
181 }
182
183 Ok(Self {
184 picture_type,
185 mime_type: format!("image/{}", ext),
186 description,
187 width: img.width(),
188 height: img.height(),
189 depth: img.color().bits_per_pixel() as u32,
190 colors: 0, data,
192 })
193 }
194
195 pub fn color_indexed(&self) -> bool {
196 self.colors != 0
197 }
198}
199
200#[repr(u32)]
203#[derive(Copy, Clone, Debug, FromPrimitive, PartialEq, Eq)]
204pub enum PictureType {
205 Other,
207 FileIcon,
209 OtherFileIcon,
211 CoverFront,
213 CoverBack,
215 LeafletPage,
217 Media,
219 LeadArtist,
221 Artist,
223 Conductor,
225 Band,
227 Composer,
229 Lyricist,
231 RecordingLocation,
233 DuringRecording,
235 DuringPerformance,
237 MovieVideoScreenCapture,
239 BrightColoredFish,
241 Illustration,
243 BandArtistLogotype,
245 PublisherStudioLogotype,
247 Unknown,
249}
250
251impl PictureType {
252 pub fn as_str(&self) -> &'static str {
253 match self {
254 PictureType::Other => "Other",
255 PictureType::FileIcon => "32x32 pixels 'file icon' (PNG only)",
256 PictureType::OtherFileIcon => "Other file icon",
257 PictureType::CoverFront => "Cover (front)",
258 PictureType::CoverBack => "Cover (back)",
259 PictureType::LeafletPage => "Leaflet page",
260 PictureType::Media => "Media (e.g. label side of CD)",
261 PictureType::LeadArtist => "Lead artist/lead performer/soloist",
262 PictureType::Artist => "Artist/performer",
263 PictureType::Conductor => "Conductor",
264 PictureType::Band => "Band/Orchestra",
265 PictureType::Composer => "Composer",
266 PictureType::Lyricist => "Lyricist/text writer",
267 PictureType::RecordingLocation => "Recording Location",
268 PictureType::DuringRecording => "During recording",
269 PictureType::DuringPerformance => "During performance",
270 PictureType::MovieVideoScreenCapture => "Movie/video screen capture",
271 PictureType::BrightColoredFish => "A bright coloured fish",
272 PictureType::Illustration => "Illustration",
273 PictureType::BandArtistLogotype => "Band/artist logotype",
274 PictureType::PublisherStudioLogotype => "Publisher/Studio logotype",
275 PictureType::Unknown => "Unknown",
276 }
277 }
278}
279
280impl FromStr for PictureType {
281 type Err = FlacError;
282
283 fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
284 if let Ok(n) = u32::from_str(s) {
285 if n <= 20 {
286 return Ok(FromPrimitive::from_u32(n).unwrap());
288 }
289 }
290
291 match s.to_ascii_lowercase().as_str() {
292 "other" => Ok(PictureType::Other),
293 "file_icon" => Ok(PictureType::FileIcon),
294 "other_file_icon" => Ok(PictureType::OtherFileIcon),
295 "cover" | "front_cover" => Ok(PictureType::CoverFront),
296 "back_cover" => Ok(PictureType::CoverBack),
297 "leaflet" => Ok(PictureType::LeafletPage),
298 "media" => Ok(PictureType::Media),
299 "lead_artist" => Ok(PictureType::LeadArtist),
300 "artist" => Ok(PictureType::Artist),
301 "conductor" => Ok(PictureType::Conductor),
302 "band" => Ok(PictureType::Band),
303 "composer" => Ok(PictureType::Composer),
304 "lyricist" => Ok(PictureType::Lyricist),
305 "recording_location" => Ok(PictureType::RecordingLocation),
306 "during_recording" => Ok(PictureType::DuringRecording),
307 "during_performance" => Ok(PictureType::DuringPerformance),
308 "screen_capture" => Ok(PictureType::MovieVideoScreenCapture),
309 "bright_colored_fish" => Ok(PictureType::BrightColoredFish),
310 "illustration" => Ok(PictureType::Illustration),
311 "band_logo" | "artist_logo" => Ok(PictureType::BandArtistLogotype),
312 "publisher_logo" | "studio_logo" => Ok(PictureType::PublisherStudioLogotype),
313 &_ => Err(Self::Err::InvalidPictureType),
314 }
315 }
316}