1#[cfg(feature = "image-jxl")]
3use super::jxl::*;
4use crate::ext::io::*;
5use crate::types::*;
6use anyhow::Result;
7use std::convert::TryFrom;
8use std::io::Write;
9
10pub fn reverse_alpha_values(data: &mut ImageData) -> Result<()> {
14 if data.color_type != ImageColorType::Rgba && data.color_type != ImageColorType::Bgra {
15 return Err(anyhow::anyhow!("Image is not RGBA or BGRA"));
16 }
17 if data.depth != 8 {
18 return Err(anyhow::anyhow!(
19 "Alpha value reversal only supports 8-bit depth"
20 ));
21 }
22 for i in (0..data.data.len()).step_by(4) {
23 data.data[i + 3] = 255 - data.data[i + 3];
24 }
25 Ok(())
26}
27
28pub fn convert_bgr_to_bgra(data: &mut ImageData) -> Result<()> {
32 if data.color_type != ImageColorType::Bgr {
33 return Err(anyhow::anyhow!("Image is not BGR"));
34 }
35 if data.depth != 8 {
36 return Err(anyhow::anyhow!(
37 "BGR to BGRA conversion only supports 8-bit depth"
38 ));
39 }
40 let mut new_data = Vec::with_capacity(data.data.len() / 3 * 4);
41 for chunk in data.data.chunks_exact(3) {
42 new_data.push(chunk[0]); new_data.push(chunk[1]); new_data.push(chunk[2]); new_data.push(255); }
47 data.data = new_data;
48 data.color_type = ImageColorType::Bgra;
49 Ok(())
50}
51
52pub fn convert_bgr_to_rgb(data: &mut ImageData) -> Result<()> {
56 if data.color_type != ImageColorType::Bgr {
57 return Err(anyhow::anyhow!("Image is not BGR"));
58 }
59 if data.depth != 8 {
60 return Err(anyhow::anyhow!(
61 "BGR to RGB conversion only supports 8-bit depth"
62 ));
63 }
64 for i in (0..data.data.len()).step_by(3) {
65 let b = data.data[i];
66 data.data[i] = data.data[i + 2];
67 data.data[i + 2] = b;
68 }
69 data.color_type = ImageColorType::Rgb;
70 Ok(())
71}
72
73pub fn convert_bgra_to_bgr(data: &mut ImageData) -> Result<()> {
77 if data.color_type != ImageColorType::Bgra {
78 return Err(anyhow::anyhow!("Image is not BGRA"));
79 }
80 if data.depth != 8 {
81 return Err(anyhow::anyhow!(
82 "BGRA to BGR conversion only supports 8-bit depth"
83 ));
84 }
85 let mut new_data = Vec::with_capacity(data.data.len() / 4 * 3);
86 for chunk in data.data.chunks_exact(4) {
87 new_data.push(chunk[0]); new_data.push(chunk[1]); new_data.push(chunk[2]); }
91 data.data = new_data;
92 data.color_type = ImageColorType::Bgr;
93 Ok(())
94}
95
96pub fn convert_bgra_to_rgba(data: &mut ImageData) -> Result<()> {
100 if data.color_type != ImageColorType::Bgra {
101 return Err(anyhow::anyhow!("Image is not BGRA"));
102 }
103 if data.depth != 8 {
104 return Err(anyhow::anyhow!(
105 "BGRA to RGBA conversion only supports 8-bit depth"
106 ));
107 }
108 for i in (0..data.data.len()).step_by(4) {
109 let b = data.data[i];
110 data.data[i] = data.data[i + 2];
111 data.data[i + 2] = b;
112 }
113 data.color_type = ImageColorType::Rgba;
114 Ok(())
115}
116
117pub fn convert_rgb_to_rgba(data: &mut ImageData) -> Result<()> {
121 if data.color_type != ImageColorType::Rgb {
122 return Err(anyhow::anyhow!("Image is not RGB"));
123 }
124 if data.depth != 8 {
125 return Err(anyhow::anyhow!(
126 "RGB to RGBA conversion only supports 8-bit depth"
127 ));
128 }
129 let mut new_data = Vec::with_capacity(data.data.len() / 3 * 4);
130 for chunk in data.data.chunks_exact(3) {
131 new_data.push(chunk[0]); new_data.push(chunk[1]); new_data.push(chunk[2]); new_data.push(255); }
136 data.data = new_data;
137 data.color_type = ImageColorType::Rgba;
138 Ok(())
139}
140
141pub fn convert_rgb_to_bgr(data: &mut ImageData) -> Result<()> {
145 if data.color_type != ImageColorType::Rgb {
146 return Err(anyhow::anyhow!("Image is not RGB"));
147 }
148 if data.depth != 8 {
149 return Err(anyhow::anyhow!(
150 "RGB to BGR conversion only supports 8-bit depth"
151 ));
152 }
153 for i in (0..data.data.len()).step_by(3) {
154 let r = data.data[i];
155 data.data[i] = data.data[i + 2];
156 data.data[i + 2] = r;
157 }
158 data.color_type = ImageColorType::Bgr;
159 Ok(())
160}
161
162pub fn convert_rgba_to_bgra(data: &mut ImageData) -> Result<()> {
166 if data.color_type != ImageColorType::Rgba {
167 return Err(anyhow::anyhow!("Image is not RGBA"));
168 }
169 if data.depth != 8 {
170 return Err(anyhow::anyhow!(
171 "RGBA to BGRA conversion only supports 8-bit depth"
172 ));
173 }
174 for i in (0..data.data.len()).step_by(4) {
175 let r = data.data[i];
176 data.data[i] = data.data[i + 2];
177 data.data[i + 2] = r;
178 }
179 data.color_type = ImageColorType::Bgra;
180 Ok(())
181}
182
183pub fn convert_grayscale_to_rgb(data: &mut ImageData) -> Result<()> {
185 if data.color_type != ImageColorType::Grayscale {
186 return Err(anyhow::anyhow!("Image is not Grayscale"));
187 }
188 if data.depth != 8 {
189 return Err(anyhow::anyhow!(
190 "Grayscale to RGB conversion only supports 8-bit depth"
191 ));
192 }
193 let mut new_data = Vec::with_capacity(data.data.len() * 3);
194 for &gray in &data.data {
195 new_data.push(gray); new_data.push(gray); new_data.push(gray); }
199 data.data = new_data;
200 data.color_type = ImageColorType::Rgb;
201 Ok(())
202}
203
204pub fn convert_grayscale_to_rgba(data: &mut ImageData) -> Result<()> {
206 if data.color_type != ImageColorType::Grayscale {
207 return Err(anyhow::anyhow!("Image is not Grayscale"));
208 }
209 if data.depth != 8 {
210 return Err(anyhow::anyhow!(
211 "Grayscale to RGBA conversion only supports 8-bit depth"
212 ));
213 }
214 let mut new_data = Vec::with_capacity(data.data.len() * 4);
215 for &gray in &data.data {
216 new_data.push(gray); new_data.push(gray); new_data.push(gray); new_data.push(255); }
221 data.data = new_data;
222 data.color_type = ImageColorType::Rgba;
223 Ok(())
224}
225
226pub fn convert_to_rgba(data: &mut ImageData) -> Result<()> {
228 match data.color_type {
229 ImageColorType::Rgb => convert_rgb_to_rgba(data),
230 ImageColorType::Bgr => convert_bgr_to_bgra(data),
231 ImageColorType::Rgba => Ok(()),
232 ImageColorType::Bgra => convert_bgra_to_rgba(data),
233 ImageColorType::Grayscale => convert_grayscale_to_rgba(data),
234 }
235}
236
237pub fn encode_img(
244 data: ImageData,
245 typ: ImageOutputType,
246 filename: &str,
247 config: &ExtraConfig,
248) -> Result<()> {
249 let mut file = crate::utils::files::write_file(filename)?;
250 encode_img_writer(data, typ, &mut file, config)
251}
252
253pub fn encode_img_writer<T: Write>(
260 mut data: ImageData,
261 typ: ImageOutputType,
262 mut file: &mut T,
263 config: &ExtraConfig,
264) -> Result<()> {
265 match typ {
266 ImageOutputType::Png => {
267 let color_type = match data.color_type {
268 ImageColorType::Grayscale => png::ColorType::Grayscale,
269 ImageColorType::Rgb => png::ColorType::Rgb,
270 ImageColorType::Rgba => png::ColorType::Rgba,
271 ImageColorType::Bgr => {
272 convert_bgr_to_rgb(&mut data)?;
273 png::ColorType::Rgb
274 }
275 ImageColorType::Bgra => {
276 convert_bgra_to_rgba(&mut data)?;
277 png::ColorType::Rgba
278 }
279 };
280 let bit_depth = match &data.depth {
281 1 => png::BitDepth::One,
282 2 => png::BitDepth::Two,
283 4 => png::BitDepth::Four,
284 8 => png::BitDepth::Eight,
285 16 => png::BitDepth::Sixteen,
286 _ => return Err(anyhow::anyhow!("Unsupported bit depth: {}", data.depth)),
287 };
288 let mut encoder = png::Encoder::new(&mut file, data.width, data.height);
289 encoder.set_color(color_type);
290 encoder.set_depth(bit_depth);
291 encoder.set_compression(config.png_compression_level.to_compression());
292 let mut writer = encoder.write_header()?;
293 writer.write_image_data(&data.data)?;
294 writer.finish()?;
295 Ok(())
296 }
297 #[cfg(feature = "image-jpg")]
298 ImageOutputType::Jpg => {
299 let color_type = match data.color_type {
300 ImageColorType::Grayscale => mozjpeg::ColorSpace::JCS_GRAYSCALE,
301 ImageColorType::Rgb => mozjpeg::ColorSpace::JCS_RGB,
302 ImageColorType::Rgba => mozjpeg::ColorSpace::JCS_EXT_RGBA,
303 ImageColorType::Bgr => {
304 convert_bgr_to_rgb(&mut data)?;
305 mozjpeg::ColorSpace::JCS_RGB
306 }
307 ImageColorType::Bgra => {
308 convert_bgra_to_rgba(&mut data)?;
309 mozjpeg::ColorSpace::JCS_EXT_RGBA
310 }
311 };
312 if data.depth != 8 {
313 return Err(anyhow::anyhow!(
314 "JPEG encoding only supports 8-bit depth, found: {}",
315 data.depth
316 ));
317 }
318 let mut encoder = mozjpeg::compress::Compress::new(color_type);
319 encoder.set_size(data.width as usize, data.height as usize);
320 encoder.set_quality(config.jpeg_quality as f32);
321 let mut start = encoder.start_compress(file)?;
322 start.write_scanlines(&data.data)?;
323 start.finish()?;
324 Ok(())
325 }
326 #[cfg(feature = "image-webp")]
327 ImageOutputType::Webp => {
328 let color_type = match data.color_type {
329 ImageColorType::Rgb => webp::PixelLayout::Rgb,
330 ImageColorType::Rgba => webp::PixelLayout::Rgba,
331 ImageColorType::Bgr => {
332 convert_bgr_to_rgb(&mut data)?;
333 webp::PixelLayout::Rgb
334 }
335 ImageColorType::Bgra => {
336 convert_bgra_to_rgba(&mut data)?;
337 webp::PixelLayout::Rgba
338 }
339 _ => {
340 return Err(anyhow::anyhow!(
341 "Unsupported color type for WebP: {:?}",
342 data.color_type
343 ));
344 }
345 };
346 if data.depth != 8 {
347 return Err(anyhow::anyhow!(
348 "WebP encoding only supports 8-bit depth, found: {}",
349 data.depth
350 ));
351 }
352 let encoder = webp::Encoder::new(&data.data, color_type, data.width, data.height);
353 let re = encoder
354 .encode_simple(config.webp_lossless, config.webp_quality as f32)
355 .map_err(|e| anyhow::anyhow!("Failed to encode WebP image: {:?}", e))?;
356 file.write_all(&re)?;
357 Ok(())
358 }
359 #[cfg(feature = "image-jxl")]
360 ImageOutputType::Jxl => {
361 let data = encode_jxl(data, config)?;
362 file.write_all(&data)?;
363 Ok(())
364 }
365 }
366}
367
368pub fn convert_grayscale_alpha_to_rgba(
369 raw_data: &[u8],
370 width: usize,
371 height: usize,
372 bit_depth: u8,
373) -> Result<ImageData> {
374 if bit_depth != 8 && bit_depth != 16 {
375 return Err(anyhow::anyhow!(
376 "Unsupported bit depth for GrayscaleAlpha to RGBA conversion: {}",
377 bit_depth
378 ));
379 }
380 let bytes_per_channel = (bit_depth / 8) as usize;
381 let stride = width * 2 * bytes_per_channel;
382 if raw_data.len() != stride * height {
383 return Err(anyhow::anyhow!(
384 "Input data size does not match expected size for GrayscaleAlpha image"
385 ));
386 }
387 let mut data = Vec::with_capacity(width * height * 4 * bytes_per_channel);
388 for y in 0..height {
389 for x in 0..width {
390 let base = y * stride + x * 2 * bytes_per_channel;
391 data.extend_from_slice(&raw_data[base..base + bytes_per_channel]);
393 data.extend_from_slice(&raw_data[base..base + bytes_per_channel]);
394 data.extend_from_slice(&raw_data[base..base + bytes_per_channel]);
395 data.extend_from_slice(
397 &raw_data[base + bytes_per_channel..base + 2 * bytes_per_channel],
398 );
399 }
400 }
401 Ok(ImageData {
402 width: width as u32,
403 height: height as u32,
404 depth: bit_depth,
405 color_type: ImageColorType::Rgba,
406 data,
407 })
408}
409
410pub fn load_png<R: std::io::Read + std::io::Seek>(data: R) -> Result<ImageData> {
412 let decoder = png::Decoder::new(std::io::BufReader::new(data));
413 let mut reader = decoder.read_info()?;
414 let bit_depth = match reader.info().bit_depth {
415 png::BitDepth::One => 1,
416 png::BitDepth::Two => 2,
417 png::BitDepth::Four => 4,
418 png::BitDepth::Eight => 8,
419 png::BitDepth::Sixteen => 16,
420 };
421 let color_type = match reader.info().color_type {
422 png::ColorType::Grayscale => ImageColorType::Grayscale,
423 png::ColorType::Rgb => ImageColorType::Rgb,
424 png::ColorType::Rgba => ImageColorType::Rgba,
425 png::ColorType::GrayscaleAlpha => ImageColorType::Rgba,
426 png::ColorType::Indexed => {
427 if let Some(palette) = &reader.info().palette {
428 if palette.len() % 3 != 0 {
429 return Err(anyhow::anyhow!(
430 "Invalid PNG palette length: {}",
431 palette.len()
432 ));
433 }
434 ImageColorType::Rgb
435 } else {
436 return Err(anyhow::anyhow!(
437 "PNG image has indexed color type but no palette"
438 ));
439 }
440 }
441 };
442 if reader.info().color_type == png::ColorType::GrayscaleAlpha {
443 let height = reader.info().height as usize;
444 let width = reader.info().width as usize;
445 let raw_stride = width * 2 * bit_depth as usize / 8;
446 let mut raw_data = vec![0; raw_stride * height];
447 reader.next_frame(&mut raw_data)?;
448 return convert_grayscale_alpha_to_rgba(&raw_data, width, height, bit_depth);
449 }
450 if reader.info().color_type == png::ColorType::Indexed {
451 let mut palette = reader
452 .info()
453 .palette
454 .as_ref()
455 .ok_or_else(|| anyhow::anyhow!("PNG image has indexed color type but no palette"))?
456 .to_vec();
457 let mut palette_format = PaletteFormat::Rgb;
458 if let Some(trns) = &reader.info().trns {
459 let mut new_palette = Vec::with_capacity(palette.len() / 3 * 4);
460 let trns_len = trns.len();
461 for i in 0..(palette.len() / 3) {
462 new_palette.push(palette[i * 3]);
463 new_palette.push(palette[i * 3 + 1]);
464 new_palette.push(palette[i * 3 + 2]);
465 let alpha = if i < trns_len { trns[i] } else { 255 };
466 new_palette.push(alpha);
467 }
468 palette = new_palette;
469 palette_format = PaletteFormat::RgbA;
470 }
471 let width = reader.info().width as usize;
472 let height = reader.info().height as usize;
473 let raw_stride = width * bit_depth as usize / 8;
474 let mut raw_data = vec![0; raw_stride * height];
475 reader.next_frame(&mut raw_data)?;
476 return convert_index_palette_to_normal_bitmap(
477 &raw_data,
478 bit_depth as usize,
479 &palette,
480 palette_format,
481 width,
482 height,
483 );
484 }
485 let stride = reader.info().width as usize * color_type.bpp(bit_depth) as usize / 8;
486 let mut data = vec![0; stride * reader.info().height as usize];
487 reader.next_frame(&mut data)?;
488 Ok(ImageData {
489 width: reader.info().width,
490 height: reader.info().height,
491 depth: bit_depth,
492 color_type,
493 data,
494 })
495}
496
497#[cfg(feature = "mozjpeg")]
498pub fn load_jpg<R: std::io::Read>(data: R) -> Result<ImageData> {
499 let decoder = mozjpeg::decompress::Decompress::new_reader(std::io::BufReader::new(data))?;
500 let color_type = match decoder.color_space() {
501 mozjpeg::ColorSpace::JCS_GRAYSCALE => ImageColorType::Grayscale,
502 mozjpeg::ColorSpace::JCS_RGB => ImageColorType::Rgb,
503 mozjpeg::ColorSpace::JCS_EXT_RGBA => ImageColorType::Rgba,
504 _ => ImageColorType::Rgb, };
506 let width = decoder.width() as u32;
507 let height = decoder.height() as u32;
508 let stride = width as usize * color_type.bpp(8) as usize / 8;
509 let mut data = vec![0; stride * height as usize];
510 let mut re = match color_type {
511 ImageColorType::Grayscale => decoder.grayscale()?,
512 ImageColorType::Rgb => decoder.rgb()?,
513 ImageColorType::Rgba => decoder.rgba()?,
514 _ => {
515 unreachable!(); }
517 };
518 re.read_scanlines_into(&mut data)?;
519 Ok(ImageData {
520 width,
521 height,
522 depth: 8,
523 color_type,
524 data,
525 })
526}
527
528pub fn decode_img(typ: ImageOutputType, filename: &str) -> Result<ImageData> {
533 match typ {
534 ImageOutputType::Png => {
535 let file = crate::utils::files::read_file(filename)?;
536 let reader = MemReader::new(file);
537 load_png(reader)
538 }
539 #[cfg(feature = "image-jpg")]
540 ImageOutputType::Jpg => {
541 let file = crate::utils::files::read_file(filename)?;
542 load_jpg(&file[..])
543 }
544 #[cfg(feature = "image-webp")]
545 ImageOutputType::Webp => {
546 let file = crate::utils::files::read_file(filename)?;
547 let decoder = webp::Decoder::new(&file);
548 let image = decoder
549 .decode()
550 .ok_or(anyhow::anyhow!("Failed to decode WebP image"))?;
551 let color_type = if image.is_alpha() {
552 ImageColorType::Rgba
553 } else {
554 ImageColorType::Rgb
555 };
556 let width = image.width();
557 let height = image.height();
558 let stride = width as usize * color_type.bpp(8) as usize / 8;
559 let mut data = vec![0; stride * height as usize];
560 if image.len() != data.len() {
561 return Err(anyhow::anyhow!(
562 "WebP image data size mismatch: expected {}, got {}",
563 data.len(),
564 image.len()
565 ));
566 }
567 data.copy_from_slice(&image);
568 Ok(ImageData {
569 width,
570 height,
571 depth: 8,
572 color_type,
573 data,
574 })
575 }
576 #[cfg(feature = "image-jxl")]
577 ImageOutputType::Jxl => {
578 let file = crate::utils::files::read_file(filename)?;
579 decode_jxl(&file[..])
580 }
581 }
582}
583
584pub fn draw_on_canvas(
594 img: ImageData,
595 canvas_width: u32,
596 canvas_height: u32,
597 offset_x: u32,
598 offset_y: u32,
599) -> Result<ImageData> {
600 let bytes_per_pixel = img.color_type.bpp(img.depth) as u32 / 8;
601 let mut canvas_data = vec![0u8; (canvas_width * canvas_height * bytes_per_pixel) as usize];
602 let canvas_stride = canvas_width * bytes_per_pixel;
603 let img_stride = img.width * bytes_per_pixel;
604
605 for y in 0..img.height {
606 let canvas_y = y + offset_y;
607 if canvas_y >= canvas_height {
608 continue;
609 }
610 let canvas_start = (canvas_y * canvas_stride + offset_x * bytes_per_pixel) as usize;
611 let img_start = (y * img_stride) as usize;
612 let copy_len = img_stride as usize;
613 if canvas_start + copy_len > canvas_data.len() {
614 continue;
615 }
616 canvas_data[canvas_start..canvas_start + copy_len]
617 .copy_from_slice(&img.data[img_start..img_start + copy_len]);
618 }
619
620 Ok(ImageData {
621 width: canvas_width,
622 height: canvas_height,
623 color_type: img.color_type,
624 depth: img.depth,
625 data: canvas_data,
626 })
627}
628
629pub fn flip_image(data: &mut ImageData) -> Result<()> {
631 if data.height <= 1 {
632 return Ok(());
633 }
634 let row_size = data.color_type.bpp(data.depth) as usize * data.width as usize / 8;
635 if row_size == 0 {
636 return Ok(());
637 }
638
639 let mut i = 0;
640 let mut j = data.height as usize - 1;
641 while i < j {
642 let (top, bottom) = data.data.split_at_mut(j * row_size);
643 let top_row = &mut top[i * row_size..i * row_size + row_size];
644 let bottom_row = &mut bottom[0..row_size];
645 top_row.swap_with_slice(bottom_row);
646 i += 1;
647 j -= 1;
648 }
649
650 Ok(())
651}
652
653pub fn apply_opacity(img: &mut ImageData, opacity: u8) -> Result<()> {
657 if img.color_type != ImageColorType::Rgba && img.color_type != ImageColorType::Bgra {
658 return Err(anyhow::anyhow!("Image is not RGBA or BGRA"));
659 }
660 if img.depth != 8 {
661 return Err(anyhow::anyhow!(
662 "Opacity application only supports 8-bit depth"
663 ));
664 }
665 for i in (0..img.data.len()).step_by(4) {
666 img.data[i + 3] = (img.data[i + 3] as u16 * opacity as u16 / 255) as u8;
667 }
668 Ok(())
669}
670
671pub fn draw_on_image(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
678 if base.color_type != diff.color_type {
679 return Err(anyhow::anyhow!("Image color types do not match"));
680 }
681 if base.depth != diff.depth {
682 return Err(anyhow::anyhow!("Image depths do not match"));
683 }
684
685 let bits_per_pixel = base.color_type.bpp(base.depth) as usize;
686 if bits_per_pixel == 0 || bits_per_pixel % 8 != 0 {
687 return Err(anyhow::anyhow!(
688 "Unsupported pixel bit layout: {} bits",
689 bits_per_pixel
690 ));
691 }
692 let bpp = bits_per_pixel / 8;
693
694 let base_stride = base.width as usize * bpp;
695 let diff_stride = diff.width as usize * bpp;
696
697 for y in 0..diff.height {
698 let base_y = top + y;
699 if base_y >= base.height {
700 continue;
701 }
702
703 for x in 0..diff.width {
704 let base_x = left + x;
705 if base_x >= base.width {
706 continue;
707 }
708
709 let diff_idx = (y as usize * diff_stride) + (x as usize * bpp);
710 let base_idx = (base_y as usize * base_stride) + (base_x as usize * bpp);
711
712 if diff_idx + bpp > diff.data.len() || base_idx + bpp > base.data.len() {
714 continue;
715 }
716
717 base.data[base_idx..base_idx + bpp]
718 .copy_from_slice(&diff.data[diff_idx..diff_idx + bpp]);
719 }
720 }
721
722 Ok(())
723}
724
725pub fn draw_on_img_with_opacity(
733 base: &mut ImageData,
734 diff: &ImageData,
735 left: u32,
736 top: u32,
737 opacity: u8,
738) -> Result<()> {
739 if base.color_type != diff.color_type {
740 return Err(anyhow::anyhow!("Image color types do not match"));
741 }
742 if base.color_type != ImageColorType::Rgba && base.color_type != ImageColorType::Bgra {
743 return Err(anyhow::anyhow!("Images are not RGBA or BGRA"));
744 }
745 if base.depth != 8 || diff.depth != 8 {
746 return Err(anyhow::anyhow!(
747 "Image drawing with opacity only supports 8-bit depth"
748 ));
749 }
750
751 let bpp = 4;
752 let base_stride = base.width as usize * bpp;
753 let diff_stride = diff.width as usize * bpp;
754
755 for y in 0..diff.height {
756 let base_y = top + y;
757 if base_y >= base.height {
758 continue;
759 }
760
761 for x in 0..diff.width {
762 let base_x = left + x;
763 if base_x >= base.width {
764 continue;
765 }
766
767 let diff_idx = (y as usize * diff_stride) + (x as usize * bpp);
768 let base_idx = (base_y as usize * base_stride) + (base_x as usize * bpp);
769
770 let diff_pixel = &diff.data[diff_idx..diff_idx + bpp];
771 let base_pixel_orig = base.data[base_idx..base_idx + bpp].to_vec();
772
773 let src_alpha_u16 = (diff_pixel[3] as u16 * opacity as u16) / 255;
774
775 if src_alpha_u16 == 0 {
776 continue;
777 }
778
779 let dst_alpha_u16 = base_pixel_orig[3] as u16;
780
781 let out_alpha_u16 = src_alpha_u16 + (dst_alpha_u16 * (255 - src_alpha_u16)) / 255;
783
784 if out_alpha_u16 == 0 {
785 for i in 0..4 {
786 base.data[base_idx + i] = 0;
787 }
788 continue;
789 }
790
791 for i in 0..3 {
793 let src_comp = diff_pixel[i] as u16;
794 let dst_comp = base_pixel_orig[i] as u16;
795
796 let numerator = src_comp as u32 * src_alpha_u16 as u32
797 + (dst_comp as u32 * dst_alpha_u16 as u32 * (255 - src_alpha_u16) as u32) / 255;
798 base.data[base_idx + i] = (numerator / out_alpha_u16 as u32) as u8;
799 }
800 base.data[base_idx + 3] = out_alpha_u16 as u8;
801 }
802 }
803
804 Ok(())
805}
806
807#[derive(Debug, Clone, Copy, PartialEq, Eq)]
808pub enum PaletteFormat {
809 Rgb,
811 Bgr,
813 RgbX,
815 BgrX,
817 RgbA,
819 BgrA,
821}
822
823pub fn convert_index_palette_to_normal_bitmap(
827 pixel_data: &[u8],
828 pixel_size: usize,
829 palettes: &[u8],
830 palette_format: PaletteFormat,
831 width: usize,
832 height: usize,
833) -> Result<ImageData> {
834 if width == 0 || height == 0 {
835 return Err(anyhow::anyhow!("Image dimensions must be non-zero"));
836 }
837 if pixel_size == 0 {
838 return Err(anyhow::anyhow!("pixel_size must be greater than zero"));
839 }
840
841 let width_u32 =
842 u32::try_from(width).map_err(|_| anyhow::anyhow!("width exceeds u32::MAX: {}", width))?;
843 let height_u32 = u32::try_from(height)
844 .map_err(|_| anyhow::anyhow!("height exceeds u32::MAX: {}", height))?;
845
846 let pixel_count = width
847 .checked_mul(height)
848 .ok_or_else(|| anyhow::anyhow!("Image dimensions overflow: {}x{}", width, height))?;
849
850 let palette_entry_size = match palette_format {
851 PaletteFormat::Rgb | PaletteFormat::Bgr => 3usize,
852 PaletteFormat::RgbX | PaletteFormat::BgrX | PaletteFormat::RgbA | PaletteFormat::BgrA => {
853 4usize
854 }
855 };
856
857 if palettes.len() < palette_entry_size {
858 return Err(anyhow::anyhow!("Palette data is too small"));
859 }
860 if palettes.len() % palette_entry_size != 0 {
861 return Err(anyhow::anyhow!(
862 "Palette length {} is not a multiple of {}",
863 palettes.len(),
864 palette_entry_size
865 ));
866 }
867 let palette_color_count = palettes.len() / palette_entry_size;
868 if palette_color_count == 0 {
869 return Err(anyhow::anyhow!("Palette does not contain any colors"));
870 }
871
872 let (color_type, output_channels) = match palette_format {
873 PaletteFormat::Rgb | PaletteFormat::RgbX => (ImageColorType::Rgb, 3usize),
874 PaletteFormat::Bgr | PaletteFormat::BgrX => (ImageColorType::Bgr, 3usize),
875 PaletteFormat::RgbA => (ImageColorType::Rgba, 4usize),
876 PaletteFormat::BgrA => (ImageColorType::Bgra, 4usize),
877 };
878
879 let palette_table_len = palette_color_count
880 .checked_mul(output_channels)
881 .ok_or_else(|| anyhow::anyhow!("Palette size overflow"))?;
882 let mut palette_table = Vec::with_capacity(palette_table_len);
883 for idx in 0..palette_color_count {
884 let base = idx * palette_entry_size;
885 match palette_format {
886 PaletteFormat::Rgb => {
887 palette_table.extend_from_slice(&palettes[base..base + 3]);
888 }
889 PaletteFormat::Bgr => {
890 palette_table.extend_from_slice(&palettes[base..base + 3]);
891 }
892 PaletteFormat::RgbX => {
893 palette_table.extend_from_slice(&palettes[base..base + 3]);
894 }
895 PaletteFormat::BgrX => {
896 palette_table.extend_from_slice(&palettes[base..base + 3]);
897 }
898 PaletteFormat::RgbA => {
899 palette_table.extend_from_slice(&palettes[base..base + 4]);
900 }
901 PaletteFormat::BgrA => {
902 palette_table.extend_from_slice(&palettes[base..base + 4]);
903 }
904 }
905 }
906
907 let total_bits_required = pixel_count
908 .checked_mul(pixel_size)
909 .ok_or_else(|| anyhow::anyhow!("Pixel count overflow for pixel_size {}", pixel_size))?;
910 if total_bits_required > pixel_data.len() * 8 {
911 return Err(anyhow::anyhow!(
912 "Pixel data too short: need {} bits, have {} bits",
913 total_bits_required,
914 pixel_data.len() * 8
915 ));
916 }
917
918 let output_len = pixel_count
919 .checked_mul(output_channels)
920 .ok_or_else(|| anyhow::anyhow!("Output image size overflow"))?;
921 let mut output = Vec::with_capacity(output_len);
922
923 let stride = output_channels;
924 if pixel_size == 8 {
925 if pixel_data.len() < pixel_count {
926 return Err(anyhow::anyhow!(
927 "Pixel data too short: expected {} bytes, got {}",
928 pixel_count,
929 pixel_data.len()
930 ));
931 }
932 for &index in pixel_data.iter().take(pixel_count) {
933 let idx = index as usize;
934 if idx >= palette_color_count {
935 return Err(anyhow::anyhow!(
936 "Palette index {} exceeds palette size {}",
937 idx,
938 palette_color_count
939 ));
940 }
941 let start = idx * stride;
942 output.extend_from_slice(&palette_table[start..start + stride]);
943 }
944 } else {
945 let mut bit_offset = 0usize;
946 for _ in 0..pixel_count {
947 let idx = read_bits_as_usize(pixel_data, bit_offset, pixel_size)?;
948 bit_offset = bit_offset
949 .checked_add(pixel_size)
950 .ok_or_else(|| anyhow::anyhow!("Bit offset overflow"))?;
951 if idx >= palette_color_count {
952 return Err(anyhow::anyhow!(
953 "Palette index {} exceeds palette size {}",
954 idx,
955 palette_color_count
956 ));
957 }
958 let start = idx * stride;
959 output.extend_from_slice(&palette_table[start..start + stride]);
960 }
961 }
962
963 Ok(ImageData {
964 width: width_u32,
965 height: height_u32,
966 color_type,
967 depth: 8,
968 data: output,
969 })
970}
971
972fn read_bits_as_usize(data: &[u8], bit_offset: usize, bit_len: usize) -> Result<usize> {
973 if bit_len == 0 {
974 return Err(anyhow::anyhow!("bit_len must be greater than zero"));
975 }
976 if bit_len > (std::mem::size_of::<usize>() * 8) {
977 return Err(anyhow::anyhow!("Cannot read {} bits into usize", bit_len));
978 }
979
980 let mut value = 0usize;
981 for bit_idx in 0..bit_len {
982 let absolute_bit = bit_offset + bit_idx;
983 let byte_index = absolute_bit / 8;
984 if byte_index >= data.len() {
985 return Err(anyhow::anyhow!(
986 "Bit offset {} exceeds pixel data",
987 absolute_bit
988 ));
989 }
990 let bit_in_byte = 7 - (absolute_bit % 8);
991 let bit = (data[byte_index] >> bit_in_byte) & 1;
992 value = (value << 1) | bit as usize;
993 }
994 Ok(value)
995}