Compare commits
10 commits
5fc6ce7636
...
16d4306763
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16d4306763 | ||
|
|
f40db8a474 | ||
|
|
0c39058288 | ||
|
|
19e74b6dcb | ||
|
|
26744cd261 | ||
|
|
0c3ae9aa5b | ||
|
|
6a09094d54 | ||
|
|
42f2ccffac | ||
|
|
fea1db9377 | ||
|
|
40ebe619f5 |
3
.gitignore
vendored
|
|
@ -2,3 +2,6 @@ node_modules/
|
||||||
target/
|
target/
|
||||||
pkg/
|
pkg/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
dist/
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
18
README.md
|
|
@ -1,7 +1,17 @@
|
||||||
**Spiegel** image filter project (Work In Progress)
|
**Spiegel** image filter project
|
||||||
|
|
||||||
- rust
|
- rust/webassembly for image processing
|
||||||
- wasm
|
- vanilla javascript for the user interface
|
||||||
|
- no server (just static pages)
|
||||||
|
|
||||||
|
Live demo at https://shautvast.github.io/spiegel-demo/
|
||||||
|
|
||||||
|
* Sorry for the poor performance, especially on larger images.
|
||||||
|
* It uses the median image filter from image.rs. That in itself can be pretty slow.
|
||||||
|
(Although the implementation uses a _sliding window histogram_, which I think is pretty nifty).
|
||||||
|
* And on top of that, I created this custom flood fill algorithm,
|
||||||
|
that instead of filling it with with a single color, looks up a sample from the
|
||||||
|
Spiegel book (that has a corresponding color) and takes the pixels from that.
|
||||||
|
|
||||||
sample output
|
sample output
|
||||||

|

|
||||||
|
|
|
||||||
BIN
dist/11dc9380193954aac8c4.module.wasm
vendored
BIN
dist/630027c604733df47374e4f5adb1b7c5.jpg
vendored
|
Before Width: | Height: | Size: 349 KiB |
BIN
dist/66d531fb4651c57454ae.module.wasm
vendored
1
dist/796.bundle.js
vendored
BIN
dist/93be161a4640fe100e05af248fa4c52e.png
vendored
|
Before Width: | Height: | Size: 2.8 KiB |
BIN
dist/95cc0985b7bd6242de9b9665261616a3.jpg
vendored
|
Before Width: | Height: | Size: 192 KiB |
523
dist/bundle.js
vendored
42
dist/crate_pkg_photon_rs_js.bundle.js
vendored
42
dist/crate_pkg_spiegel_client_js.bundle.js
vendored
BIN
dist/df3fbe72e3ad368ae5bf.module.wasm
vendored
BIN
dist/ea872833a419652a23b47d50be7fe0ef.jpg
vendored
|
Before Width: | Height: | Size: 224 KiB |
BIN
dist/f49f7893ad0a3ad10176.module.wasm
vendored
29
dist/index.html
vendored
|
|
@ -1,29 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
|
||||||
<title>Spiegel</title>
|
|
||||||
<script defer src="bundle.js"></script></head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="default">
|
|
||||||
<div class="main">
|
|
||||||
<label>
|
|
||||||
<h2>Blur factor</h2< /label>
|
|
||||||
<div class="slidecontainer">
|
|
||||||
<input type="range" id="slider" value="0" min="0" max="100" class="slider" />
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
<div class="main_content">
|
|
||||||
<div class="content" id="images">
|
|
||||||
<div id="image_container"></div>
|
|
||||||
|
|
||||||
<canvas id="canvas"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
802
webclient/Cargo.lock → image-processor/Cargo.lock
generated
|
|
@ -1,19 +1,21 @@
|
||||||
[package]
|
[package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "spiegel-client"
|
name = "spiegel"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
image = "0.24.1"
|
image = "0.25"
|
||||||
imageproc = "0.23"
|
imageproc = "0.24"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
photon-rs = { version = "0.3.2", default-features = false }
|
photon-rs = { version = "0.3.2", default-features = false }
|
||||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||||
wasm-bindgen = "0.2.78"
|
wasm-bindgen = "0.2.78"
|
||||||
|
include_dir = "0.7.3"
|
||||||
|
web-sys = "0.3.55"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
Before Width: | Height: | Size: 6.1 MiB After Width: | Height: | Size: 6.1 MiB |
124
image-processor/src/lib.rs
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
use image::{GenericImage, GenericImageView, Pixel, Rgba, RgbaImage};
|
||||||
|
use photon_rs::PhotonImage;
|
||||||
|
use std::collections::LinkedList;
|
||||||
|
use image::imageops::FilterType;
|
||||||
|
|
||||||
|
mod samples;
|
||||||
|
|
||||||
|
use samples::log;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
static BLACK: Rgba<u8> = Rgba([0, 0, 0, 0]);
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn spiegel(photon_image: PhotonImage, median_kernelsize: u32, preview: bool) -> PhotonImage {
|
||||||
|
samples::read_jpeg_bytes();
|
||||||
|
|
||||||
|
let width = photon_image.get_width();
|
||||||
|
let height = photon_image.get_height();
|
||||||
|
|
||||||
|
let raw_pixels = photon_image.get_raw_pixels().to_vec();
|
||||||
|
let i1 = RgbaImage::from_vec(width, height, raw_pixels).unwrap();
|
||||||
|
|
||||||
|
let i2 = if preview {
|
||||||
|
image::imageops::resize(&i1, u32::min(500, width >> 1), u32::min(500, height >> 1), FilterType::Nearest)
|
||||||
|
} else {
|
||||||
|
image::imageops::resize(&i1, u32::min(500, width), u32::min(500, height), FilterType::Nearest)
|
||||||
|
};
|
||||||
|
let mut i3 = imageproc::filter::median_filter(&i2, median_kernelsize, median_kernelsize);
|
||||||
|
let i4 = if !preview {
|
||||||
|
apply_samples_to_image(&mut i3)
|
||||||
|
} else {
|
||||||
|
i3
|
||||||
|
};
|
||||||
|
let i5 = image::imageops::resize(&i4, width, height, FilterType::Nearest);
|
||||||
|
PhotonImage::new(i5.into_raw(), width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_samples_to_image(src: &mut RgbaImage) -> RgbaImage {
|
||||||
|
let mut out = RgbaImage::new(src.width(), src.height());
|
||||||
|
unsafe {
|
||||||
|
for y in 0..src.height() {
|
||||||
|
log(&format!("{}", y));
|
||||||
|
for x in 0..src.width() {
|
||||||
|
if out.unsafe_get_pixel(x, y) == BLACK {
|
||||||
|
let pixel = src.unsafe_get_pixel(x, y);
|
||||||
|
if pixel != BLACK {
|
||||||
|
let sample = samples::get_closest_color(pixel[0], pixel[1], pixel[2])
|
||||||
|
.image
|
||||||
|
.as_ref()
|
||||||
|
.unwrap();
|
||||||
|
fill(src, sample, &mut out, pixel, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill(
|
||||||
|
src: &mut RgbaImage,
|
||||||
|
sample: &RgbaImage,
|
||||||
|
dest: &mut RgbaImage,
|
||||||
|
color: Rgba<u8>,
|
||||||
|
px: u32,
|
||||||
|
py: u32,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
let height = sample.height();
|
||||||
|
let width = sample.width();
|
||||||
|
let mut points = LinkedList::new();
|
||||||
|
if is_same(src.unsafe_get_pixel(px, py), color) {
|
||||||
|
points.push_back(Coord(px, py));
|
||||||
|
}
|
||||||
|
|
||||||
|
while !points.is_empty() {
|
||||||
|
if let Some(coord) = points.pop_back() {
|
||||||
|
let orig_pixel = src.unsafe_get_pixel(coord.0, coord.1);
|
||||||
|
let x = coord.0;
|
||||||
|
let y = coord.1;
|
||||||
|
if src.unsafe_get_pixel(x, y) != BLACK {
|
||||||
|
if is_same(orig_pixel, color) {
|
||||||
|
let mut xx = x;
|
||||||
|
let mut yy = y;
|
||||||
|
while xx >= width {
|
||||||
|
xx -= width;
|
||||||
|
}
|
||||||
|
while yy >= height {
|
||||||
|
yy -= height;
|
||||||
|
}
|
||||||
|
dest.unsafe_put_pixel(x, y, sample.unsafe_get_pixel(xx, yy));
|
||||||
|
src.unsafe_put_pixel(x, y, BLACK);
|
||||||
|
if x > 1 {
|
||||||
|
points.push_front(Coord(x - 1, y));
|
||||||
|
}
|
||||||
|
if y > 1 {
|
||||||
|
points.push_front(Coord(x, y - 1));
|
||||||
|
}
|
||||||
|
if x < src.width() - 1 {
|
||||||
|
points.push_front(Coord(x + 1, y));
|
||||||
|
}
|
||||||
|
if y < src.height() - 1 {
|
||||||
|
points.push_front(Coord(x, y + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("break");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_same(p1: Rgba<u8>, p2: Rgba<u8>) -> bool {
|
||||||
|
let p1 = p1.channels();
|
||||||
|
let p2 = p2.channels();
|
||||||
|
i16::abs(p1[0] as i16 - p2[0] as i16) < 4
|
||||||
|
&& i16::abs(p1[1] as i16 - p2[1] as i16) < 4
|
||||||
|
&& i16::abs(p1[2] as i16 - p2[2] as i16) < 4
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Coord(u32, u32);
|
||||||
99
image-processor/src/samples.rs
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
use image::{load_from_memory_with_format, RgbaImage};
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
use include_dir::{include_dir, Dir, DirEntry};
|
||||||
|
|
||||||
|
static mut SAMPLES: OnceLock<Vec<ColorSample>> = OnceLock::new();
|
||||||
|
static SAMPLES_DIR: Dir = include_dir!("src/samples");
|
||||||
|
|
||||||
|
pub fn read_jpeg_bytes() {
|
||||||
|
unsafe {
|
||||||
|
SAMPLES.get_or_init(|| {
|
||||||
|
log("reading image samples");
|
||||||
|
read_color_samples().unwrap()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_closest_color<'a>(r: u8, g: u8, b: u8) -> &'static ColorSample {
|
||||||
|
unsafe {
|
||||||
|
let color_samples = SAMPLES.get_mut().unwrap();
|
||||||
|
|
||||||
|
let mut closest = None;
|
||||||
|
let mut min_diff: f32 = 4294967295.0; //0xFFFFFFFF
|
||||||
|
for sample in color_samples {
|
||||||
|
let diff = get_distance(sample.r, sample.g, sample.b, r, g, b);
|
||||||
|
if diff < min_diff {
|
||||||
|
closest = Some(sample);
|
||||||
|
min_diff = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let closest = closest.unwrap();
|
||||||
|
if closest.image.is_none() {
|
||||||
|
let sample_image =
|
||||||
|
load_from_memory_with_format(closest.raw_bytes, image::ImageFormat::Jpeg)
|
||||||
|
.unwrap()
|
||||||
|
.to_rgba8();
|
||||||
|
closest.image = Some(sample_image);
|
||||||
|
}
|
||||||
|
closest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns squared euclidian color distance
|
||||||
|
/// as if colors were points in 3d space
|
||||||
|
fn get_distance(r1: u8, g1: u8, b1: u8, r2: u8, g2: u8, b2: u8) -> f32 {
|
||||||
|
let red_dif = r1 as f32 - r2 as f32;
|
||||||
|
let green_dif = g1 as f32 - g2 as f32;
|
||||||
|
let blue_dif = b1 as f32 - b2 as f32;
|
||||||
|
return red_dif * red_dif + green_dif * green_dif + blue_dif * blue_dif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read all sample jpegs into memory
|
||||||
|
pub fn read_color_samples() -> anyhow::Result<Vec<ColorSample>> {
|
||||||
|
let mut color_samples: Vec<ColorSample> = Vec::new();
|
||||||
|
|
||||||
|
for entry in SAMPLES_DIR.entries() {
|
||||||
|
if let DirEntry::File(f) = entry {
|
||||||
|
let filename = entry.path().file_name().unwrap().to_str().unwrap();
|
||||||
|
let hex_r = &filename[0..2];
|
||||||
|
let hex_g = &filename[2..4];
|
||||||
|
let hex_b = &filename[4..6];
|
||||||
|
|
||||||
|
color_samples.push(ColorSample {
|
||||||
|
filename: filename.into(),
|
||||||
|
r: u8::from_str_radix(&hex_r, 16)?,
|
||||||
|
g: u8::from_str_radix(&hex_g, 16)?,
|
||||||
|
b: u8::from_str_radix(&hex_b, 16)?,
|
||||||
|
raw_bytes: f.contents(),
|
||||||
|
image: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("Done reading image samples");
|
||||||
|
Ok(color_samples)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ColorSample {
|
||||||
|
pub filename: String,
|
||||||
|
pub r: u8,
|
||||||
|
pub g: u8,
|
||||||
|
pub b: u8,
|
||||||
|
pub raw_bytes: &'static [u8],
|
||||||
|
pub image: Option<RgbaImage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
pub fn log(s: &str);
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||||
|
fn log_u32(a: u32);
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||||
|
fn log_many(a: &str, b: &str);
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |