Skip to content

Commit 2343764

Browse files
committed
feat: consume frame after added, for reducing memory usage
1 parent b2b3c2d commit 2343764

File tree

2 files changed

+97
-86
lines changed

2 files changed

+97
-86
lines changed

examples/animation.rs

+32-25
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,48 @@ use webp::AnimEncoder;
22
use webp::AnimFrame;
33
use webp::WebPConfig;
44
fn main() {
5-
let width = 32u32;
6-
let height = 32u32;
7-
fn dumy_image(width: u32, height: u32, color: [u8; 4]) -> Vec<u8> {
5+
let width = 512u32;
6+
let height = 512u32;
7+
8+
fn dumy_image(width: u32, height: u32, frame: u32, total_frames: u32) -> Vec<u8> {
89
let mut pixels = Vec::with_capacity(width as usize * height as usize * 4);
9-
for _ in 0..(width * height) {
10-
pixels.push(color[0]); //red
11-
pixels.push(color[1]); //green
12-
pixels.push(color[2]); //blue
13-
pixels.push(color[3]); //alpha
10+
for x in 0..width {
11+
for y in 0..height {
12+
let normalized_frame = frame as f32 / total_frames as f32;
13+
let normalized_x = x as f32 / width as f32;
14+
let normalized_y = y as f32 / height as f32;
15+
16+
let r = ((normalized_frame + normalized_x + normalized_y) % 1.0 * 255.0) as u8;
17+
let g =
18+
((normalized_frame + normalized_x + normalized_y + 0.33) % 1.0 * 255.0) as u8;
19+
let b =
20+
((normalized_frame + normalized_x + normalized_y + 0.67) % 1.0 * 255.0) as u8;
21+
22+
pixels.push(r);
23+
pixels.push(g);
24+
pixels.push(b);
25+
pixels.push(255); // alpha channel, fully opaque
26+
}
1427
}
1528
pixels
1629
}
30+
1731
let mut config = WebPConfig::new().unwrap();
1832
config.lossless = 1;
1933
config.alpha_compression = 0;
20-
config.quality = 75f32;
34+
config.quality = 100f32;
2135
let mut encoder = AnimEncoder::new(width as u32, height as u32, &config);
2236
encoder.set_bgcolor([255, 0, 0, 255]);
23-
encoder.set_loop_count(3);
24-
let mut time_ms = 1000;
37+
encoder.set_loop_count(0);
38+
let mut time_ms = 0;
2539

26-
let v = dumy_image(width, height, [255, 0, 0, 255]);
27-
encoder.add_frame(AnimFrame::from_rgba(&v, width, height, time_ms));
28-
time_ms += 750;
29-
30-
let v = dumy_image(width, height, [0, 255, 0, 255]);
31-
encoder.add_frame(AnimFrame::from_rgba(&v, width, height, time_ms));
32-
time_ms += 500;
33-
34-
let v = dumy_image(width, height, [0, 0, 255, 255]);
35-
encoder.add_frame(AnimFrame::from_rgba(&v, width, height, time_ms));
36-
time_ms += 250;
37-
38-
let v = dumy_image(width, height, [0, 0, 0, 0]);
39-
encoder.add_frame(AnimFrame::from_rgba(&v, width, height, time_ms));
40+
for i in 0..120 {
41+
let image = dumy_image(width, height, i, 120);
42+
encoder
43+
.add_frame(AnimFrame::from_rgba(&image, width, height, time_ms))
44+
.unwrap();
45+
time_ms += 17;
46+
}
4047

4148
let webp = encoder.encode();
4249
let output_path = std::path::Path::new("assets")

src/animation_encoder.rs

+65-61
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,34 @@ impl Into<DynamicImage> for &AnimFrame<'_> {
101101
}
102102
}
103103
pub struct AnimEncoder<'a> {
104-
frames: Vec<AnimFrame<'a>>,
105104
width: u32,
106105
height: u32,
107106
config: &'a WebPConfig,
107+
mux_abi_version: i32,
108108
muxparams: WebPMuxAnimParams,
109+
encoder: *mut WebPAnimEncoder,
109110
}
110111
impl<'a> AnimEncoder<'a> {
111112
pub fn new(width: u32, height: u32, config: &'a WebPConfig) -> Self {
113+
let mut uninit = std::mem::MaybeUninit::<WebPAnimEncoderOptions>::uninit();
114+
115+
let mux_abi_version = WebPGetMuxABIVersion();
116+
let encoder = unsafe {
117+
WebPAnimEncoderOptionsInitInternal(uninit.as_mut_ptr(), mux_abi_version);
118+
WebPAnimEncoderNewInternal(
119+
width as i32,
120+
height as i32,
121+
uninit.as_ptr(),
122+
mux_abi_version,
123+
)
124+
};
125+
112126
Self {
113-
frames: vec![],
114127
width,
115128
height,
116129
config,
130+
encoder,
131+
mux_abi_version,
117132
muxparams: WebPMuxAnimParams {
118133
bgcolor: 0,
119134
loop_count: 0,
@@ -130,14 +145,59 @@ impl<'a> AnimEncoder<'a> {
130145
pub fn set_loop_count(&mut self, loop_count: i32) {
131146
self.muxparams.loop_count = loop_count;
132147
}
133-
pub fn add_frame(&mut self, frame: AnimFrame<'a>) {
134-
self.frames.push(frame);
148+
pub fn add_frame(&mut self, frame: AnimFrame<'_>) -> Result<(), AnimEncodeError> {
149+
unsafe {
150+
let mut pic = crate::new_picture(frame.image, frame.layout, self.width, self.height);
151+
let config = frame.config.unwrap_or(self.config);
152+
let ok = WebPAnimEncoderAdd(
153+
self.encoder,
154+
&mut *pic as *mut _,
155+
frame.timestamp as std::os::raw::c_int,
156+
config,
157+
);
158+
if ok == 0 {
159+
return Err(AnimEncodeError::WebPEncodingError(pic.error_code));
160+
}
161+
}
162+
163+
Ok(())
135164
}
136165
pub fn encode(&self) -> WebPMemory {
137166
self.try_encode().unwrap()
138167
}
139168
pub fn try_encode(&self) -> Result<WebPMemory, AnimEncodeError> {
140-
unsafe { anim_encode(&self) }
169+
let encoder = self.encoder;
170+
unsafe {
171+
WebPAnimEncoderAdd(encoder, std::ptr::null_mut(), 0, std::ptr::null());
172+
173+
let mut webp_data = std::mem::MaybeUninit::<WebPData>::uninit();
174+
let ok = WebPAnimEncoderAssemble(encoder, webp_data.as_mut_ptr());
175+
if ok == 0 {
176+
//ok == false
177+
let cstring = WebPAnimEncoderGetError(encoder);
178+
let cstring = CString::from_raw(cstring as *mut _);
179+
let string = cstring.to_string_lossy().to_string();
180+
return Err(AnimEncodeError::WebPAnimEncoderGetError(string));
181+
}
182+
let mux = WebPMuxCreateInternal(webp_data.as_ptr(), 1, self.mux_abi_version);
183+
let mux_error = WebPMuxSetAnimationParams(mux, &self.muxparams);
184+
if mux_error != WebPMuxError::WEBP_MUX_OK {
185+
return Err(AnimEncodeError::WebPMuxError(mux_error));
186+
}
187+
let mut raw_data: WebPData = webp_data.assume_init();
188+
WebPDataClear(&mut raw_data);
189+
let mut webp_data = std::mem::MaybeUninit::<WebPData>::uninit();
190+
WebPMuxAssemble(mux, webp_data.as_mut_ptr());
191+
WebPMuxDelete(mux);
192+
let raw_data: WebPData = webp_data.assume_init();
193+
Ok(WebPMemory(raw_data.bytes as *mut u8, raw_data.size))
194+
}
195+
}
196+
}
197+
198+
impl Drop for AnimEncoder<'_> {
199+
fn drop(&mut self) {
200+
unsafe { WebPAnimEncoderDelete(self.encoder) };
141201
}
142202
}
143203

@@ -147,59 +207,3 @@ pub enum AnimEncodeError {
147207
WebPMuxError(WebPMuxError),
148208
WebPAnimEncoderGetError(String),
149209
}
150-
unsafe fn anim_encode(all_frame: &AnimEncoder) -> Result<WebPMemory, AnimEncodeError> {
151-
let width = all_frame.width;
152-
let height = all_frame.height;
153-
let mut uninit = std::mem::MaybeUninit::<WebPAnimEncoderOptions>::uninit();
154-
155-
let mux_abi_version = WebPGetMuxABIVersion();
156-
WebPAnimEncoderOptionsInitInternal(uninit.as_mut_ptr(), mux_abi_version);
157-
let encoder = WebPAnimEncoderNewInternal(
158-
width as i32,
159-
height as i32,
160-
uninit.as_ptr(),
161-
mux_abi_version,
162-
);
163-
let mut frame_pictures = vec![];
164-
for frame in all_frame.frames.iter() {
165-
let mut pic = crate::new_picture(frame.image, frame.layout, width, height);
166-
let config = frame.config.unwrap_or(all_frame.config);
167-
let ok = WebPAnimEncoderAdd(
168-
encoder,
169-
&mut *pic as *mut _,
170-
frame.timestamp as std::os::raw::c_int,
171-
config,
172-
);
173-
if ok == 0 {
174-
//ok == false
175-
WebPAnimEncoderDelete(encoder);
176-
return Err(AnimEncodeError::WebPEncodingError(pic.error_code));
177-
}
178-
frame_pictures.push(pic);
179-
}
180-
WebPAnimEncoderAdd(encoder, std::ptr::null_mut(), 0, std::ptr::null());
181-
182-
let mut webp_data = std::mem::MaybeUninit::<WebPData>::uninit();
183-
let ok = WebPAnimEncoderAssemble(encoder, webp_data.as_mut_ptr());
184-
if ok == 0 {
185-
//ok == false
186-
let cstring = WebPAnimEncoderGetError(encoder);
187-
let cstring = CString::from_raw(cstring as *mut _);
188-
let string = cstring.to_string_lossy().to_string();
189-
WebPAnimEncoderDelete(encoder);
190-
return Err(AnimEncodeError::WebPAnimEncoderGetError(string));
191-
}
192-
WebPAnimEncoderDelete(encoder);
193-
let mux = WebPMuxCreateInternal(webp_data.as_ptr(), 1, mux_abi_version);
194-
let mux_error = WebPMuxSetAnimationParams(mux, &all_frame.muxparams);
195-
if mux_error != WebPMuxError::WEBP_MUX_OK {
196-
return Err(AnimEncodeError::WebPMuxError(mux_error));
197-
}
198-
let mut raw_data: WebPData = webp_data.assume_init();
199-
WebPDataClear(&mut raw_data);
200-
let mut webp_data = std::mem::MaybeUninit::<WebPData>::uninit();
201-
WebPMuxAssemble(mux, webp_data.as_mut_ptr());
202-
WebPMuxDelete(mux);
203-
let raw_data: WebPData = webp_data.assume_init();
204-
Ok(WebPMemory(raw_data.bytes as *mut u8, raw_data.size))
205-
}

0 commit comments

Comments
 (0)