Merge pull request #12 from shssoichiro/bit-depth-reduction

Fix all bit depth and color reduction tests
This commit is contained in:
Josh Holmer 2016-03-02 12:42:12 -05:00
commit 73a049ab8d
83 changed files with 2889 additions and 227 deletions

View file

@ -1,5 +1,5 @@
use libz_sys;
use libc;
use libc::c_int;
pub fn inflate(data: &[u8]) -> Result<Vec<u8>, String> {
let mut input = data.to_owned();
@ -19,10 +19,10 @@ pub fn inflate(data: &[u8]) -> Result<Vec<u8>, String> {
pub fn deflate(data: &[u8], zc: u8, zm: u8, zs: u8, zw: u8) -> Result<Vec<u8>, String> {
let mut input = data.to_owned();
let mut stream = super::stream::Stream::new_compress(zc as libc::c_int,
zw as libc::c_int,
zm as libc::c_int,
zs as libc::c_int);
let mut stream = super::stream::Stream::new_compress(zc as c_int,
zw as c_int,
zm as c_int,
zs as c_int);
let mut output = Vec::with_capacity(data.len() / 20);
loop {
match stream.compress_vec(input.as_mut(), output.as_mut()) {

View file

@ -5,15 +5,10 @@ extern crate crossbeam;
extern crate libc;
extern crate libz_sys;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fs;
use std::fs::File;
use std::io;
use std::io::BufWriter;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::collections::{HashMap, HashSet};
use std::fs::{File, copy};
use std::io::{BufWriter, Write, stdout};
use std::path::{Path, PathBuf};
pub mod deflate {
pub mod deflate;
@ -79,40 +74,43 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
let mut something_changed = false;
if opts.color_type_reduction {
if png.reduce_color_type() {
something_changed = true;
};
}
if opts.bit_depth_reduction {
if png.reduce_bit_depth() {
something_changed = true;
if opts.verbosity == Some(1) {
report_reduction(&png);
}
};
}
if opts.color_type_reduction {
if png.reduce_color_type() {
something_changed = true;
if opts.verbosity == Some(1) {
report_reduction(&png);
}
};
}
if opts.palette_reduction {
if png.reduce_palette() {
something_changed = true;
if opts.verbosity == Some(1) {
report_reduction(&png);
}
};
}
if something_changed {
if let Some(palette) = png.palette.clone() {
println!("Reducing image to {} bits/pixel, {} colors in palette",
png.ihdr_data.bit_depth,
palette.len() / 3);
} else {
println!("Reducing image to {}x{} bits/pixel, {}",
png.channels_per_pixel(),
png.ihdr_data.bit_depth,
png.ihdr_data.color_type);
}
if something_changed && opts.verbosity.is_some() {
report_reduction(&png);
}
if let Some(interlacing) = opts.interlace {
if png.change_interlacing(interlacing) {
something_changed = true;
if opts.verbosity == Some(1) {
report_reduction(&png);
}
}
}
@ -159,7 +157,8 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
for result in results {
if let Ok(ok_result) = result.join() {
if (best.is_some() && ok_result.4.len() < best.clone().unwrap().4.len()) ||
if (best.is_some() &&
ok_result.4.len() < best.as_ref().map(|x| x.4.len()).unwrap()) ||
(best.is_none() &&
(ok_result.4.len() < png.idat_data.len() ||
(opts.interlace.is_some() &&
@ -197,12 +196,12 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
println!("Running in pretend mode, no output");
} else {
if opts.backup {
match fs::copy(in_file,
in_file.with_extension(format!("bak.{}",
in_file.extension()
.unwrap()
.to_str()
.unwrap()))) {
match copy(in_file,
in_file.with_extension(format!("bak.{}",
in_file.extension()
.unwrap()
.to_str()
.unwrap()))) {
Ok(x) => x,
Err(_) => {
return Err(format!("Unable to write to backup file at {}",
@ -212,7 +211,7 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
}
if opts.stdout {
let mut buffer = BufWriter::new(io::stdout());
let mut buffer = BufWriter::new(stdout());
match buffer.write_all(&output_data) {
Ok(_) => (),
Err(_) => return Err("Unable to write to stdout".to_owned()),
@ -258,3 +257,16 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> {
Ok(())
}
fn report_reduction(png: &png::PngData) {
if let Some(palette) = png.palette.clone() {
println!("Reducing image to {} bits/pixel, {} colors in palette",
png.ihdr_data.bit_depth,
palette.len() / 3);
} else {
println!("Reducing image to {}x{} bits/pixel, {}",
png.channels_per_pixel(),
png.ihdr_data.bit_depth,
png.ihdr_data.color_type);
}
}

View file

@ -90,28 +90,22 @@ impl BitDepth {
}
#[derive(Debug,Clone)]
struct ScanLines<'a> {
png: &'a PngData,
pub struct ScanLines<'a> {
pub png: &'a PngData,
start: usize,
end: usize,
}
impl<'a> ScanLines<'a> {
fn len(&self) -> usize {
self.png.raw_data.len()
}
}
impl<'a> Iterator for ScanLines<'a> {
type Item = ScanLine;
fn next(&mut self) -> Option<Self::Item> {
if self.end == self.len() {
if self.end == self.png.raw_data.len() {
None
} else {
let bits_per_line = self.png.ihdr_data.width as usize *
self.png.ihdr_data.bit_depth.as_u8() as usize *
self.png.channels_per_pixel() as usize;
// This avoids casting to and from floats, which is expensive
// Round up without converting to float
let bytes_per_line = (bits_per_line + bits_per_line % 8) >> 3;
self.start = self.end;
self.end = self.start + bytes_per_line + 1;
@ -124,9 +118,9 @@ impl<'a> Iterator for ScanLines<'a> {
}
#[derive(Debug,Clone)]
struct ScanLine {
filter: u8,
data: Vec<u8>,
pub struct ScanLine {
pub filter: u8,
pub data: Vec<u8>,
}
#[derive(Debug,Clone)]
@ -339,7 +333,7 @@ impl PngData {
output
}
fn scan_lines(&self) -> ScanLines {
pub fn scan_lines(&self) -> ScanLines {
ScanLines {
png: &self,
start: 0,
@ -348,8 +342,8 @@ impl PngData {
}
pub fn unfilter_image(&self) -> Vec<u8> {
let mut unfiltered = Vec::with_capacity(self.raw_data.len());
// This avoids casting to and from floats, which is expensive
let tmp = self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel();
// Round up without converting to float
let bpp = (tmp + tmp % 8) >> 3;
let mut last_line: Vec<u8> = vec![];
for line in self.scan_lines() {
@ -375,10 +369,10 @@ impl PngData {
2 => {
let mut data = Vec::with_capacity(line.data.len());
for (i, byte) in line.data.iter().enumerate() {
if !last_line.is_empty() {
data.push(byte.wrapping_add(last_line[i]));
} else {
if last_line.is_empty() {
data.push(*byte);
} else {
data.push(byte.wrapping_add(last_line[i]));
};
}
last_line = data.clone();
@ -387,7 +381,15 @@ impl PngData {
3 => {
let mut data = Vec::with_capacity(line.data.len());
for (i, byte) in line.data.iter().enumerate() {
if !last_line.is_empty() {
if last_line.is_empty() {
match i.checked_sub(bpp as usize) {
Some(x) => {
let b = data[x];
data.push(byte.wrapping_add(b >> 1))
}
None => data.push(*byte),
};
} else {
match i.checked_sub(bpp as usize) {
Some(x) => {
let b = data[x];
@ -397,14 +399,6 @@ impl PngData {
}
None => data.push(byte.wrapping_add(last_line[i] >> 1)),
};
} else {
match i.checked_sub(bpp as usize) {
Some(x) => {
let b = data[x];
data.push(byte.wrapping_add(b >> 1))
}
None => data.push(*byte),
};
};
}
last_line = data.clone();
@ -413,7 +407,15 @@ impl PngData {
4 => {
let mut data = Vec::with_capacity(line.data.len());
for (i, byte) in line.data.iter().enumerate() {
if !last_line.is_empty() {
if last_line.is_empty() {
match i.checked_sub(bpp as usize) {
Some(x) => {
let b = data[x];
data.push(byte.wrapping_add(b))
}
None => data.push(*byte),
};
} else {
match i.checked_sub(bpp as usize) {
Some(x) => {
let b = data[x];
@ -423,14 +425,6 @@ impl PngData {
}
None => data.push(byte.wrapping_add(last_line[i])),
};
} else {
match i.checked_sub(bpp as usize) {
Some(x) => {
let b = data[x];
data.push(byte.wrapping_add(b))
}
None => data.push(*byte),
};
};
}
last_line = data.clone();
@ -443,8 +437,8 @@ impl PngData {
}
pub fn filter_image(&self, filter: u8) -> Vec<u8> {
let mut filtered = Vec::with_capacity(self.raw_data.len());
// This avoids casting to and from floats, which is expensive
let tmp = self.ihdr_data.bit_depth.as_u8() * self.channels_per_pixel();
// Round up without converting to float
let bpp = (tmp + tmp % 8) >> 3;
let mut last_line: Vec<u8> = vec![];
// We could try a different filter method for each line
@ -468,33 +462,38 @@ impl PngData {
}
2 => {
for (i, byte) in line.data.iter().enumerate() {
if !last_line.is_empty() {
filtered.push(byte.wrapping_sub(last_line[i]));
} else {
if last_line.is_empty() {
filtered.push(*byte);
} else {
filtered.push(byte.wrapping_sub(last_line[i]));
};
}
}
3 => {
for (i, byte) in line.data.iter().enumerate() {
if !last_line.is_empty() {
if last_line.is_empty() {
filtered.push(match i.checked_sub(bpp as usize) {
Some(x) => byte.wrapping_sub(line.data[x] >> 1),
None => *byte,
});
} else {
filtered.push(match i.checked_sub(bpp as usize) {
Some(x) => byte.wrapping_sub(
((line.data[x] as u16 + last_line[i] as u16) >> 1) as u8
),
None => byte.wrapping_sub(last_line[i] >> 1),
});
} else {
filtered.push(match i.checked_sub(bpp as usize) {
Some(x) => byte.wrapping_sub(line.data[x] >> 1),
None => *byte,
});
};
}
}
4 => {
for (i, byte) in line.data.iter().enumerate() {
if !last_line.is_empty() {
if last_line.is_empty() {
filtered.push(match i.checked_sub(bpp as usize) {
Some(x) => byte.wrapping_sub(line.data[x]),
None => *byte,
});
} else {
filtered.push(match i.checked_sub(bpp as usize) {
Some(x) => {
byte.wrapping_sub(paeth_predictor(line.data[x],
@ -503,11 +502,6 @@ impl PngData {
}
None => byte.wrapping_sub(last_line[i]),
});
} else {
filtered.push(match i.checked_sub(bpp as usize) {
Some(x) => byte.wrapping_sub(line.data[x]),
None => *byte,
});
};
}
}
@ -525,7 +519,22 @@ impl PngData {
let mut line_3 = Vec::with_capacity(line.data.len());
let mut line_4 = Vec::with_capacity(line.data.len());
for (i, byte) in line.data.iter().enumerate() {
if !last_line.is_empty() {
if last_line.is_empty() {
match i.checked_sub(bpp as usize) {
Some(x) => {
line_1.push(byte.wrapping_sub(line.data[x]));
line_2.push(*byte);
line_3.push(byte.wrapping_sub(line.data[x] >> 1));
line_4.push(byte.wrapping_sub(line.data[x]));
}
None => {
line_1.push(*byte);
line_2.push(*byte);
line_3.push(*byte);
line_4.push(*byte);
}
}
} else {
match i.checked_sub(bpp as usize) {
Some(x) => {
line_1.push(byte.wrapping_sub(line.data[x]));
@ -544,21 +553,6 @@ impl PngData {
line_4.push(byte.wrapping_sub(last_line[i]));
}
}
} else {
match i.checked_sub(bpp as usize) {
Some(x) => {
line_1.push(byte.wrapping_sub(line.data[x]));
line_2.push(*byte);
line_3.push(byte.wrapping_sub(line.data[x] >> 1));
line_4.push(byte.wrapping_sub(line.data[x]));
}
None => {
line_1.push(*byte);
line_2.push(*byte);
line_3.push(*byte);
line_4.push(*byte);
}
}
};
}
@ -606,33 +600,42 @@ impl PngData {
if self.ihdr_data.color_type == ColorType::Indexed ||
self.ihdr_data.color_type == ColorType::Grayscale {
return match reduce_bit_depth_8_or_less(self) {
Some(_) => true,
Some((data, depth)) => {
self.raw_data = data;
self.ihdr_data.bit_depth = BitDepth::from_u8(depth);
true
}
None => false,
};
}
return false;
}
// It's difficult to estimate without knowing the number of ScanLines
// So we overallocate to prioritize speed over memory efficiency
let mut reduced = Vec::with_capacity(self.raw_data.len());
// Reduce from 16 to 8 bits per channel per pixel
let mut reduced =
Vec::with_capacity((self.ihdr_data.width * self.ihdr_data.height *
self.channels_per_pixel() as u32 +
self.ihdr_data.height) as usize);
let mut high_byte = 0;
for line in self.scan_lines() {
reduced.push(line.filter);
for (i, byte) in line.data.iter().enumerate() {
if i % 2 == 0 {
// High byte
if *byte != 0 {
high_byte = *byte;
} else {
// Low byte
if high_byte != *byte {
// Can't reduce, exit early
return false;
}
} else {
// Low byte
reduced.push(*byte);
}
}
}
self.ihdr_data.bit_depth = BitDepth::Eight;
self.raw_data = reduced;
true
}
@ -647,7 +650,6 @@ impl PngData {
// Go down one step at a time
// Maybe not the most efficient, but it's safe
if self.ihdr_data.color_type == ColorType::RGBA {
// Do this first, it's more likely to exit early
if let Some(data) = reduce_rgba_to_grayscale_alpha(self) {
self.raw_data = data;
self.ihdr_data.color_type = ColorType::GrayscaleAlpha;
@ -659,15 +661,33 @@ impl PngData {
} else if let Some((data, palette, trans)) = reduce_rgba_to_palette(self) {
self.raw_data = data;
self.palette = Some(palette);
self.transparency_palette = Some(trans);
if trans.iter().any(|x| *x != 255) {
self.transparency_palette = Some(trans);
} else {
self.transparency_palette = None;
}
self.ihdr_data.color_type = ColorType::Indexed;
changed = true;
should_reduce_bit_depth = true;
}
}
if self.ihdr_data.color_type == ColorType::GrayscaleAlpha {
if let Some(data) = reduce_grayscale_alpha_to_grayscale(self) {
self.raw_data = data;
self.ihdr_data.color_type = ColorType::Grayscale;
changed = true;
should_reduce_bit_depth = true;
}
}
if self.ihdr_data.color_type == ColorType::RGB {
if let Some((data, palette)) = reduce_rgb_to_palette(self) {
if let Some(data) = reduce_rgb_to_grayscale(self) {
self.raw_data = data;
self.ihdr_data.color_type = ColorType::Grayscale;
changed = true;
should_reduce_bit_depth = true;
} else if let Some((data, palette)) = reduce_rgb_to_palette(self) {
self.raw_data = data;
self.palette = Some(palette);
self.ihdr_data.color_type = ColorType::Indexed;
@ -676,19 +696,20 @@ impl PngData {
}
}
if self.ihdr_data.color_type == ColorType::Indexed && self.transparency_palette.is_none() {
if self.ihdr_data.color_type == ColorType::Indexed && self.transparency_palette.is_none() &&
self.palette.as_ref().map(|x| x.len()).unwrap() > 128 {
if let Some(data) = reduce_palette_to_grayscale(self) {
self.raw_data = data;
self.palette = None;
self.ihdr_data.color_type = ColorType::Grayscale;
changed = true;
should_reduce_bit_depth = false;
}
}
if self.ihdr_data.color_type == ColorType::GrayscaleAlpha {
if let Some(data) = reduce_grayscale_alpha_to_grayscale(self) {
} else if self.ihdr_data.color_type == ColorType::Grayscale {
if let Some((data, palette)) = reduce_grayscale_to_palette(self) {
self.raw_data = data;
self.ihdr_data.color_type = ColorType::Grayscale;
self.palette = Some(palette);
self.ihdr_data.color_type = ColorType::Indexed;
changed = true;
should_reduce_bit_depth = true;
}
@ -743,6 +764,10 @@ fn reduce_bit_depth_8_or_less(png: &PngData) -> Option<(Vec<u8>, u8)> {
reduced.push(bit);
}
}
// Pad end of line to get 8 bits per byte
while reduced.len() % 8 != 0 {
reduced.push(false);
}
}
Some((reduced.to_bytes(), allowed_bits as u8))
@ -820,23 +845,23 @@ fn reduce_rgba_to_palette(png: &PngData) -> Option<(Vec<u8>, Vec<u8>, Vec<u8>)>
}
let mut reduced = Vec::with_capacity(png.raw_data.len());
let mut palette = Vec::with_capacity(256);
let bpp: usize = 4;
let bpp: usize = (4 * png.ihdr_data.bit_depth.as_u8() as usize) >> 3;
for line in png.scan_lines() {
reduced.push(line.filter);
let mut cur_pixel = Vec::with_capacity(bpp);
for (i, byte) in line.data.iter().enumerate() {
cur_pixel.push(*byte);
if i % bpp == bpp - 1 {
if !palette.contains(&cur_pixel) {
if palette.contains(&cur_pixel) {
let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0;
reduced.push(idx as u8);
} else {
let len = palette.len();
if len == 256 {
return None;
}
palette.push(cur_pixel.clone());
reduced.push(len as u8);
} else {
let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0;
reduced.push(idx as u8);
}
cur_pixel.clear();
}
@ -864,23 +889,23 @@ fn reduce_rgb_to_palette(png: &PngData) -> Option<(Vec<u8>, Vec<u8>)> {
}
let mut reduced = Vec::with_capacity(png.raw_data.len());
let mut palette = Vec::with_capacity(256);
let bpp: usize = 3;
let bpp: usize = (3 * png.ihdr_data.bit_depth.as_u8() as usize) >> 3;
for line in png.scan_lines() {
reduced.push(line.filter);
let mut cur_pixel = Vec::with_capacity(bpp);
for (i, byte) in line.data.iter().enumerate() {
cur_pixel.push(*byte);
if i % bpp == bpp - 1 {
if !palette.contains(&cur_pixel) {
if palette.contains(&cur_pixel) {
let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0;
reduced.push(idx as u8);
} else {
let len = palette.len();
if len == 256 {
return None;
}
palette.push(cur_pixel.clone());
reduced.push(len as u8);
} else {
let idx = palette.iter().enumerate().find(|&x| x.1 == &cur_pixel).unwrap().0;
reduced.push(idx as u8);
}
cur_pixel.clear();
}
@ -895,6 +920,57 @@ fn reduce_rgb_to_palette(png: &PngData) -> Option<(Vec<u8>, Vec<u8>)> {
Some((reduced, color_palette))
}
fn reduce_grayscale_to_palette(png: &PngData) -> Option<(Vec<u8>, Vec<u8>)> {
if png.ihdr_data.bit_depth == BitDepth::Sixteen {
return None;
}
let mut reduced = BitVec::with_capacity(png.raw_data.len() * 8);
// Only perform reduction if we can get to 4-bits or less
let mut palette = Vec::with_capacity(16);
let bpp: usize = png.ihdr_data.bit_depth.as_u8() as usize;
for line in png.scan_lines() {
reduced.extend(BitVec::from_bytes(&[line.filter]));
let bit_vec = BitVec::from_bytes(&line.data);
let mut cur_pixel = BitVec::with_capacity(bpp);
for (i, bit) in bit_vec.iter().enumerate() {
cur_pixel.push(bit);
if i % bpp == bpp - 1 {
let pix_value = cur_pixel.to_bytes()[0] >> (8 - bpp);
let pix_slice = vec![pix_value, pix_value, pix_value];
if palette.contains(&pix_slice) {
let index = palette.iter().enumerate().find(|&x| x.1 == &pix_slice).unwrap().0;
let idx = BitVec::from_bytes(&[(index as u8) << (8 - bpp)]);
for b in idx.iter().take(bpp) {
reduced.push(b);
}
} else {
let len = palette.len();
if len == 16 {
return None;
}
palette.push(pix_slice.clone());
let idx = BitVec::from_bytes(&[(len as u8) << (8 - bpp)]);
for b in idx.iter().take(bpp) {
reduced.push(b);
}
}
cur_pixel = BitVec::with_capacity(bpp);
}
}
// Pad end of line to get 8 bits per byte
while reduced.len() % 8 != 0 {
reduced.push(false);
}
}
let mut color_palette = Vec::with_capacity(palette.len() * 3);
for color in &palette {
color_palette.extend_from_slice(&color);
}
Some((reduced.to_bytes(), color_palette))
}
fn reduce_palette_to_grayscale(png: &PngData) -> Option<Vec<u8>> {
let mut reduced = BitVec::with_capacity(png.raw_data.len() * 8);
let mut cur_pixel = Vec::with_capacity(3);
@ -923,17 +999,66 @@ fn reduce_palette_to_grayscale(png: &PngData) -> Option<Vec<u8>> {
// At the end of each pixel, push its grayscale value onto the reduced image
cur_pixel.push(bit);
if cur_pixel.len() == bit_depth {
let palette_idx: usize = cur_pixel.to_bytes()[0] as usize * 3;
// `to_bytes` gives us e.g. 10000000 for a 1-bit pixel, when we would want 00000001
let padded_pixel = cur_pixel.to_bytes()[0] >> (8 - bit_depth);
let palette_idx: usize = padded_pixel as usize * 3;
reduced.extend(BitVec::from_bytes(&[palette[palette_idx]]));
// BitVec's clear function doesn't set len to 0
cur_pixel = BitVec::with_capacity(bit_depth);
}
}
// Pad end of line to get 8 bits per byte
while reduced.len() % 8 != 0 {
reduced.push(false);
}
}
Some(reduced.to_bytes())
}
fn reduce_rgb_to_grayscale(png: &PngData) -> Option<Vec<u8>> {
let mut reduced = Vec::with_capacity(png.raw_data.len());
let byte_depth = png.ihdr_data.bit_depth.as_u8() >> 3;
let bpp: usize = 3 * byte_depth as usize;
let mut cur_pixel = Vec::with_capacity(bpp);
for line in png.scan_lines() {
reduced.push(line.filter);
for (i, byte) in line.data.iter().enumerate() {
cur_pixel.push(*byte);
if i % bpp == bpp - 1 {
if bpp == 3 {
cur_pixel.sort();
cur_pixel.dedup();
if cur_pixel.len() > 1 {
return None;
}
reduced.push(cur_pixel[0]);
} else {
let mut pixel_zip = cur_pixel.iter()
.enumerate()
.filter(|&(i, _)| i % 2 == 0)
.map(|(_, x)| *x)
.zip(cur_pixel.iter()
.enumerate()
.filter(|&(i, _)| i % 2 == 1)
.map(|(_, x)| *x))
.collect::<Vec<(u8, u8)>>();
pixel_zip.sort();
pixel_zip.dedup();
if pixel_zip.len() > 1 {
return None;
}
reduced.push(pixel_zip[0].0);
reduced.push(pixel_zip[0].1);
}
cur_pixel.clear();
}
}
}
Some(reduced)
}
fn reduce_grayscale_alpha_to_grayscale(png: &PngData) -> Option<Vec<u8>> {
let mut reduced = Vec::with_capacity(png.raw_data.len());
let byte_depth = png.ihdr_data.bit_depth.as_u8() >> 3;

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

View file

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View file

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View file

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View file

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View file

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 127 KiB

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because it is too large Load diff