• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Wuffs Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //    https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // ----------------
16 
17 // This program exercises the Rust GIF decoder at
18 // https://github.com/Geal/gif.rs
19 //
20 // Wuffs' C code doesn't depend on Rust or Nom (a parser combinator library
21 // written in Rust) per se, but this program gives some performance data for
22 // specific Rust GIF implementations. The equivalent Wuffs benchmarks (on the
23 // same test image) are run via:
24 //
25 // wuffs bench -mimic -focus=wuffs_gif_decode_1000k,mimic_gif_decode_1000k std/gif
26 //
27 // The "1000k" is because the test image (harvesters.gif) has approximately 1
28 // million pixels.
29 //
30 // The Wuffs benchmark reports megabytes per second. This program reports
31 // megapixels per second. The two concepts should be equivalent, since GIF
32 // images' pixel data are always 1 byte per pixel indices into a color palette.
33 // However, the gif.rs library only lets you decode 3 bytes per pixel (RGB),
34 // not 1 byte per pixel. This automatic palette look-up is arguably a defect in
35 // its API: decoding a 1,000 × 1,000 pixel image requires (2,000,000 - 768)
36 // more bytes this way, per frame, and animated GIFs have multiple frames.
37 //
38 // To run this program, do "cargo run --release > /dev/null" from the parent
39 // directory (the directory containing the Cargo.toml file). See the eprint
40 // comment below for why we redirect to /dev/null.
41 
42 // TODO: unify this program, bench-rust-gif-dot-rs, with bench-rust-gif, the
43 // other Rust GIF benchmark program. They are two separate programs because
44 // both libraries want to be named "gif", and the cargo package manager cannot
45 // handle duplicate names (https://github.com/rust-lang/cargo/issues/1311).
46 
47 extern crate gif;
48 
49 use std::time::Instant;
50 
51 // These constants are hard-coded to the harvesters.gif test image. As of
52 // 2018-01-27, the https://github.com/Geal/gif.rs library's src/lib.rs' Decoder
53 // implementation is incomplete, and doesn't expose enough API to calculate
54 // these from the source GIF image.
55 const WIDTH: usize = 1165;
56 const HEIGHT: usize = 859;
57 const BYTES_PER_PIXEL: usize = 3; // Red, green, blue.
58 const NUM_BYTES: usize = WIDTH * HEIGHT * BYTES_PER_PIXEL;
59 const COLOR_TABLE_ELEMENT_COUNT: u16 = 256;
60 const COLOR_TABLE_OFFSET: usize = 13;
61 const GRAPHIC_BLOCK_OFFSET: usize = 782;
62 const FIRST_PIXEL: u8 = 1; // Top left pixel's red component is 0x01.
63 const LAST_PIXEL: u8 = 3; // Bottom right pixel's blue component is 0x03.
64 
main()65 fn main() {
66     let mut dst = [0; NUM_BYTES];
67     let src = include_bytes!("../../../test/data/harvesters.gif");
68 
69     let start = Instant::now();
70 
71     const REPS: u32 = 50;
72     for _ in 0..REPS {
73         decode(&mut dst[..], src);
74     }
75 
76     let elapsed = start.elapsed();
77     let elapsed_nanos = elapsed.as_secs() * 1_000_000_000 + (elapsed.subsec_nanos() as u64);
78 
79     let total_pixels: u64 = ((WIDTH * HEIGHT) as u64) * (REPS as u64);
80     let kp_per_s: u64 = total_pixels * 1_000_000 / elapsed_nanos;
81 
82     // Use eprint instead of print because the https://github.com/Geal/gif.rs
83     // library can print its own debugging messages (this is filed as
84     // https://github.com/Geal/gif.rs/issues/4). When running this program, it
85     // can be useful to redirect stdout (but not stderr) to /dev/null.
86     eprint!(
87         "gif.rs  {:3}.{:03} megapixels/second  github.com/Geal/gif.rs\n",
88         kp_per_s / 1_000,
89         kp_per_s % 1_000
90     );
91 }
92 
decode(dst: &mut [u8], src: &[u8])93 fn decode(dst: &mut [u8], src: &[u8]) {
94     // Set up the hard-coded sanity check, executed below.
95     dst[0] = 0xFE;
96     dst[NUM_BYTES - 1] = 0xFE;
97 
98     let (_, colors) =
99         gif::parser::color_table(&src[COLOR_TABLE_OFFSET..], COLOR_TABLE_ELEMENT_COUNT).unwrap();
100 
101     let (_, block) = gif::parser::graphic_block(&src[GRAPHIC_BLOCK_OFFSET..]).unwrap();
102 
103     let rendering = match block {
104         gif::parser::Block::GraphicBlock(_, x) => x,
105         _ => panic!("not a graphic block"),
106     };
107 
108     let (code_size, blocks) = match rendering {
109         gif::parser::GraphicRenderingBlock::TableBasedImage(_, x, y) => (x, y),
110         _ => panic!("not a table based image"),
111     };
112 
113     let num_bytes = gif::lzw::decode_lzw(&colors, code_size as usize, blocks, dst).unwrap();
114 
115     if num_bytes != NUM_BYTES {
116         panic!("wrong num_bytes")
117     }
118 
119     // A hard-coded sanity check that we decoded the pixel data correctly.
120     if (dst[0] != FIRST_PIXEL) || (dst[NUM_BYTES - 1] != LAST_PIXEL) {
121         panic!("wrong dst pixels")
122     }
123 }
124