[go: up one dir, main page]

libwebp/
decode.rs

1use libwebp_sys as sys;
2use std::mem;
3use std::os::raw::*;
4use std::panic::{RefUnwindSafe, UnwindSafe};
5use std::ptr::{self, NonNull};
6use std::slice;
7
8use crate::boxed::{wrap_bytes, WebpBox, WebpYuvBox};
9use crate::error::WebPSimpleError;
10
11/// Return the decoder's version number, packed in hexadecimal using 8bits for
12/// each of major/minor/revision.
13///
14/// E.g: v2.5.7 is `0x020507`.
15///
16/// ## Examples
17///
18/// ```rust
19/// use libwebp::WebPGetDecoderVersion;
20///
21/// let version = WebPGetDecoderVersion();
22/// ```
23#[allow(non_snake_case)]
24pub fn WebPGetDecoderVersion() -> u32 {
25    (unsafe { sys::WebPGetDecoderVersion() }) as u32
26}
27
28/// Retrieve basic header information: width, height.
29///
30/// This function will also validate the header, returning
31/// `Ok((width, height))` on success, `Err(_)` otherwise.
32///
33/// ## Errors
34///
35/// Returns `Err` if `data` doesn't contain a valid WebP header.
36///
37/// ## Examples
38///
39/// ```rust
40/// use libwebp::WebPGetInfo;
41///
42/// let data: &[u8];
43/// # let data: &[u8] = include_bytes!("lena.webp");
44///
45/// let (width, height) = WebPGetInfo(data).expect("Invalid WebP data");
46/// # assert_eq!((width, height), (128, 128));
47/// ```
48#[allow(non_snake_case)]
49pub fn WebPGetInfo(data: &[u8]) -> Result<(u32, u32), WebPSimpleError> {
50    let mut width: c_int = 0;
51    let mut height: c_int = 0;
52    let result = unsafe { sys::WebPGetInfo(data.as_ptr(), data.len(), &mut width, &mut height) };
53    if result != 0 {
54        Ok((width as u32, height as u32))
55    } else {
56        Err(WebPSimpleError)
57    }
58}
59
60/// Decodes WebP images pointed to by `data` and returns RGBA samples, along
61/// with the dimensions (width and height).
62///
63/// The ordering of samples in memory is R, G, B, A, R, G, B, A... in scan
64/// order (endian-independent).
65///
66/// ## Errors
67///
68/// Returns `Err` if `data` doesn't contain a valid WebP image.
69///
70/// ## Variants
71///
72/// - `WebPDecodeRGBA`
73/// - [`WebPDecodeARGB`]
74/// - [`WebPDecodeBGRA`]
75/// - [`WebPDecodeRGB`]
76/// - [`WebPDecodeBGR`]
77///
78/// [`WebPDecodeARGB`]: fn.WebPDecodeARGB.html
79/// [`WebPDecodeBGRA`]: fn.WebPDecodeBGRA.html
80/// [`WebPDecodeRGB`]: fn.WebPDecodeRGB.html
81/// [`WebPDecodeBGR`]: fn.WebPDecodeBGR.html
82///
83/// ## Examples
84///
85/// ```rust
86/// use libwebp::WebPDecodeRGBA;
87///
88/// let data: &[u8];
89/// # let data: &[u8] = include_bytes!("lena.webp");
90///
91/// let (width, height, buf) = WebPDecodeRGBA(data).expect("Invalid WebP data");
92/// # assert_eq!((width, height), (128, 128));
93/// assert_eq!(buf.len(), width as usize * height as usize * 4);
94/// eprintln!(
95///     "top-left pixel: rgba({}, {}, {}, {})",
96///     buf[0],
97///     buf[1],
98///     buf[2],
99///     buf[3] as f64 / 255.0,
100/// )
101/// ```
102#[allow(non_snake_case)]
103pub fn WebPDecodeRGBA(data: &[u8]) -> Result<(u32, u32, WebpBox<[u8]>), WebPSimpleError> {
104    let mut width: c_int = 0;
105    let mut height: c_int = 0;
106    let result = unsafe { sys::WebPDecodeRGBA(data.as_ptr(), data.len(), &mut width, &mut height) };
107    let buf = (unsafe { wrap_bytes(result, || width as usize * height as usize * 4) })?;
108    Ok((width as u32, height as u32, buf))
109}
110
111/// Same as [`WebPDecodeRGBA`], but returning A, R, G, B, A, R, G, B...
112/// ordered data.
113///
114/// [`WebPDecodeRGBA`]: fn.WebPDecodeRGBA.html
115#[allow(non_snake_case)]
116pub fn WebPDecodeARGB(data: &[u8]) -> Result<(u32, u32, WebpBox<[u8]>), WebPSimpleError> {
117    let mut width: c_int = 0;
118    let mut height: c_int = 0;
119    let result = unsafe { sys::WebPDecodeARGB(data.as_ptr(), data.len(), &mut width, &mut height) };
120    let buf = (unsafe { wrap_bytes(result, || width as usize * height as usize * 4) })?;
121    Ok((width as u32, height as u32, buf))
122}
123
124/// Same as [`WebPDecodeRGBA`], but returning B, G, R, A, B, G, R, A...
125/// ordered data.
126///
127/// [`WebPDecodeRGBA`]: fn.WebPDecodeRGBA.html
128#[allow(non_snake_case)]
129pub fn WebPDecodeBGRA(data: &[u8]) -> Result<(u32, u32, WebpBox<[u8]>), WebPSimpleError> {
130    let mut width: c_int = 0;
131    let mut height: c_int = 0;
132    let result = unsafe { sys::WebPDecodeBGRA(data.as_ptr(), data.len(), &mut width, &mut height) };
133    let buf = (unsafe { wrap_bytes(result, || width as usize * height as usize * 4) })?;
134    Ok((width as u32, height as u32, buf))
135}
136
137/// Same as [`WebPDecodeRGBA`], but returning R, G, B, R, G, B... ordered data.
138///
139/// If the bitstream contains transparency, it is ignored.
140///
141/// [`WebPDecodeRGBA`]: fn.WebPDecodeRGBA.html
142#[allow(non_snake_case)]
143pub fn WebPDecodeRGB(data: &[u8]) -> Result<(u32, u32, WebpBox<[u8]>), WebPSimpleError> {
144    let mut width: c_int = 0;
145    let mut height: c_int = 0;
146    let result = unsafe { sys::WebPDecodeRGB(data.as_ptr(), data.len(), &mut width, &mut height) };
147    let buf = (unsafe { wrap_bytes(result, || width as usize * height as usize * 3) })?;
148    Ok((width as u32, height as u32, buf))
149}
150
151/// Same as [`WebPDecodeRGBA`], but returning B, G, R, B, G, R... ordered data.
152///
153/// If the bitstream contains transparency, it is ignored.
154///
155/// [`WebPDecodeRGBA`]: fn.WebPDecodeRGBA.html
156#[allow(non_snake_case)]
157pub fn WebPDecodeBGR(data: &[u8]) -> Result<(u32, u32, WebpBox<[u8]>), WebPSimpleError> {
158    let mut width: c_int = 0;
159    let mut height: c_int = 0;
160    let result = unsafe { sys::WebPDecodeBGR(data.as_ptr(), data.len(), &mut width, &mut height) };
161    let buf = (unsafe { wrap_bytes(result, || width as usize * height as usize * 3) })?;
162    Ok((width as u32, height as u32, buf))
163}
164
165/// Decodes WebP images pointed to by `data` to Y'UV format[^1].
166///
167/// [^1] Also named Y'CbCr. See: [http://en.wikipedia.org/wiki/YCbCr](http://en.wikipedia.org/wiki/YCbCr)
168///
169/// ## Return value
170///
171/// It retuns a tuple with the following data in this order:
172///
173/// - width
174/// - height
175/// - stride
176/// - uv\_stride
177/// - a [`WebpYuvBox`] which contains the pointers to the Y, U and V planes.
178///
179/// [`WebpYuvBox`]: boxed/struct.WebPYuvBox.html
180///
181/// The dimension of the U and V planes are both `(width + 1) / 2` and `(height + 1)/ 2`.
182/// The Y buffer has a stride returned as `stride`, while U and V
183/// have a common stride returned as `uv_stride`.
184///
185/// ## Errors
186///
187/// Returns `Err` if `data` doesn't contain a valid WebP image.
188///
189/// ## Examples
190///
191/// ```rust
192/// use libwebp::WebPDecodeYUV;
193///
194/// let data: &[u8];
195/// # let data: &[u8] = include_bytes!("lena.webp");
196///
197/// let (width, height, stride, uv_stride, buf) =
198///     WebPDecodeYUV(data).expect("Invalid WebP data");
199/// # assert_eq!((width, height), (128, 128));
200/// assert!(width <= stride);
201/// assert!((width + 1) / 2 <= uv_stride);
202/// assert_eq!(buf.y().len(), stride as usize * height as usize);
203/// assert_eq!(buf.u().len(), uv_stride as usize * ((height as usize + 1) / 2));
204/// assert_eq!(buf.v().len(), uv_stride as usize * ((height as usize + 1) / 2));
205/// eprintln!(
206///     "top-left pixel: yuv({}, {}, {})",
207///     buf.y()[0],
208///     buf.u()[0],
209///     buf.v()[0],
210/// )
211/// ```
212#[allow(non_snake_case)]
213pub fn WebPDecodeYUV(data: &[u8]) -> Result<(u32, u32, u32, u32, WebpYuvBox), WebPSimpleError> {
214    let mut width: c_int = 0;
215    let mut height: c_int = 0;
216    let mut u: *mut u8 = ptr::null_mut();
217    let mut v: *mut u8 = ptr::null_mut();
218    let mut stride: c_int = 0;
219    let mut uv_stride: c_int = 0;
220    let result = unsafe {
221        sys::WebPDecodeYUV(
222            data.as_ptr(),
223            data.len(),
224            &mut width,
225            &mut height,
226            &mut u,
227            &mut v,
228            &mut stride,
229            &mut uv_stride,
230        )
231    };
232    if !result.is_null() {
233        let y_len = height as usize * stride as usize;
234        let uv_len = (height as usize + 1) / 2 * uv_stride as usize;
235        let buf = unsafe {
236            WebpYuvBox::from_raw_yuv(
237                slice::from_raw_parts_mut(result, y_len),
238                slice::from_raw_parts_mut(u, uv_len),
239                slice::from_raw_parts_mut(v, uv_len),
240            )
241        };
242        Ok((
243            width as u32,
244            height as u32,
245            stride as u32,
246            uv_stride as u32,
247            buf,
248        ))
249    } else {
250        Err(WebPSimpleError)
251    }
252}
253
254/// Decodes WebP images pointed to by `data` and writes RGBA samples to
255/// `output_buffer`.
256///
257/// The parameter 'output_stride' specifies the distance (in bytes)
258/// between scanlines. Hence, `output_buffer.len()` is expected to be at least
259/// `output_stride` x `picture-height`.
260///
261/// The ordering of samples in memory is R, G, B, A, R, G, B, A... in scan
262/// order (endian-independent).
263///
264/// ## Errors
265///
266/// Returns `Err` if `data` doesn't contain a valid WebP image, or
267/// `output_buffer` is too small.
268///
269/// ## Variants
270///
271/// - `WebPDecodeRGBAInto`
272/// - [`WebPDecodeARGBInto`]
273/// - [`WebPDecodeBGRAInto`]
274/// - [`WebPDecodeRGBInto`]
275/// - [`WebPDecodeBGRInto`]
276///
277/// [`WebPDecodeARGBInto`]: fn.WebPDecodeARGBInto.html
278/// [`WebPDecodeBGRAInto`]: fn.WebPDecodeBGRAInto.html
279/// [`WebPDecodeRGBInto`]: fn.WebPDecodeRGBInto.html
280/// [`WebPDecodeBGRInto`]: fn.WebPDecodeBGRInto.html
281///
282/// ## Examples
283///
284/// ```rust
285/// use libwebp::{WebPGetInfo, WebPDecodeRGBAInto};
286///
287/// let data: &[u8];
288/// # let data: &[u8] = include_bytes!("lena.webp");
289///
290/// let (width, height) = WebPGetInfo(data).expect("Invalid WebP header");
291/// # assert_eq!((width, height), (128, 128));
292/// let stride = width * 4;
293/// let mut buf = vec![0; stride as usize * height as usize];
294/// WebPDecodeRGBAInto(data, &mut buf, stride).expect("Invalid WebP data");
295/// eprintln!(
296///     "top-left pixel: rgba({}, {}, {}, {})",
297///     buf[0],
298///     buf[1],
299///     buf[2],
300///     buf[3] as f64 / 255.0,
301/// )
302/// ```
303#[allow(non_snake_case)]
304pub fn WebPDecodeRGBAInto(
305    data: &[u8],
306    output_buffer: &mut [u8],
307    output_stride: u32,
308) -> Result<(), WebPSimpleError> {
309    assert!(output_stride as c_int >= 0);
310    assert_eq!(output_stride as c_int as u32, output_stride);
311    let result = unsafe {
312        sys::WebPDecodeRGBAInto(
313            data.as_ptr(),
314            data.len(),
315            output_buffer.as_mut_ptr(),
316            output_buffer.len(),
317            output_stride as c_int,
318        )
319    };
320    if !result.is_null() {
321        Ok(())
322    } else {
323        Err(WebPSimpleError)
324    }
325}
326
327/// Same as [`WebPDecodeRGBAInto`], but returning A, R, G, B, A, R, G, B...
328/// ordered data.
329///
330/// [`WebPDecodeRGBAInto`]: fn.WebPDecodeRGBAInto.html
331#[allow(non_snake_case)]
332pub fn WebPDecodeARGBInto(
333    data: &[u8],
334    output_buffer: &mut [u8],
335    output_stride: u32,
336) -> Result<(), WebPSimpleError> {
337    assert!(output_stride as c_int >= 0);
338    assert_eq!(output_stride as c_int as u32, output_stride);
339    let result = unsafe {
340        sys::WebPDecodeARGBInto(
341            data.as_ptr(),
342            data.len(),
343            output_buffer.as_mut_ptr(),
344            output_buffer.len(),
345            output_stride as c_int,
346        )
347    };
348    if !result.is_null() {
349        Ok(())
350    } else {
351        Err(WebPSimpleError)
352    }
353}
354
355/// Same as [`WebPDecodeRGBAInto`], but returning B, G, R, A, B, G, R, A...
356/// ordered data.
357///
358/// [`WebPDecodeRGBAInto`]: fn.WebPDecodeRGBAInto.html
359#[allow(non_snake_case)]
360pub fn WebPDecodeBGRAInto(
361    data: &[u8],
362    output_buffer: &mut [u8],
363    output_stride: u32,
364) -> Result<(), WebPSimpleError> {
365    assert!(output_stride as c_int >= 0);
366    assert_eq!(output_stride as c_int as u32, output_stride);
367    let result = unsafe {
368        sys::WebPDecodeBGRAInto(
369            data.as_ptr(),
370            data.len(),
371            output_buffer.as_mut_ptr(),
372            output_buffer.len(),
373            output_stride as c_int,
374        )
375    };
376    if !result.is_null() {
377        Ok(())
378    } else {
379        Err(WebPSimpleError)
380    }
381}
382
383/// Same as [`WebPDecodeRGBAInto`], but returning R, G, B, R, G, B...
384/// ordered data.
385///
386/// If the bitstream contains transparency, it is ignored.
387///
388/// [`WebPDecodeRGBAInto`]: fn.WebPDecodeRGBAInto.html
389#[allow(non_snake_case)]
390pub fn WebPDecodeRGBInto(
391    data: &[u8],
392    output_buffer: &mut [u8],
393    output_stride: u32,
394) -> Result<(), WebPSimpleError> {
395    assert!(output_stride as c_int >= 0);
396    assert_eq!(output_stride as c_int as u32, output_stride);
397    let result = unsafe {
398        sys::WebPDecodeRGBInto(
399            data.as_ptr(),
400            data.len(),
401            output_buffer.as_mut_ptr(),
402            output_buffer.len(),
403            output_stride as c_int,
404        )
405    };
406    if !result.is_null() {
407        Ok(())
408    } else {
409        Err(WebPSimpleError)
410    }
411}
412
413/// Same as [`WebPDecodeRGBAInto`], but returning B, G, R, B, G, R...
414/// ordered data.
415///
416/// If the bitstream contains transparency, it is ignored.
417///
418/// [`WebPDecodeRGBAInto`]: fn.WebPDecodeRGBAInto.html
419#[allow(non_snake_case)]
420pub fn WebPDecodeBGRInto(
421    data: &[u8],
422    output_buffer: &mut [u8],
423    output_stride: u32,
424) -> Result<(), WebPSimpleError> {
425    assert!(output_stride as c_int >= 0);
426    assert_eq!(output_stride as c_int as u32, output_stride);
427    let result = unsafe {
428        sys::WebPDecodeBGRInto(
429            data.as_ptr(),
430            data.len(),
431            output_buffer.as_mut_ptr(),
432            output_buffer.len(),
433            output_stride as c_int,
434        )
435    };
436    if !result.is_null() {
437        Ok(())
438    } else {
439        Err(WebPSimpleError)
440    }
441}
442
443/// A variant of [`WebPDecodeYUVInto`] that operates directly into
444/// pre-allocated buffers.
445///
446/// [`WebPDecodeYUVInto`]: fn.WebPDecodeYUVInto.html
447///
448/// ## Errors
449///
450/// Returns `Err` if `data` doesn't contain a valid WebP image, or
451/// any of the buffers is too small.
452///
453/// ## Examples
454///
455/// ```rust
456/// use libwebp::{WebPGetInfo, WebPDecodeYUVInto};
457///
458/// let data: &[u8];
459/// # let data: &[u8] = include_bytes!("lena.webp");
460///
461/// let (width, height) = WebPGetInfo(data).expect("Invalid WebP header");
462/// # assert_eq!((width, height), (128, 128));
463/// let luma_stride = width;
464/// let u_stride = (width + 1) / 2;
465/// let v_stride = (width + 1) / 2;
466/// let mut luma = vec![0; luma_stride as usize * height as usize];
467/// let mut u = vec![0; u_stride as usize * ((height + 1) / 2) as usize];
468/// let mut v = vec![0; v_stride as usize * ((height + 1) / 2) as usize];
469///
470/// WebPDecodeYUVInto(
471///     data,
472///     &mut luma,
473///     luma_stride,
474///     &mut u,
475///     u_stride,
476///     &mut v,
477///     v_stride,
478/// ).expect("Invalid WebP header");
479/// eprintln!(
480///     "top-left pixel: yuv({}, {}, {})",
481///     luma[0],
482///     u[0],
483///     v[0],
484/// )
485/// ```
486#[allow(non_snake_case)]
487pub fn WebPDecodeYUVInto(
488    data: &[u8],
489    luma: &mut [u8],
490    luma_stride: u32,
491    u: &mut [u8],
492    u_stride: u32,
493    v: &mut [u8],
494    v_stride: u32,
495) -> Result<(), WebPSimpleError> {
496    assert!(luma_stride as c_int >= 0);
497    assert_eq!(luma_stride as c_int as u32, luma_stride);
498    assert!(u_stride as c_int >= 0);
499    assert_eq!(u_stride as c_int as u32, u_stride);
500    assert!(v_stride as c_int >= 0);
501    assert_eq!(v_stride as c_int as u32, v_stride);
502    let result = unsafe {
503        sys::WebPDecodeYUVInto(
504            data.as_ptr(),
505            data.len(),
506            luma.as_mut_ptr(),
507            luma.len(),
508            luma_stride as c_int,
509            u.as_mut_ptr(),
510            u.len(),
511            u_stride as c_int,
512            v.as_mut_ptr(),
513            v.len(),
514            v_stride as c_int,
515        )
516    };
517    if !result.is_null() {
518        Ok(())
519    } else {
520        Err(WebPSimpleError)
521    }
522}
523
524#[allow(non_camel_case_types)]
525#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
526pub enum WEBP_CSP_MODE {
527    MODE_RGB = 0,
528    MODE_RGBA = 1,
529    MODE_BGR = 2,
530    MODE_BGRA = 3,
531    MODE_ARGB = 4,
532    MODE_RGBA_4444 = 5,
533    MODE_RGB_565 = 6,
534    /// A variant of `MODE_RGBA` with premultiplied alpha
535    MODE_rgbA = 7,
536    /// A variant of `MODE_BGRA` with premultiplied alpha
537    MODE_bgrA = 8,
538    /// A variant of `MODE_ARGB` with premultiplied alpha
539    MODE_Argb = 9,
540    /// A variant of `MODE_RGBA_4444` with premultiplied alpha
541    MODE_rgbA_4444 = 10,
542    // YUV modes must come after RGB ones.
543    MODE_YUV = 11,
544    MODE_YUVA = 12, // yuv 4:2:0
545}
546
547impl WEBP_CSP_MODE {
548    pub fn from_raw(raw: sys::WEBP_CSP_MODE) -> Self {
549        use self::WEBP_CSP_MODE::*;
550
551        match raw {
552            sys::MODE_RGB => MODE_RGB,
553            sys::MODE_RGBA => MODE_RGBA,
554            sys::MODE_BGR => MODE_BGR,
555            sys::MODE_BGRA => MODE_BGRA,
556            sys::MODE_ARGB => MODE_ARGB,
557            sys::MODE_RGBA_4444 => MODE_RGBA_4444,
558            sys::MODE_RGB_565 => MODE_RGB_565,
559            sys::MODE_rgbA => MODE_rgbA,
560            sys::MODE_bgrA => MODE_bgrA,
561            sys::MODE_Argb => MODE_Argb,
562            sys::MODE_rgbA_4444 => MODE_rgbA_4444,
563            sys::MODE_YUV => MODE_YUV,
564            sys::MODE_YUVA => MODE_YUVA,
565            _ => panic!("WEBP_CSP_MODE::from_raw: unknown value {:?}", raw),
566        }
567    }
568
569    pub fn into_raw(self) -> sys::WEBP_CSP_MODE {
570        use self::WEBP_CSP_MODE::*;
571
572        match self {
573            MODE_RGB => sys::MODE_RGB,
574            MODE_RGBA => sys::MODE_RGBA,
575            MODE_BGR => sys::MODE_BGR,
576            MODE_BGRA => sys::MODE_BGRA,
577            MODE_ARGB => sys::MODE_ARGB,
578            MODE_RGBA_4444 => sys::MODE_RGBA_4444,
579            MODE_RGB_565 => sys::MODE_RGB_565,
580            MODE_rgbA => sys::MODE_rgbA,
581            MODE_bgrA => sys::MODE_bgrA,
582            MODE_Argb => sys::MODE_Argb,
583            MODE_rgbA_4444 => sys::MODE_rgbA_4444,
584            MODE_YUV => sys::MODE_YUV,
585            MODE_YUVA => sys::MODE_YUVA,
586        }
587    }
588}
589
590#[allow(non_snake_case)]
591pub fn WebPIsPremultipliedMode(mode: WEBP_CSP_MODE) -> bool {
592    use self::WEBP_CSP_MODE::*;
593
594    match mode {
595        MODE_rgbA | MODE_bgrA | MODE_Argb | MODE_rgbA_4444 => true,
596        MODE_RGB | MODE_RGBA | MODE_BGR | MODE_BGRA | MODE_ARGB | MODE_RGBA_4444 | MODE_RGB_565
597        | MODE_YUV | MODE_YUVA => false,
598    }
599}
600
601#[allow(non_snake_case)]
602pub fn WebPIsAlphaMode(mode: WEBP_CSP_MODE) -> bool {
603    use self::WEBP_CSP_MODE::*;
604
605    match mode {
606        MODE_RGBA | MODE_BGRA | MODE_ARGB | MODE_RGBA_4444 | MODE_rgbA | MODE_bgrA | MODE_Argb
607        | MODE_rgbA_4444 | MODE_YUVA => true,
608        MODE_RGB | MODE_BGR | MODE_RGB_565 | MODE_YUV => false,
609    }
610}
611
612#[allow(non_snake_case)]
613pub fn WebPIsRGBMode(mode: WEBP_CSP_MODE) -> bool {
614    use self::WEBP_CSP_MODE::*;
615
616    match mode {
617        MODE_RGB | MODE_RGBA | MODE_BGR | MODE_BGRA | MODE_ARGB | MODE_RGBA_4444 | MODE_RGB_565
618        | MODE_rgbA | MODE_bgrA | MODE_Argb | MODE_rgbA_4444 => true,
619        MODE_YUV | MODE_YUVA => false,
620    }
621}
622
623// #[derive(Debug)]
624// pub struct WebPDecBuffer(sys::WebPDecBuffer);
625//
626// unsafe impl Send for WebPDecBuffer {}
627// unsafe impl Sync for WebPDecBuffer {}
628//
629// impl Drop for WebPDecBuffer {
630//     fn drop(&mut self) {
631//         unsafe {
632//             sys::WebPFreeDecBuffer(&mut self.0);
633//         }
634//     }
635// }
636//
637// impl WebPDecBuffer {
638//     pub unsafe fn from_raw(raw: sys::WebPDecBuffer) -> Self {
639//         debug_assert_eq!(raw.is_external_memory, 0, "is_external_memory should be 0");
640//         WebPDecBuffer(raw)
641//     }
642//
643//     pub fn into_raw(self) -> sys::WebPDecBuffer {
644//         let ret = unsafe { ptr::read(&self.0) };
645//         mem::forget(self);
646//         ret
647//     }
648//
649//     pub fn colorspace(&self) -> WEBP_CSP_MODE {
650//         WEBP_CSP_MODE::from_raw(self.0.colorspace)
651//     }
652//
653//     pub fn set_colorspace(&mut self, colorspace: WEBP_CSP_MODE) {
654//         self.0.colorspace = colorspace.into_raw();
655//     }
656//
657//     pub fn width(&self) -> u32 {
658//         self.0.width as u32
659//     }
660//
661//     pub fn set_width(&mut self, width: u32) {
662//         assert!(width as c_int >= 0);
663//         assert_eq!(width as c_int as u32, width);
664//         self.0.width = width as c_int;
665//     }
666//
667//     pub fn height(&self) -> u32 {
668//         self.0.height as u32
669//     }
670//
671//     pub fn set_height(&mut self, height: u32) {
672//         assert!(height as c_int >= 0);
673//         assert_eq!(height as c_int as u32, height);
674//         self.0.height = height as c_int;
675//     }
676// }
677//
678// #[allow(non_snake_case)]
679// pub fn WebPInitDecBuffer() -> WebPDecBuffer {
680//     let mut buf: sys::WebPDecBuffer = unsafe { mem::zeroed() };
681//     let result = unsafe { sys::WebPInitDecBuffer(&mut buf) };
682//     if result != 0 {
683//         unsafe { WebPDecBuffer::from_raw(buf) }
684//     } else {
685//         panic!("libwebp version mismatch")
686//     }
687// }
688
689#[allow(non_camel_case_types)]
690#[must_use]
691#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
692pub enum VP8StatusCode {
693    VP8_STATUS_OK = 0,
694    VP8_STATUS_OUT_OF_MEMORY = 1,
695    VP8_STATUS_INVALID_PARAM = 2,
696    VP8_STATUS_BITSTREAM_ERROR = 3,
697    VP8_STATUS_UNSUPPORTED_FEATURE = 4,
698    VP8_STATUS_SUSPENDED = 5,
699    VP8_STATUS_USER_ABORT = 6,
700    VP8_STATUS_NOT_ENOUGH_DATA = 7,
701}
702
703impl VP8StatusCode {
704    pub fn from_raw(raw: sys::VP8StatusCode) -> Self {
705        use self::VP8StatusCode::*;
706
707        match raw {
708            sys::VP8_STATUS_OK => VP8_STATUS_OK,
709            sys::VP8_STATUS_OUT_OF_MEMORY => VP8_STATUS_OUT_OF_MEMORY,
710            sys::VP8_STATUS_INVALID_PARAM => VP8_STATUS_INVALID_PARAM,
711            sys::VP8_STATUS_BITSTREAM_ERROR => VP8_STATUS_BITSTREAM_ERROR,
712            sys::VP8_STATUS_UNSUPPORTED_FEATURE => VP8_STATUS_UNSUPPORTED_FEATURE,
713            sys::VP8_STATUS_SUSPENDED => VP8_STATUS_SUSPENDED,
714            sys::VP8_STATUS_USER_ABORT => VP8_STATUS_USER_ABORT,
715            sys::VP8_STATUS_NOT_ENOUGH_DATA => VP8_STATUS_NOT_ENOUGH_DATA,
716            _ => panic!("VP8StatusCode::from_raw: unknown value {:?}", raw),
717        }
718    }
719
720    pub fn into_raw(self) -> sys::WEBP_CSP_MODE {
721        use self::VP8StatusCode::*;
722
723        match self {
724            VP8_STATUS_OK => sys::VP8_STATUS_OK,
725            VP8_STATUS_OUT_OF_MEMORY => sys::VP8_STATUS_OUT_OF_MEMORY,
726            VP8_STATUS_INVALID_PARAM => sys::VP8_STATUS_INVALID_PARAM,
727            VP8_STATUS_BITSTREAM_ERROR => sys::VP8_STATUS_BITSTREAM_ERROR,
728            VP8_STATUS_UNSUPPORTED_FEATURE => sys::VP8_STATUS_UNSUPPORTED_FEATURE,
729            VP8_STATUS_SUSPENDED => sys::VP8_STATUS_SUSPENDED,
730            VP8_STATUS_USER_ABORT => sys::VP8_STATUS_USER_ABORT,
731            VP8_STATUS_NOT_ENOUGH_DATA => sys::VP8_STATUS_NOT_ENOUGH_DATA,
732        }
733    }
734}
735
736#[derive(Debug)]
737pub struct WebPIDecoder(NonNull<sys::WebPIDecoder>);
738
739unsafe impl Send for WebPIDecoder {}
740unsafe impl Sync for WebPIDecoder {}
741impl UnwindSafe for WebPIDecoder {}
742impl RefUnwindSafe for WebPIDecoder {}
743
744impl Drop for WebPIDecoder {
745    fn drop(&mut self) {
746        unsafe {
747            sys::WebPIDelete(self.0.as_ptr());
748        }
749    }
750}
751
752impl WebPIDecoder {
753    pub unsafe fn from_raw(raw: NonNull<sys::WebPIDecoder>) -> Self {
754        WebPIDecoder(raw)
755    }
756
757    pub fn into_raw(self) -> NonNull<sys::WebPIDecoder> {
758        let ret = self.0;
759        mem::forget(self);
760        ret
761    }
762
763    pub fn as_ptr(&self) -> *const sys::WebPIDecoder {
764        unsafe { self.0.as_ref() }
765    }
766
767    pub fn as_mut_ptr(&mut self) -> *mut sys::WebPIDecoder {
768        unsafe { self.0.as_mut() }
769    }
770}
771
772// TODO: Implment external version
773#[allow(non_snake_case)]
774pub fn WebPINewDecoder() -> WebPIDecoder {
775    let result = unsafe { sys::WebPINewDecoder(ptr::null_mut()) };
776    if let Some(result) = NonNull::new(result) {
777        unsafe { WebPIDecoder::from_raw(result) }
778    } else {
779        panic!("WebPINewDecoder: allocation failed");
780    }
781}
782
783// TODO: Implment external version
784#[allow(non_snake_case)]
785pub fn WebPINewRGB(csp: WEBP_CSP_MODE) -> WebPIDecoder {
786    assert!(WebPIsRGBMode(csp), "Not an RGB mode: {:?}", csp);
787    let result = unsafe { sys::WebPINewRGB(csp.into_raw(), ptr::null_mut(), 0, 0) };
788    if let Some(result) = NonNull::new(result) {
789        unsafe { WebPIDecoder::from_raw(result) }
790    } else {
791        panic!("WebPINewRGB: allocation failed");
792    }
793}
794
795// TODO: Implment external version
796#[allow(non_snake_case)]
797pub fn WebPINewYUVA() -> WebPIDecoder {
798    let result = unsafe {
799        sys::WebPINewYUVA(
800            ptr::null_mut(),
801            0,
802            0,
803            ptr::null_mut(),
804            0,
805            0,
806            ptr::null_mut(),
807            0,
808            0,
809            ptr::null_mut(),
810            0,
811            0,
812        )
813    };
814    if let Some(result) = NonNull::new(result) {
815        unsafe { WebPIDecoder::from_raw(result) }
816    } else {
817        panic!("WebPINewYUVA: allocation failed");
818    }
819}
820
821#[allow(non_snake_case)]
822pub fn WebPIAppend(idec: &mut WebPIDecoder, data: &[u8]) -> VP8StatusCode {
823    if data.is_empty() {
824        // libwebp AppendToMemBuffer (src/dec/idec_dec.c) doesn't expect empty slice at the beginning.
825        // Since the decoder status cannot be observed otherwise, we just panic here for now.
826        panic!("WebPIAppend: appending an empty slice is not supported for now");
827    }
828    let result = unsafe { sys::WebPIAppend(idec.as_mut_ptr(), data.as_ptr(), data.len()) };
829    VP8StatusCode::from_raw(result)
830}
831
832// TODO: check if it's safe to inconsistently pass data into WebPIUpdate
833// #[allow(non_snake_case)]
834// pub fn WebPIUpdate(idec: &mut WebPIDecoder, data: &[u8]) -> VP8StatusCode {
835//     let result = unsafe { sys::WebPIUpdate(idec.as_mut_ptr(), data.as_ptr(), data.len()) };
836//     VP8StatusCode::from_raw(result)
837// }
838
839#[derive(Debug)]
840pub struct WebPIDecGetRGBResult<'a> {
841    pub buf: &'a [u8],
842    pub last_y: u32,
843    pub width: u32,
844    pub height: u32,
845    pub stride: u32,
846}
847
848#[allow(non_snake_case)]
849pub fn WebPIDecGetRGB(idec: &WebPIDecoder) -> Result<WebPIDecGetRGBResult<'_>, WebPSimpleError> {
850    let mut last_y: c_int = 0;
851    let mut width: c_int = 0;
852    let mut height: c_int = 0;
853    let mut stride: c_int = 0;
854    let result = unsafe {
855        sys::WebPIDecGetRGB(
856            idec.as_ptr(),
857            &mut last_y,
858            &mut width,
859            &mut height,
860            &mut stride,
861        )
862    };
863    if !result.is_null() {
864        // TODO: can this be stride * height?
865        let len = stride as usize * last_y as usize;
866        let buf = unsafe { slice::from_raw_parts(result, len) };
867        Ok(WebPIDecGetRGBResult {
868            buf,
869            last_y: last_y as u32,
870            width: width as u32,
871            height: height as u32,
872            stride: stride as u32,
873        })
874    } else {
875        Err(WebPSimpleError)
876    }
877}
878
879#[derive(Debug)]
880pub struct WebPIDecGetYUVAResult<'a> {
881    pub luma: &'a [u8],
882    pub last_y: u32,
883    pub u: &'a [u8],
884    pub v: &'a [u8],
885    pub a: Option<&'a [u8]>,
886    pub width: u32,
887    pub height: u32,
888    pub stride: u32,
889    pub uv_stride: u32,
890    pub a_stride: u32,
891}
892
893#[allow(non_snake_case)]
894pub fn WebPIDecGetYUVA(idec: &WebPIDecoder) -> Result<WebPIDecGetYUVAResult<'_>, WebPSimpleError> {
895    let mut last_y: c_int = 0;
896    let mut u: *mut u8 = ptr::null_mut();
897    let mut v: *mut u8 = ptr::null_mut();
898    let mut a: *mut u8 = ptr::null_mut();
899    let mut width: c_int = 0;
900    let mut height: c_int = 0;
901    let mut stride: c_int = 0;
902    let mut uv_stride: c_int = 0;
903    let mut a_stride: c_int = 0;
904    let result = unsafe {
905        sys::WebPIDecGetYUVA(
906            idec.as_ptr(),
907            &mut last_y,
908            &mut u,
909            &mut v,
910            &mut a,
911            &mut width,
912            &mut height,
913            &mut stride,
914            &mut uv_stride,
915            &mut a_stride,
916        )
917    };
918    if !result.is_null() {
919        // TODO: can this be stride * height?
920        let luma_len = stride as usize * last_y as usize;
921        let luma = unsafe { slice::from_raw_parts(result, luma_len) };
922        // TODO: can this be uv_stride * ((height + 1) / 2)?
923        let uv_len = uv_stride as usize * ((last_y as usize + 1) / 2);
924        let u = unsafe { slice::from_raw_parts(u as *const u8, uv_len) };
925        let v = unsafe { slice::from_raw_parts(v as *const u8, uv_len) };
926        // TODO: can this be a_stride * height?
927        let a = if !a.is_null() {
928            let a_len = a_stride as usize * last_y as usize;
929            Some(unsafe { slice::from_raw_parts(a as *const u8, a_len) })
930        } else {
931            None
932        };
933        Ok(WebPIDecGetYUVAResult {
934            luma,
935            last_y: last_y as u32,
936            u,
937            v,
938            a,
939            width: width as u32,
940            height: height as u32,
941            stride: stride as u32,
942            uv_stride: uv_stride as u32,
943            a_stride: a_stride as u32,
944        })
945    } else {
946        Err(WebPSimpleError)
947    }
948}
949
950#[cfg(test)]
951mod tests {
952    use rand::prelude::*;
953
954    use super::*;
955
956    fn lena() -> Vec<u8> {
957        include_bytes!("lena.webp").to_vec()
958    }
959
960    #[test]
961    #[allow(non_snake_case)]
962    fn test_WebPDecodeRGBA() {
963        let (width, height, buf) = WebPDecodeRGBA(&lena()).unwrap();
964        assert_eq!(width, 128);
965        assert_eq!(height, 128);
966        assert_eq!(
967            &buf[..24],
968            &[
969                226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113, 255,
970                223, 155, 109, 255, 223, 155, 109, 255,
971            ]
972        );
973    }
974
975    #[test]
976    #[allow(non_snake_case)]
977    fn test_WebPDecodeARGB() {
978        let (width, height, buf) = WebPDecodeARGB(&lena()).unwrap();
979        assert_eq!(width, 128);
980        assert_eq!(height, 128);
981        assert_eq!(
982            &buf[..24],
983            &[
984                255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113,
985                255, 223, 155, 109, 255, 223, 155, 109,
986            ]
987        );
988    }
989
990    #[test]
991    #[allow(non_snake_case)]
992    fn test_WebPDecodeBGRA() {
993        let (width, height, buf) = WebPDecodeBGRA(&lena()).unwrap();
994        assert_eq!(width, 128);
995        assert_eq!(height, 128);
996        assert_eq!(
997            &buf[..24],
998            &[
999                113, 158, 226, 255, 113, 158, 226, 255, 113, 158, 226, 255, 113, 158, 226, 255,
1000                109, 155, 223, 255, 109, 155, 223, 255,
1001            ]
1002        );
1003    }
1004
1005    #[test]
1006    #[allow(non_snake_case)]
1007    fn test_WebPDecodeRGB() {
1008        let (width, height, buf) = WebPDecodeRGB(&lena()).unwrap();
1009        assert_eq!(width, 128);
1010        assert_eq!(height, 128);
1011        assert_eq!(
1012            &buf[..24],
1013            &[
1014                226, 158, 113, 226, 158, 113, 226, 158, 113, 226, 158, 113, 223, 155, 109, 223,
1015                155, 109, 223, 155, 109, 223, 155, 109,
1016            ]
1017        );
1018    }
1019
1020    #[test]
1021    #[allow(non_snake_case)]
1022    fn test_WebPDecodeBGR() {
1023        let (width, height, buf) = WebPDecodeBGR(&lena()).unwrap();
1024        assert_eq!(width, 128);
1025        assert_eq!(height, 128);
1026        assert_eq!(
1027            &buf[..24],
1028            &[
1029                113, 158, 226, 113, 158, 226, 113, 158, 226, 113, 158, 226, 109, 155, 223, 109,
1030                155, 223, 109, 155, 223, 109, 155, 223,
1031            ]
1032        );
1033    }
1034
1035    #[test]
1036    #[allow(non_snake_case)]
1037    fn test_WebPDecodeYUV() {
1038        let (width, height, stride, uv_stride, buf) = WebPDecodeYUV(&lena()).unwrap();
1039        assert_eq!(width, 128);
1040        assert_eq!(height, 128);
1041        assert!(stride >= 128);
1042        assert!(uv_stride >= 64);
1043        assert_eq!(&buf.y()[..6], &[165, 165, 165, 165, 162, 162]);
1044        assert_eq!(&buf.u()[..6], &[98, 98, 98, 98, 98, 98]);
1045        assert_eq!(&buf.v()[..6], &[161, 161, 161, 161, 161, 161]);
1046    }
1047
1048    #[test]
1049    #[allow(non_snake_case)]
1050    fn test_WebPDecodeRGBAInto() {
1051        let mut buf = vec![0; 128 * 128 * 4];
1052        WebPDecodeRGBAInto(&lena(), &mut buf, 128 * 4).unwrap();
1053        assert_eq!(
1054            &buf[..24],
1055            &[
1056                226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113, 255,
1057                223, 155, 109, 255, 223, 155, 109, 255,
1058            ]
1059        );
1060    }
1061
1062    #[test]
1063    #[allow(non_snake_case)]
1064    fn test_WebPDecodeARGBInto() {
1065        let mut buf = vec![0; 128 * 128 * 4];
1066        WebPDecodeARGBInto(&lena(), &mut buf, 128 * 4).unwrap();
1067        assert_eq!(
1068            &buf[..24],
1069            &[
1070                255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113,
1071                255, 223, 155, 109, 255, 223, 155, 109,
1072            ]
1073        );
1074    }
1075
1076    #[test]
1077    #[allow(non_snake_case)]
1078    fn test_WebPDecodeBGRAInto() {
1079        let mut buf = vec![0; 128 * 128 * 4];
1080        WebPDecodeBGRAInto(&lena(), &mut buf, 128 * 4).unwrap();
1081        assert_eq!(
1082            &buf[..24],
1083            &[
1084                113, 158, 226, 255, 113, 158, 226, 255, 113, 158, 226, 255, 113, 158, 226, 255,
1085                109, 155, 223, 255, 109, 155, 223, 255,
1086            ]
1087        );
1088    }
1089
1090    #[test]
1091    #[allow(non_snake_case)]
1092    fn test_WebPDecodeRGBInto() {
1093        let mut buf = vec![0; 128 * 128 * 3];
1094        WebPDecodeRGBInto(&lena(), &mut buf, 128 * 3).unwrap();
1095        assert_eq!(
1096            &buf[..24],
1097            &[
1098                226, 158, 113, 226, 158, 113, 226, 158, 113, 226, 158, 113, 223, 155, 109, 223,
1099                155, 109, 223, 155, 109, 223, 155, 109,
1100            ]
1101        );
1102    }
1103
1104    #[test]
1105    #[allow(non_snake_case)]
1106    fn test_WebPDecodeBGRInto() {
1107        let mut buf = vec![0; 128 * 128 * 3];
1108        WebPDecodeBGRInto(&lena(), &mut buf, 128 * 3).unwrap();
1109        assert_eq!(
1110            &buf[..24],
1111            &[
1112                113, 158, 226, 113, 158, 226, 113, 158, 226, 113, 158, 226, 109, 155, 223, 109,
1113                155, 223, 109, 155, 223, 109, 155, 223
1114            ],
1115        );
1116    }
1117
1118    #[test]
1119    #[allow(non_snake_case)]
1120    fn test_WebPDecodeYUVInto() {
1121        let mut luma = vec![0; 128 * 128];
1122        let mut u = vec![0; 64 * 64];
1123        let mut v = vec![0; 64 * 64];
1124        WebPDecodeYUVInto(&lena(), &mut luma, 128, &mut u, 64, &mut v, 64).unwrap();
1125        assert_eq!(&luma[..6], &[165, 165, 165, 165, 162, 162]);
1126        assert_eq!(&u[..6], &[98, 98, 98, 98, 98, 98]);
1127        assert_eq!(&v[..6], &[161, 161, 161, 161, 161, 161]);
1128    }
1129
1130    #[allow(unused)]
1131    fn test_auto_traits() {
1132        fn is_send<T: Send>() {}
1133        fn is_sync<T: Sync>() {}
1134        fn is_unwind_safe<T: UnwindSafe>() {}
1135        fn is_ref_unwind_safe<T: RefUnwindSafe>() {}
1136
1137        is_send::<WebPIDecoder>();
1138        is_sync::<WebPIDecoder>();
1139        is_unwind_safe::<WebPIDecoder>();
1140        is_ref_unwind_safe::<WebPIDecoder>();
1141    }
1142
1143    #[test]
1144    #[allow(non_snake_case)]
1145    fn test_WebPINewDecoder() {
1146        let _idec = WebPINewDecoder();
1147    }
1148
1149    #[test]
1150    fn test_incr_argb() {
1151        let data = lena();
1152        let mut rng = rand::thread_rng();
1153        for _ in 0..50 {
1154            let mut idec = WebPINewRGB(WEBP_CSP_MODE::MODE_ARGB);
1155            let mut idx = 0;
1156            while idx < data.len() {
1157                // TODO: include 0 as write_len
1158                let write_len = std::cmp::min(rng.gen_range(1, 64), data.len() - idx);
1159                let result = WebPIAppend(&mut idec, &data[idx..idx + write_len]);
1160                idx += write_len;
1161                if result == VP8StatusCode::VP8_STATUS_OK {
1162                    break;
1163                } else if result == VP8StatusCode::VP8_STATUS_SUSPENDED {
1164                    if let Ok(result) = WebPIDecGetRGB(&mut idec) {
1165                        if result.last_y >= 1 {
1166                            assert_eq!(
1167                                &result.buf[..24],
1168                                &[
1169                                    255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113,
1170                                    255, 226, 158, 113, 255, 223, 155, 109, 255, 223, 155, 109,
1171                                ]
1172                            );
1173                        }
1174                    }
1175                } else {
1176                    panic!("Unexpected status: {:?}", result);
1177                }
1178            }
1179            let result = WebPIDecGetRGB(&mut idec).unwrap();
1180            assert_eq!(result.width, 128);
1181            assert_eq!(result.height, 128);
1182            assert_eq!(result.last_y, 128);
1183            assert_eq!(
1184                &result.buf[..24],
1185                &[
1186                    255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113, 255, 226, 158, 113,
1187                    255, 223, 155, 109, 255, 223, 155, 109,
1188                ]
1189            );
1190        }
1191    }
1192
1193    #[test]
1194    fn test_incr_yuva() {
1195        let data = lena();
1196        let mut rng = rand::thread_rng();
1197        for _ in 0..50 {
1198            let mut idec = WebPINewYUVA();
1199            let mut idx = 0;
1200            while idx < data.len() {
1201                // TODO: include 0 as write_len
1202                let write_len = std::cmp::min(rng.gen_range(1, 64), data.len() - idx);
1203                let result = WebPIAppend(&mut idec, &data[idx..idx + write_len]);
1204                idx += write_len;
1205                if result == VP8StatusCode::VP8_STATUS_OK {
1206                    break;
1207                } else if result == VP8StatusCode::VP8_STATUS_SUSPENDED {
1208                    if let Ok(result) = WebPIDecGetYUVA(&mut idec) {
1209                        if result.last_y >= 1 {
1210                            assert_eq!(&result.luma[..6], &[165, 165, 165, 165, 162, 162]);
1211                            assert_eq!(&result.u[..6], &[98, 98, 98, 98, 98, 98]);
1212                            assert_eq!(&result.v[..6], &[161, 161, 161, 161, 161, 161]);
1213                        }
1214                    }
1215                } else {
1216                    panic!("Unexpected status: {:?}", result);
1217                }
1218            }
1219            let result = WebPIDecGetYUVA(&mut idec).unwrap();
1220            assert_eq!(result.width, 128);
1221            assert_eq!(result.height, 128);
1222            assert_eq!(result.last_y, 128);
1223            assert_eq!(&result.luma[..6], &[165, 165, 165, 165, 162, 162]);
1224            assert_eq!(&result.u[..6], &[98, 98, 98, 98, 98, 98]);
1225            assert_eq!(&result.v[..6], &[161, 161, 161, 161, 161, 161]);
1226        }
1227    }
1228}