1#[cfg(feature = "image-jxl")]
3use super::jxl::*;
4use crate::ext::io::*;
5use crate::types::*;
6use anyhow::Result;
7use std::convert::TryFrom;
8
9pub fn reverse_alpha_values(data: &mut ImageData) -> Result<()> {
13 if data.color_type != ImageColorType::Rgba && data.color_type != ImageColorType::Bgra {
14 return Err(anyhow::anyhow!("Image is not RGBA or BGRA"));
15 }
16 if data.depth != 8 {
17 return Err(anyhow::anyhow!(
18 "Alpha value reversal only supports 8-bit depth"
19 ));
20 }
21 for i in (0..data.data.len()).step_by(4) {
22 data.data[i + 3] = 255 - data.data[i + 3];
23 }
24 Ok(())
25}
26
27pub fn convert_bgr_to_bgra(data: &mut ImageData) -> Result<()> {
31 if data.color_type != ImageColorType::Bgr {
32 return Err(anyhow::anyhow!("Image is not BGR"));
33 }
34 if data.depth != 8 {
35 return Err(anyhow::anyhow!(
36 "BGR to BGRA conversion only supports 8-bit depth"
37 ));
38 }
39 let mut new_data = Vec::with_capacity(data.data.len() / 3 * 4);
40 for chunk in data.data.chunks_exact(3) {
41 new_data.push(chunk[0]); new_data.push(chunk[1]); new_data.push(chunk[2]); new_data.push(255); }
46 data.data = new_data;
47 data.color_type = ImageColorType::Bgra;
48 Ok(())
49}
50
51pub fn convert_bgr_to_rgb(data: &mut ImageData) -> Result<()> {
55 if data.color_type != ImageColorType::Bgr {
56 return Err(anyhow::anyhow!("Image is not BGR"));
57 }
58 if data.depth != 8 {
59 return Err(anyhow::anyhow!(
60 "BGR to RGB conversion only supports 8-bit depth"
61 ));
62 }
63 for i in (0..data.data.len()).step_by(3) {
64 let b = data.data[i];
65 data.data[i] = data.data[i + 2];
66 data.data[i + 2] = b;
67 }
68 data.color_type = ImageColorType::Rgb;
69 Ok(())
70}
71
72pub fn convert_bgra_to_bgr(data: &mut ImageData) -> Result<()> {
76 if data.color_type != ImageColorType::Bgra {
77 return Err(anyhow::anyhow!("Image is not BGRA"));
78 }
79 if data.depth != 8 {
80 return Err(anyhow::anyhow!(
81 "BGRA to BGR conversion only supports 8-bit depth"
82 ));
83 }
84 let mut new_data = Vec::with_capacity(data.data.len() / 4 * 3);
85 for chunk in data.data.chunks_exact(4) {
86 new_data.push(chunk[0]); new_data.push(chunk[1]); new_data.push(chunk[2]); }
90 data.data = new_data;
91 data.color_type = ImageColorType::Bgr;
92 Ok(())
93}
94
95pub fn convert_bgra_to_rgba(data: &mut ImageData) -> Result<()> {
99 if data.color_type != ImageColorType::Bgra {
100 return Err(anyhow::anyhow!("Image is not BGRA"));
101 }
102 if data.depth != 8 {
103 return Err(anyhow::anyhow!(
104 "BGRA to RGBA conversion only supports 8-bit depth"
105 ));
106 }
107 for i in (0..data.data.len()).step_by(4) {
108 let b = data.data[i];
109 data.data[i] = data.data[i + 2];
110 data.data[i + 2] = b;
111 }
112 data.color_type = ImageColorType::Rgba;
113 Ok(())
114}
115
116pub fn convert_rgb_to_rgba(data: &mut ImageData) -> Result<()> {
120 if data.color_type != ImageColorType::Rgb {
121 return Err(anyhow::anyhow!("Image is not RGB"));
122 }
123 if data.depth != 8 {
124 return Err(anyhow::anyhow!(
125 "RGB to RGBA conversion only supports 8-bit depth"
126 ));
127 }
128 let mut new_data = Vec::with_capacity(data.data.len() / 3 * 4);
129 for chunk in data.data.chunks_exact(3) {
130 new_data.push(chunk[0]); new_data.push(chunk[1]); new_data.push(chunk[2]); new_data.push(255); }
135 data.data = new_data;
136 data.color_type = ImageColorType::Rgba;
137 Ok(())
138}
139
140pub fn convert_rgb_to_bgr(data: &mut ImageData) -> Result<()> {
144 if data.color_type != ImageColorType::Rgb {
145 return Err(anyhow::anyhow!("Image is not RGB"));
146 }
147 if data.depth != 8 {
148 return Err(anyhow::anyhow!(
149 "RGB to BGR conversion only supports 8-bit depth"
150 ));
151 }
152 for i in (0..data.data.len()).step_by(3) {
153 let r = data.data[i];
154 data.data[i] = data.data[i + 2];
155 data.data[i + 2] = r;
156 }
157 data.color_type = ImageColorType::Bgr;
158 Ok(())
159}
160
161pub fn convert_rgba_to_bgra(data: &mut ImageData) -> Result<()> {
165 if data.color_type != ImageColorType::Rgba {
166 return Err(anyhow::anyhow!("Image is not RGBA"));
167 }
168 if data.depth != 8 {
169 return Err(anyhow::anyhow!(
170 "RGBA to BGRA conversion only supports 8-bit depth"
171 ));
172 }
173 for i in (0..data.data.len()).step_by(4) {
174 let r = data.data[i];
175 data.data[i] = data.data[i + 2];
176 data.data[i + 2] = r;
177 }
178 data.color_type = ImageColorType::Bgra;
179 Ok(())
180}
181
182pub fn encode_img(
189 mut data: ImageData,
190 typ: ImageOutputType,
191 filename: &str,
192 config: &ExtraConfig,
193) -> Result<()> {
194 match typ {
195 ImageOutputType::Png => {
196 let mut file = crate::utils::files::write_file(filename)?;
197 let color_type = match data.color_type {
198 ImageColorType::Grayscale => png::ColorType::Grayscale,
199 ImageColorType::Rgb => png::ColorType::Rgb,
200 ImageColorType::Rgba => png::ColorType::Rgba,
201 ImageColorType::Bgr => {
202 convert_bgr_to_rgb(&mut data)?;
203 png::ColorType::Rgb
204 }
205 ImageColorType::Bgra => {
206 convert_bgra_to_rgba(&mut data)?;
207 png::ColorType::Rgba
208 }
209 };
210 let bit_depth = match &data.depth {
211 1 => png::BitDepth::One,
212 2 => png::BitDepth::Two,
213 4 => png::BitDepth::Four,
214 8 => png::BitDepth::Eight,
215 16 => png::BitDepth::Sixteen,
216 _ => return Err(anyhow::anyhow!("Unsupported bit depth: {}", data.depth)),
217 };
218 let mut encoder = png::Encoder::new(&mut file, data.width, data.height);
219 encoder.set_color(color_type);
220 encoder.set_depth(bit_depth);
221 encoder.set_compression(config.png_compression_level.to_compression());
222 let mut writer = encoder.write_header()?;
223 writer.write_image_data(&data.data)?;
224 writer.finish()?;
225 Ok(())
226 }
227 #[cfg(feature = "image-jpg")]
228 ImageOutputType::Jpg => {
229 let file = crate::utils::files::write_file(filename)?;
230 let color_type = match data.color_type {
231 ImageColorType::Grayscale => mozjpeg::ColorSpace::JCS_GRAYSCALE,
232 ImageColorType::Rgb => mozjpeg::ColorSpace::JCS_RGB,
233 ImageColorType::Rgba => mozjpeg::ColorSpace::JCS_EXT_RGBA,
234 ImageColorType::Bgr => {
235 convert_bgr_to_rgb(&mut data)?;
236 mozjpeg::ColorSpace::JCS_RGB
237 }
238 ImageColorType::Bgra => {
239 convert_bgra_to_rgba(&mut data)?;
240 mozjpeg::ColorSpace::JCS_EXT_RGBA
241 }
242 };
243 if data.depth != 8 {
244 return Err(anyhow::anyhow!(
245 "JPEG encoding only supports 8-bit depth, found: {}",
246 data.depth
247 ));
248 }
249 let mut encoder = mozjpeg::compress::Compress::new(color_type);
250 encoder.set_size(data.width as usize, data.height as usize);
251 encoder.set_quality(config.jpeg_quality as f32);
252 let mut start = encoder.start_compress(file)?;
253 start.write_scanlines(&data.data)?;
254 start.finish()?;
255 Ok(())
256 }
257 #[cfg(feature = "image-webp")]
258 ImageOutputType::Webp => {
259 let mut file = crate::utils::files::write_file(filename)?;
260 let color_type = match data.color_type {
261 ImageColorType::Rgb => webp::PixelLayout::Rgb,
262 ImageColorType::Rgba => webp::PixelLayout::Rgba,
263 ImageColorType::Bgr => {
264 convert_bgr_to_rgb(&mut data)?;
265 webp::PixelLayout::Rgb
266 }
267 ImageColorType::Bgra => {
268 convert_bgra_to_rgba(&mut data)?;
269 webp::PixelLayout::Rgba
270 }
271 _ => {
272 return Err(anyhow::anyhow!(
273 "Unsupported color type for WebP: {:?}",
274 data.color_type
275 ));
276 }
277 };
278 if data.depth != 8 {
279 return Err(anyhow::anyhow!(
280 "WebP encoding only supports 8-bit depth, found: {}",
281 data.depth
282 ));
283 }
284 let encoder = webp::Encoder::new(&data.data, color_type, data.width, data.height);
285 let re = encoder
286 .encode_simple(config.webp_lossless, config.webp_quality as f32)
287 .map_err(|e| anyhow::anyhow!("Failed to encode WebP image: {:?}", e))?;
288 file.write_all(&re)?;
289 Ok(())
290 }
291 #[cfg(feature = "image-jxl")]
292 ImageOutputType::Jxl => {
293 let mut file = crate::utils::files::write_file(filename)?;
294 let data = encode_jxl(data, config)?;
295 file.write_all(&data)?;
296 Ok(())
297 }
298 }
299}
300
301pub fn load_png<R: std::io::Read + std::io::Seek>(data: R) -> Result<ImageData> {
303 let decoder = png::Decoder::new(std::io::BufReader::new(data));
304 let mut reader = decoder.read_info()?;
305 let bit_depth = match reader.info().bit_depth {
306 png::BitDepth::One => 1,
307 png::BitDepth::Two => 2,
308 png::BitDepth::Four => 4,
309 png::BitDepth::Eight => 8,
310 png::BitDepth::Sixteen => 16,
311 };
312 let color_type = match reader.info().color_type {
313 png::ColorType::Grayscale => ImageColorType::Grayscale,
314 png::ColorType::Rgb => ImageColorType::Rgb,
315 png::ColorType::Rgba => ImageColorType::Rgba,
316 _ => {
317 return Err(anyhow::anyhow!(
318 "Unsupported color type: {:?}",
319 reader.info().color_type
320 ));
321 }
322 };
323 let stride = reader.info().width as usize * color_type.bpp(bit_depth) as usize / 8;
324 let mut data = vec![0; stride * reader.info().height as usize];
325 reader.next_frame(&mut data)?;
326 Ok(ImageData {
327 width: reader.info().width,
328 height: reader.info().height,
329 depth: bit_depth,
330 color_type,
331 data,
332 })
333}
334
335#[cfg(feature = "mozjpeg")]
336pub fn load_jpg<R: std::io::Read>(data: R) -> Result<ImageData> {
337 let decoder = mozjpeg::decompress::Decompress::new_reader(std::io::BufReader::new(data))?;
338 let color_type = match decoder.color_space() {
339 mozjpeg::ColorSpace::JCS_GRAYSCALE => ImageColorType::Grayscale,
340 mozjpeg::ColorSpace::JCS_RGB => ImageColorType::Rgb,
341 mozjpeg::ColorSpace::JCS_EXT_RGBA => ImageColorType::Rgba,
342 _ => ImageColorType::Rgb, };
344 let width = decoder.width() as u32;
345 let height = decoder.height() as u32;
346 let stride = width as usize * color_type.bpp(8) as usize / 8;
347 let mut data = vec![0; stride * height as usize];
348 let mut re = match color_type {
349 ImageColorType::Grayscale => decoder.grayscale()?,
350 ImageColorType::Rgb => decoder.rgb()?,
351 ImageColorType::Rgba => decoder.rgba()?,
352 _ => {
353 unreachable!(); }
355 };
356 re.read_scanlines_into(&mut data)?;
357 Ok(ImageData {
358 width,
359 height,
360 depth: 8,
361 color_type,
362 data,
363 })
364}
365
366pub fn decode_img(typ: ImageOutputType, filename: &str) -> Result<ImageData> {
371 match typ {
372 ImageOutputType::Png => {
373 let file = crate::utils::files::read_file(filename)?;
374 let reader = MemReader::new(file);
375 load_png(reader)
376 }
377 #[cfg(feature = "image-jpg")]
378 ImageOutputType::Jpg => {
379 let file = crate::utils::files::read_file(filename)?;
380 load_jpg(&file[..])
381 }
382 #[cfg(feature = "image-webp")]
383 ImageOutputType::Webp => {
384 let file = crate::utils::files::read_file(filename)?;
385 let decoder = webp::Decoder::new(&file);
386 let image = decoder
387 .decode()
388 .ok_or(anyhow::anyhow!("Failed to decode WebP image"))?;
389 let color_type = if image.is_alpha() {
390 ImageColorType::Rgba
391 } else {
392 ImageColorType::Rgb
393 };
394 let width = image.width();
395 let height = image.height();
396 let stride = width as usize * color_type.bpp(8) as usize / 8;
397 let mut data = vec![0; stride * height as usize];
398 if image.len() != data.len() {
399 return Err(anyhow::anyhow!(
400 "WebP image data size mismatch: expected {}, got {}",
401 data.len(),
402 image.len()
403 ));
404 }
405 data.copy_from_slice(&image);
406 Ok(ImageData {
407 width,
408 height,
409 depth: 8,
410 color_type,
411 data,
412 })
413 }
414 #[cfg(feature = "image-jxl")]
415 ImageOutputType::Jxl => {
416 let file = crate::utils::files::read_file(filename)?;
417 decode_jxl(&file[..])
418 }
419 }
420}
421
422pub fn draw_on_canvas(
432 img: ImageData,
433 canvas_width: u32,
434 canvas_height: u32,
435 offset_x: u32,
436 offset_y: u32,
437) -> Result<ImageData> {
438 let bytes_per_pixel = img.color_type.bpp(img.depth) as u32 / 8;
439 let mut canvas_data = vec![0u8; (canvas_width * canvas_height * bytes_per_pixel) as usize];
440 let canvas_stride = canvas_width * bytes_per_pixel;
441 let img_stride = img.width * bytes_per_pixel;
442
443 for y in 0..img.height {
444 let canvas_y = y + offset_y;
445 if canvas_y >= canvas_height {
446 continue;
447 }
448 let canvas_start = (canvas_y * canvas_stride + offset_x * bytes_per_pixel) as usize;
449 let img_start = (y * img_stride) as usize;
450 let copy_len = img_stride as usize;
451 if canvas_start + copy_len > canvas_data.len() {
452 continue;
453 }
454 canvas_data[canvas_start..canvas_start + copy_len]
455 .copy_from_slice(&img.data[img_start..img_start + copy_len]);
456 }
457
458 Ok(ImageData {
459 width: canvas_width,
460 height: canvas_height,
461 color_type: img.color_type,
462 depth: img.depth,
463 data: canvas_data,
464 })
465}
466
467pub fn flip_image(data: &mut ImageData) -> Result<()> {
469 if data.height <= 1 {
470 return Ok(());
471 }
472 let row_size = data.color_type.bpp(data.depth) as usize * data.width as usize / 8;
473 if row_size == 0 {
474 return Ok(());
475 }
476
477 let mut i = 0;
478 let mut j = data.height as usize - 1;
479 while i < j {
480 let (top, bottom) = data.data.split_at_mut(j * row_size);
481 let top_row = &mut top[i * row_size..i * row_size + row_size];
482 let bottom_row = &mut bottom[0..row_size];
483 top_row.swap_with_slice(bottom_row);
484 i += 1;
485 j -= 1;
486 }
487
488 Ok(())
489}
490
491pub fn apply_opacity(img: &mut ImageData, opacity: u8) -> Result<()> {
495 if img.color_type != ImageColorType::Rgba && img.color_type != ImageColorType::Bgra {
496 return Err(anyhow::anyhow!("Image is not RGBA or BGRA"));
497 }
498 if img.depth != 8 {
499 return Err(anyhow::anyhow!(
500 "Opacity application only supports 8-bit depth"
501 ));
502 }
503 for i in (0..img.data.len()).step_by(4) {
504 img.data[i + 3] = (img.data[i + 3] as u16 * opacity as u16 / 255) as u8;
505 }
506 Ok(())
507}
508
509pub fn draw_on_img_with_opacity(
517 base: &mut ImageData,
518 diff: &ImageData,
519 left: u32,
520 top: u32,
521 opacity: u8,
522) -> Result<()> {
523 if base.color_type != diff.color_type {
524 return Err(anyhow::anyhow!("Image color types do not match"));
525 }
526 if base.color_type != ImageColorType::Rgba && base.color_type != ImageColorType::Bgra {
527 return Err(anyhow::anyhow!("Images are not RGBA or BGRA"));
528 }
529 if base.depth != 8 || diff.depth != 8 {
530 return Err(anyhow::anyhow!(
531 "Image drawing with opacity only supports 8-bit depth"
532 ));
533 }
534
535 let bpp = 4;
536 let base_stride = base.width as usize * bpp;
537 let diff_stride = diff.width as usize * bpp;
538
539 for y in 0..diff.height {
540 let base_y = top + y;
541 if base_y >= base.height {
542 continue;
543 }
544
545 for x in 0..diff.width {
546 let base_x = left + x;
547 if base_x >= base.width {
548 continue;
549 }
550
551 let diff_idx = (y as usize * diff_stride) + (x as usize * bpp);
552 let base_idx = (base_y as usize * base_stride) + (base_x as usize * bpp);
553
554 let diff_pixel = &diff.data[diff_idx..diff_idx + bpp];
555 let base_pixel_orig = base.data[base_idx..base_idx + bpp].to_vec();
556
557 let src_alpha_u16 = (diff_pixel[3] as u16 * opacity as u16) / 255;
558
559 if src_alpha_u16 == 0 {
560 continue;
561 }
562
563 let dst_alpha_u16 = base_pixel_orig[3] as u16;
564
565 let out_alpha_u16 = src_alpha_u16 + (dst_alpha_u16 * (255 - src_alpha_u16)) / 255;
567
568 if out_alpha_u16 == 0 {
569 for i in 0..4 {
570 base.data[base_idx + i] = 0;
571 }
572 continue;
573 }
574
575 for i in 0..3 {
577 let src_comp = diff_pixel[i] as u16;
578 let dst_comp = base_pixel_orig[i] as u16;
579
580 let numerator = src_comp * src_alpha_u16
581 + (dst_comp * dst_alpha_u16 * (255 - src_alpha_u16)) / 255;
582 base.data[base_idx + i] = (numerator / out_alpha_u16) as u8;
583 }
584 base.data[base_idx + 3] = out_alpha_u16 as u8;
585 }
586 }
587
588 Ok(())
589}
590
591#[derive(Debug, Clone, Copy, PartialEq, Eq)]
592pub enum PaletteFormat {
593 Rgb,
595 Bgr,
597 RgbX,
599 BgrX,
601 RgbA,
603 BgrA,
605}
606
607pub fn convert_index_palette_to_normal_bitmap(
611 pixel_data: &[u8],
612 pixel_size: usize,
613 palettes: &[u8],
614 palette_format: PaletteFormat,
615 width: usize,
616 height: usize,
617) -> Result<ImageData> {
618 if width == 0 || height == 0 {
619 return Err(anyhow::anyhow!("Image dimensions must be non-zero"));
620 }
621 if pixel_size == 0 {
622 return Err(anyhow::anyhow!("pixel_size must be greater than zero"));
623 }
624
625 let width_u32 =
626 u32::try_from(width).map_err(|_| anyhow::anyhow!("width exceeds u32::MAX: {}", width))?;
627 let height_u32 = u32::try_from(height)
628 .map_err(|_| anyhow::anyhow!("height exceeds u32::MAX: {}", height))?;
629
630 let pixel_count = width
631 .checked_mul(height)
632 .ok_or_else(|| anyhow::anyhow!("Image dimensions overflow: {}x{}", width, height))?;
633
634 let palette_entry_size = match palette_format {
635 PaletteFormat::Rgb | PaletteFormat::Bgr => 3usize,
636 PaletteFormat::RgbX | PaletteFormat::BgrX | PaletteFormat::RgbA | PaletteFormat::BgrA => {
637 4usize
638 }
639 };
640
641 if palettes.len() < palette_entry_size {
642 return Err(anyhow::anyhow!("Palette data is too small"));
643 }
644 if palettes.len() % palette_entry_size != 0 {
645 return Err(anyhow::anyhow!(
646 "Palette length {} is not a multiple of {}",
647 palettes.len(),
648 palette_entry_size
649 ));
650 }
651 let palette_color_count = palettes.len() / palette_entry_size;
652 if palette_color_count == 0 {
653 return Err(anyhow::anyhow!("Palette does not contain any colors"));
654 }
655
656 let (color_type, output_channels) = match palette_format {
657 PaletteFormat::Rgb | PaletteFormat::RgbX => (ImageColorType::Rgb, 3usize),
658 PaletteFormat::Bgr | PaletteFormat::BgrX => (ImageColorType::Bgr, 3usize),
659 PaletteFormat::RgbA => (ImageColorType::Rgba, 4usize),
660 PaletteFormat::BgrA => (ImageColorType::Bgra, 4usize),
661 };
662
663 let palette_table_len = palette_color_count
664 .checked_mul(output_channels)
665 .ok_or_else(|| anyhow::anyhow!("Palette size overflow"))?;
666 let mut palette_table = Vec::with_capacity(palette_table_len);
667 for idx in 0..palette_color_count {
668 let base = idx * palette_entry_size;
669 match palette_format {
670 PaletteFormat::Rgb => {
671 palette_table.extend_from_slice(&palettes[base..base + 3]);
672 }
673 PaletteFormat::Bgr => {
674 palette_table.extend_from_slice(&palettes[base..base + 3]);
675 }
676 PaletteFormat::RgbX => {
677 palette_table.extend_from_slice(&palettes[base..base + 3]);
678 }
679 PaletteFormat::BgrX => {
680 palette_table.extend_from_slice(&palettes[base..base + 3]);
681 }
682 PaletteFormat::RgbA => {
683 palette_table.extend_from_slice(&palettes[base..base + 4]);
684 }
685 PaletteFormat::BgrA => {
686 palette_table.extend_from_slice(&palettes[base..base + 4]);
687 }
688 }
689 }
690
691 let total_bits_required = pixel_count
692 .checked_mul(pixel_size)
693 .ok_or_else(|| anyhow::anyhow!("Pixel count overflow for pixel_size {}", pixel_size))?;
694 if total_bits_required > pixel_data.len() * 8 {
695 return Err(anyhow::anyhow!(
696 "Pixel data too short: need {} bits, have {} bits",
697 total_bits_required,
698 pixel_data.len() * 8
699 ));
700 }
701
702 let output_len = pixel_count
703 .checked_mul(output_channels)
704 .ok_or_else(|| anyhow::anyhow!("Output image size overflow"))?;
705 let mut output = Vec::with_capacity(output_len);
706
707 let stride = output_channels;
708 if pixel_size == 8 {
709 if pixel_data.len() < pixel_count {
710 return Err(anyhow::anyhow!(
711 "Pixel data too short: expected {} bytes, got {}",
712 pixel_count,
713 pixel_data.len()
714 ));
715 }
716 for &index in pixel_data.iter().take(pixel_count) {
717 let idx = index as usize;
718 if idx >= palette_color_count {
719 return Err(anyhow::anyhow!(
720 "Palette index {} exceeds palette size {}",
721 idx,
722 palette_color_count
723 ));
724 }
725 let start = idx * stride;
726 output.extend_from_slice(&palette_table[start..start + stride]);
727 }
728 } else {
729 let mut bit_offset = 0usize;
730 for _ in 0..pixel_count {
731 let idx = read_bits_as_usize(pixel_data, bit_offset, pixel_size)?;
732 bit_offset = bit_offset
733 .checked_add(pixel_size)
734 .ok_or_else(|| anyhow::anyhow!("Bit offset overflow"))?;
735 if idx >= palette_color_count {
736 return Err(anyhow::anyhow!(
737 "Palette index {} exceeds palette size {}",
738 idx,
739 palette_color_count
740 ));
741 }
742 let start = idx * stride;
743 output.extend_from_slice(&palette_table[start..start + stride]);
744 }
745 }
746
747 Ok(ImageData {
748 width: width_u32,
749 height: height_u32,
750 color_type,
751 depth: 8,
752 data: output,
753 })
754}
755
756fn read_bits_as_usize(data: &[u8], bit_offset: usize, bit_len: usize) -> Result<usize> {
757 if bit_len == 0 {
758 return Err(anyhow::anyhow!("bit_len must be greater than zero"));
759 }
760 if bit_len > (std::mem::size_of::<usize>() * 8) {
761 return Err(anyhow::anyhow!("Cannot read {} bits into usize", bit_len));
762 }
763
764 let mut value = 0usize;
765 for bit_idx in 0..bit_len {
766 let absolute_bit = bit_offset + bit_idx;
767 let byte_index = absolute_bit / 8;
768 if byte_index >= data.len() {
769 return Err(anyhow::anyhow!(
770 "Bit offset {} exceeds pixel data",
771 absolute_bit
772 ));
773 }
774 let bit_in_byte = 7 - (absolute_bit % 8);
775 let bit = (data[byte_index] >> bit_in_byte) & 1;
776 value = (value << 1) | bit as usize;
777 }
778 Ok(value)
779}