@@ -248,6 +248,85 @@ impl Icon {
248248 big : crate :: default_icon:: BIG ,
249249 }
250250 }
251+
252+ /// Constructs an icon from the given bytes.
253+ /// The bytes must be RGBA pixels (each 4 * u8) in row-major order.
254+ /// The image must be a square and its side length must be a power of two.
255+ ///
256+ /// Returns `None` if the above conditions are not fulfilled.
257+ pub fn from_bytes ( bytes : & [ u8 ] ) -> Option < Self > {
258+ fn adjust < const N : usize > ( bytes : & [ u8 ] , side_len : usize , desired_len : usize ) -> [ u8 ; N ] {
259+ let diff = desired_len. ilog2 ( ) as i32 - side_len. ilog2 ( ) as i32 ;
260+
261+ let mut arr = [ 0u8 ; N ] ;
262+ if diff > 0 {
263+ // upscale
264+ let n = diff as u16 + 1 ;
265+ let mut write_index = 0 ;
266+ let mut current_line_length = 0 ;
267+
268+ for pixel in bytes. chunks ( 4 ) {
269+ // repeat n times horizontally
270+ for _ in 0 ..n {
271+ arr[ write_index] = pixel[ 0 ] ;
272+ arr[ write_index + 1 ] = pixel[ 1 ] ;
273+ arr[ write_index + 2 ] = pixel[ 2 ] ;
274+ arr[ write_index + 3 ] = pixel[ 3 ] ;
275+ write_index += 4 ;
276+ }
277+ current_line_length += 1 ;
278+
279+ if current_line_length == side_len {
280+ // repeat n - 1 times vertically, because one line already exists
281+ let last_line =
282+ arr[ ( write_index - ( 4 * side_len * n as usize ) ) ..write_index] . to_vec ( ) ;
283+ for _ in 0 ..n - 1 {
284+ for i in 0 ..last_line. len ( ) {
285+ arr[ write_index] = last_line[ i] ;
286+ write_index += 1 ;
287+ }
288+ }
289+ current_line_length = 0 ;
290+ }
291+ }
292+ } else if diff == 0 {
293+ arr. copy_from_slice ( bytes) ;
294+ } else {
295+ // downscale
296+ let n = ( -diff) as usize + 1 ;
297+ let mut write_index = 0 ;
298+
299+ for line in 0 ..side_len {
300+ for column in 0 ..side_len {
301+ if line % n == 0 && column % n == 0 {
302+ let index = usize:: from ( side_len * line + column) * 4 ;
303+ arr[ write_index] = bytes[ index] ;
304+ arr[ write_index + 1 ] = bytes[ index + 1 ] ;
305+ arr[ write_index + 2 ] = bytes[ index + 2 ] ;
306+ arr[ write_index + 3 ] = bytes[ index + 3 ] ;
307+ write_index += 4 ;
308+ }
309+ }
310+ }
311+ } ;
312+
313+ arr
314+ }
315+
316+ if bytes. len ( ) % 4 != 0 {
317+ return None ;
318+ }
319+ let pixel_amount = bytes. len ( ) / 4 ;
320+ let side_len = pixel_amount. isqrt ( ) ;
321+ if side_len * side_len != pixel_amount || !side_len. is_power_of_two ( ) {
322+ return None ;
323+ }
324+ Some ( Self {
325+ small : adjust ( bytes, side_len, 16 ) ,
326+ medium : adjust ( bytes, side_len, 32 ) ,
327+ big : adjust ( bytes, side_len, 64 ) ,
328+ } )
329+ }
251330}
252331// Printing 64x64 array with a default formatter is not meaningfull,
253332// so debug will skip the data fields of an Icon
0 commit comments