r/interpreter/graphics/
raster.rs1use crate::interpreter::value::{RError, RErrorKind};
8
9pub fn svg_to_raster(
14 svg_str: &str,
15 width_px: u32,
16 height_px: u32,
17) -> Result<tiny_skia::Pixmap, RError> {
18 let tree = usvg::Tree::from_str(svg_str, &usvg::Options::default()).map_err(|e| {
20 RError::new(
21 RErrorKind::Other,
22 format!("failed to parse SVG for rasterization: {e}"),
23 )
24 })?;
25
26 let mut pixmap = tiny_skia::Pixmap::new(width_px, height_px).ok_or_else(|| {
28 RError::new(
29 RErrorKind::Other,
30 format!("failed to create {width_px}x{height_px} pixel buffer"),
31 )
32 })?;
33
34 pixmap.fill(tiny_skia::Color::WHITE);
36
37 let svg_size = tree.size();
39 let sx = width_px as f32 / svg_size.width();
40 let sy = height_px as f32 / svg_size.height();
41 let transform = tiny_skia::Transform::from_scale(sx, sy);
42
43 resvg::render(&tree, transform, &mut pixmap.as_mut());
45
46 Ok(pixmap)
47}
48
49pub fn pixmap_to_jpeg(pixmap: &tiny_skia::Pixmap, quality: u8) -> Result<Vec<u8>, RError> {
51 let width = pixmap.width();
52 let height = pixmap.height();
53
54 let mut rgb_data = Vec::with_capacity((width * height * 3) as usize);
56 for pixel in pixmap.pixels() {
57 let a = pixel.alpha() as f32 / 255.0;
59 if a > 0.0 {
60 rgb_data.push((pixel.red() as f32 / a).min(255.0) as u8);
61 rgb_data.push((pixel.green() as f32 / a).min(255.0) as u8);
62 rgb_data.push((pixel.blue() as f32 / a).min(255.0) as u8);
63 } else {
64 rgb_data.push(255);
66 rgb_data.push(255);
67 rgb_data.push(255);
68 }
69 }
70
71 let img: image::ImageBuffer<image::Rgb<u8>, _> =
72 image::ImageBuffer::from_raw(width, height, rgb_data).ok_or_else(|| {
73 RError::new(
74 RErrorKind::Other,
75 "failed to create image buffer".to_string(),
76 )
77 })?;
78
79 let mut buf = Vec::new();
80 let mut cursor = std::io::Cursor::new(&mut buf);
81 let encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut cursor, quality);
82 image::ImageEncoder::write_image(
83 encoder,
84 img.as_raw(),
85 width,
86 height,
87 image::ExtendedColorType::Rgb8,
88 )
89 .map_err(|e| RError::new(RErrorKind::Other, format!("JPEG encoding failed: {e}")))?;
90
91 Ok(buf)
92}
93
94pub fn pixmap_to_bmp(pixmap: &tiny_skia::Pixmap) -> Result<Vec<u8>, RError> {
96 let width = pixmap.width();
97 let height = pixmap.height();
98
99 let mut rgb_data = Vec::with_capacity((width * height * 3) as usize);
101 for pixel in pixmap.pixels() {
102 let a = pixel.alpha() as f32 / 255.0;
103 if a > 0.0 {
104 rgb_data.push((pixel.red() as f32 / a).min(255.0) as u8);
105 rgb_data.push((pixel.green() as f32 / a).min(255.0) as u8);
106 rgb_data.push((pixel.blue() as f32 / a).min(255.0) as u8);
107 } else {
108 rgb_data.push(255);
109 rgb_data.push(255);
110 rgb_data.push(255);
111 }
112 }
113
114 let img: image::ImageBuffer<image::Rgb<u8>, _> =
115 image::ImageBuffer::from_raw(width, height, rgb_data).ok_or_else(|| {
116 RError::new(
117 RErrorKind::Other,
118 "failed to create image buffer".to_string(),
119 )
120 })?;
121
122 let mut buf = Vec::new();
123 let mut cursor = std::io::Cursor::new(&mut buf);
124 let encoder = image::codecs::bmp::BmpEncoder::new(&mut cursor);
125 image::ImageEncoder::write_image(
126 encoder,
127 img.as_raw(),
128 width,
129 height,
130 image::ExtendedColorType::Rgb8,
131 )
132 .map_err(|e| RError::new(RErrorKind::Other, format!("BMP encoding failed: {e}")))?;
133
134 Ok(buf)
135}