working version
44
index.html
|
|
@ -4,27 +4,57 @@
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
|
||||||
<title>Spiegel</title>
|
<title>Spiegel</title>
|
||||||
|
<link rel="stylesheet" href="/css/styles.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<h1>Klaer Lightende Spiegel der Verfkonst</h1>
|
||||||
|
<h4>Paints your image in the colors of the
|
||||||
|
<a class="link" target="_blank"
|
||||||
|
href="https://nl.wikipedia.org/wiki/Klaer_Lightende_Spiegel_der_Verfkonst">handpainted
|
||||||
|
book by A. Boogert</a>,
|
||||||
|
<img id="spieghel" alt="spieghel" src="" class="hide">
|
||||||
|
the 1692 version of the Pantone color book.</h4>
|
||||||
|
|
||||||
<div class="main">
|
<div class="main">
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<h2>Blur factor</h2>
|
<h4>Upload or drag&drop a picture and move the slider, for a quick preview</h4>
|
||||||
<h4 id="msg">(blurring can take up to 15 seconds, please be patient)</h4>
|
<h4>Then press Apply</h4>
|
||||||
|
|
||||||
</label>
|
</label>
|
||||||
<div class="slidecontainer">
|
<div><input id="upload" type="file" accept="image/jpeg"></div>
|
||||||
<input type="range" id="slider" value="0" min="0" max="100" class="slider" />
|
<div id="slidecontainer" class="hide">
|
||||||
|
<label for="slider">Brush stroke size</label><input type="range" id="slider" value="0" min="0" max="100" class="slider"/>
|
||||||
|
<button id="apply">Apply</button>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<div class="main_content">
|
<div class="main-content">
|
||||||
<div id="progress"></div>
|
|
||||||
<div class="content" id="images">
|
<div class="content" id="images">
|
||||||
<div id="image_container"></div>
|
<div id="image-container" class="border" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
|
||||||
|
|
||||||
<canvas id="canvas" style="visibility: hidden;"></canvas>
|
<canvas id="canvas" style="visibility: hidden;"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<h4>(Painting can take long, be patient</h4>
|
||||||
|
<h4>all image processing is done in your browser)</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
function allowDrop(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drop(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let dt = event.dataTransfer;
|
||||||
|
|
||||||
|
console.log(dt.files);
|
||||||
|
|
||||||
|
document.querySelector("#source-image").src = URL.createObjectURL(dt.files[0]);
|
||||||
|
document.querySelector('#image-container').setAttribute("class", "no-border");
|
||||||
|
document.querySelector("#slidecontainer").setAttribute("class", "slidecontainer");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,45 @@
|
||||||
body {
|
body {
|
||||||
background-color: #222;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas {
|
canvas {
|
||||||
width: 50vw;
|
width: 50vw;
|
||||||
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
margin-left: 25%;
|
margin-left: 25%;
|
||||||
padding: 0px 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border {
|
||||||
|
border: 5px dashed black;
|
||||||
|
width: 50vw;
|
||||||
|
height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-border {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body h4, body h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div h4 {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
label,
|
||||||
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5 {
|
h5 {
|
||||||
color: white;
|
color: black;
|
||||||
font-family: "Roboto", sans-serif;
|
font-family: "Roboto", sans-serif;
|
||||||
font-weight: 300;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
|
|
@ -26,36 +48,45 @@ h4 {
|
||||||
letter-spacing: 4px;
|
letter-spacing: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link:hover + .hide {
|
||||||
|
display: flow;
|
||||||
|
position: absolute;
|
||||||
|
top: 10vh;
|
||||||
|
left: 20vw;
|
||||||
|
}
|
||||||
|
|
||||||
li:hover {
|
li:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topnav {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slidecontainer {
|
.slidecontainer {
|
||||||
width: 30%;
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
margin-left: 10vw;
|
|
||||||
width: 30vw;
|
width: 30vw;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
background: #d3d3d3;
|
background: #f3f3f3;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider:hover {
|
.slider:hover {
|
||||||
opacity: 0.7;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider::-moz-range-thumb {
|
.slider::-moz-range-thumb {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
background: #04aa6d;
|
background: #777;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
src/img/spieghel.jpg
Normal file
|
After Width: | Height: | Size: 632 KiB |
BIN
src/js/boy.jpg
|
Before Width: | Height: | Size: 5.9 MiB |
103
src/js/index.js
|
|
@ -1,51 +1,92 @@
|
||||||
import MainImage from "./nine_yards.jpg";
|
|
||||||
import "../css/styles.css";
|
import "../css/styles.css";
|
||||||
|
import spiegel from '../img/spieghel.jpg';
|
||||||
|
|
||||||
let canvas, ctx, blur_factor;
|
let canvas, ctx, originalWidth, originalHeight, canvasTop, strokeSize=1;
|
||||||
|
|
||||||
import("../../webclient/pkg").then((module) => {
|
import("../../webclient/pkg").then((module) => {
|
||||||
// Setup images
|
const slider = document.querySelector("#slider");
|
||||||
const sourceImage = new Image();
|
slider.onchange = (event) => {
|
||||||
sourceImage.src = MainImage;
|
strokeSize = parseInt(event.target.value) / 5;
|
||||||
sourceImage.onload = () => {
|
filterImage(event, true);
|
||||||
setUpCanvas();
|
};
|
||||||
|
slider.value = 0;
|
||||||
|
const applyButton = document.querySelector("#apply");
|
||||||
|
|
||||||
|
applyButton.onclick = (event) => {
|
||||||
|
filterImage(event, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const slider = document.getElementById("slider");
|
function filterImage(event, preview) {
|
||||||
const progress = document.getElementById("progress");
|
|
||||||
slider.onchange = filterImage;
|
|
||||||
slider.value = 0;
|
|
||||||
|
|
||||||
function filterImage(event) {
|
|
||||||
ctx.drawImage(sourceImage, 0, 0);
|
ctx.drawImage(sourceImage, 0, 0);
|
||||||
let sliderValue = parseInt(event.target.value);
|
|
||||||
blur_factor = sliderValue / 5;
|
|
||||||
let rust_image = module.open_image(canvas, ctx);
|
let rust_image = module.open_image(canvas, ctx);
|
||||||
|
const out= module.spiegel(rust_image, strokeSize, preview);
|
||||||
|
|
||||||
// module.gaussian_blur(rust_image, blur_factor);
|
module.putImageData(canvas, ctx, out);
|
||||||
// module.median(rust_image, blur_factor, blur_factor);
|
|
||||||
|
|
||||||
module.spiegel(rust_image, blur_factor);
|
|
||||||
module.putImageData(canvas, ctx, rust_image);
|
|
||||||
const image_container = document.getElementById("image_container");
|
|
||||||
let rect = image_container.getBoundingClientRect();
|
|
||||||
canvas.setAttribute(
|
canvas.setAttribute(
|
||||||
"style",
|
"style",
|
||||||
`visibility:visible;position:absolute;top:${rect.top};z-index:100`,
|
`visibility:visible;position:absolute;top:${canvasTop}px`,
|
||||||
);
|
);
|
||||||
// image_container.setAttribute("style", "visibility:hidden");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
document.querySelector("#spieghel").src = spiegel;
|
||||||
|
const sourceImage = new Image();
|
||||||
|
sourceImage.id = "source-image";
|
||||||
|
let element = document.querySelector("#image-container");
|
||||||
|
element.appendChild(sourceImage);
|
||||||
|
sourceImage.onload = () => {
|
||||||
|
setUpCanvas()
|
||||||
|
};
|
||||||
|
|
||||||
function setUpCanvas() {
|
function setUpCanvas() {
|
||||||
let element = document.getElementById("image_container");
|
canvas = document.querySelector("#canvas");
|
||||||
element.appendChild(sourceImage);
|
originalWidth = sourceImage.width;
|
||||||
|
originalHeight = sourceImage.height;
|
||||||
|
|
||||||
canvas = document.getElementById("canvas");
|
canvas.width = originalWidth;
|
||||||
canvas.width = sourceImage.width;
|
canvas.height = originalHeight;
|
||||||
canvas.height = sourceImage.height;
|
|
||||||
sourceImage.setAttribute("style", "width:50vw");
|
sourceImage.setAttribute("style", "width:50vw");
|
||||||
|
const imageContainer = document.querySelector("#image-container");
|
||||||
|
const rect = imageContainer.getBoundingClientRect();
|
||||||
|
canvasTop = rect.top;
|
||||||
ctx = canvas.getContext("2d");
|
ctx = canvas.getContext("2d");
|
||||||
ctx.drawImage(sourceImage, 0, 0);
|
}
|
||||||
|
|
||||||
|
document.querySelector('#upload').addEventListener('change', function (e) {
|
||||||
|
let file = e.target.files[0];
|
||||||
|
if (file.type.match('image.*')) {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = function (e) {
|
||||||
|
const image = document.querySelector('#source-image');
|
||||||
|
image.src = reader.result;
|
||||||
|
document.querySelector("#image-container").setAttribute("class", "no-border");
|
||||||
|
document.querySelector("#upload").setAttribute("class", "hide");
|
||||||
|
document.querySelector("#slidecontainer").setAttribute("class", "slidecontainer");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Uploaded file is not an image. Please upload an image file.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function allowDrop(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drag(ev) {
|
||||||
|
ev.dataTransfer.setData("text", ev.target.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drop(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
let dt = event.dataTransfer;
|
||||||
|
|
||||||
|
console.log(dt.files);
|
||||||
|
|
||||||
|
document.querySelector("#source-image").src = URL.createObjectURL(dt.files[0]);
|
||||||
|
document.querySelector('#image-container').setAttribute("class", "no-border");
|
||||||
|
document.querySelector("#upload").setAttribute("class", "hide");
|
||||||
|
document.querySelector("#slidecontainer").setAttribute("class", "show");
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 192 KiB |
778
webclient/Cargo.lock
generated
|
|
@ -7,14 +7,15 @@ version = "0.1.0"
|
||||||
|
|
||||||
[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"
|
include_dir = "0.7.3"
|
||||||
|
web-sys = "0.3.55"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,45 @@
|
||||||
use image::{GenericImage, GenericImageView, Pixel, Rgba, RgbaImage};
|
use image::{GenericImage, GenericImageView, Pixel, Rgba, RgbaImage};
|
||||||
use photon_rs::PhotonImage;
|
use photon_rs::PhotonImage;
|
||||||
use std::collections::LinkedList;
|
use std::collections::LinkedList;
|
||||||
mod quantizer;
|
use image::imageops::FilterType;
|
||||||
|
|
||||||
mod samples;
|
mod samples;
|
||||||
|
|
||||||
use samples::log;
|
use samples::log;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
static BLACK: Rgba<u8> = Rgba([0, 0, 0, 0]);
|
static BLACK: Rgba<u8> = Rgba([0, 0, 0, 0]);
|
||||||
|
|
||||||
/// Apply a median filter
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
/// * `img` - A PhotonImage.
|
|
||||||
/// * `x_radius` - x radius of median window
|
|
||||||
/// * `y_radius` - y radius of median window
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn median(photon_image: &mut PhotonImage, x_radius: u32, y_radius: u32) {
|
pub fn spiegel(photon_image: PhotonImage, median_kernelsize: u32, preview: bool) -> PhotonImage {
|
||||||
|
samples::read_jpeg_bytes();
|
||||||
|
|
||||||
let width = photon_image.get_width();
|
let width = photon_image.get_width();
|
||||||
let height = photon_image.get_height();
|
let height = photon_image.get_height();
|
||||||
|
|
||||||
if width == 0 || height == 0 {
|
let raw_pixels = photon_image.get_raw_pixels().to_vec();
|
||||||
return;
|
let i1 = RgbaImage::from_vec(width, height, raw_pixels).unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
let raw_pixels = photon_image.get_raw_pixels().to_vec(); //argh!, slice should work but doesn't
|
let i2 = if preview {
|
||||||
let rs_image = RgbaImage::from_vec(width, height, raw_pixels).unwrap();
|
image::imageops::resize(&i1, u32::min(500, width >> 1), u32::min(500, height >> 1), FilterType::Nearest)
|
||||||
|
} else {
|
||||||
let filtered: Vec<u8> =
|
image::imageops::resize(&i1, u32::min(500, width), u32::min(500, height), FilterType::Nearest)
|
||||||
imageproc::filter::median_filter(&rs_image, x_radius, y_radius).into_raw();
|
};
|
||||||
|
let mut i3 = imageproc::filter::median_filter(&i2, median_kernelsize, median_kernelsize);
|
||||||
*photon_image = PhotonImage::new(filtered, width, height);
|
let i4 = if !preview {
|
||||||
}
|
apply_samples_to_image(&mut i3)
|
||||||
|
} else {
|
||||||
#[wasm_bindgen]
|
i3
|
||||||
pub fn spiegel(photon_image: &mut PhotonImage, median_kernelsize: u32) {
|
};
|
||||||
let width = photon_image.get_width();
|
let i5 = image::imageops::resize(&i4, width, height, FilterType::Nearest);
|
||||||
let height = photon_image.get_height();
|
PhotonImage::new(i5.into_raw(), width, height)
|
||||||
|
|
||||||
if width == 0 || height == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
samples::init();
|
|
||||||
let raw_pixels = photon_image.get_raw_pixels().to_vec(); //argh!, slice should work but doesn't
|
|
||||||
let out = RgbaImage::from_vec(width, height, raw_pixels).unwrap();
|
|
||||||
log(&format!("stort"));
|
|
||||||
// let out = imageproc::filter::gaussian_blur_f32(&rs_image, 3.0);
|
|
||||||
// log(&format!("gaussian done"));
|
|
||||||
// let mut out = quantizer::quantize(&out, 256);
|
|
||||||
let mut out = imageproc::filter::median_filter(&out, median_kernelsize, median_kernelsize);
|
|
||||||
//
|
|
||||||
log(&format!("median done"));
|
|
||||||
let out = apply_samples_to_image(&mut out);
|
|
||||||
log(&format!("applying done"));
|
|
||||||
*photon_image = PhotonImage::new(out.into_raw(), width, height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_samples_to_image(src: &mut RgbaImage) -> RgbaImage {
|
fn apply_samples_to_image(src: &mut RgbaImage) -> RgbaImage {
|
||||||
let mut out = RgbaImage::new(src.width(), src.height());
|
let mut out = RgbaImage::new(src.width(), src.height());
|
||||||
unsafe {
|
unsafe {
|
||||||
for y in 0..src.height() {
|
for y in 0..src.height() {
|
||||||
|
log(&format!("{}", y));
|
||||||
for x in 0..src.width() {
|
for x in 0..src.width() {
|
||||||
if out.unsafe_get_pixel(x, y) == BLACK {
|
if out.unsafe_get_pixel(x, y) == BLACK {
|
||||||
let pixel = src.unsafe_get_pixel(x, y);
|
let pixel = src.unsafe_get_pixel(x, y);
|
||||||
|
|
|
||||||
|
|
@ -1,348 +0,0 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
use image::{Pixel, Rgba, RgbaImage};
|
|
||||||
|
|
||||||
const MAX_LEVEL: usize = 5;
|
|
||||||
|
|
||||||
pub(crate) fn quantize(image: &RgbaImage, num_colors: usize) -> RgbaImage {
|
|
||||||
let mut quantizer = OctTreeQuantizer::new(num_colors);
|
|
||||||
quantizer.quantize(image)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OctTreeQuantizer {
|
|
||||||
root: Rc<RefCell<OctTreeNode>>,
|
|
||||||
reduce_colors: usize,
|
|
||||||
maximum_colors: usize,
|
|
||||||
colors: usize,
|
|
||||||
color_list: Vec<Vec<Option<Rc<RefCell<OctTreeNode>>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OctTreeQuantizer {
|
|
||||||
fn new(num_colors: usize) -> Self {
|
|
||||||
let mut new_quantizer = Self {
|
|
||||||
root: Rc::new(RefCell::new(OctTreeNode::new())),
|
|
||||||
reduce_colors: usize::max(512, num_colors * 2),
|
|
||||||
maximum_colors: num_colors,
|
|
||||||
color_list: vec![],
|
|
||||||
colors: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
for _ in 0..=MAX_LEVEL {
|
|
||||||
new_quantizer.color_list.push(Vec::new());
|
|
||||||
}
|
|
||||||
new_quantizer
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn quantize(&mut self, image: &RgbaImage) -> RgbaImage {
|
|
||||||
for pixel in image.pixels() {
|
|
||||||
self.insert_color(pixel, Rc::clone(&self.root));
|
|
||||||
|
|
||||||
if self.colors > self.reduce_colors {
|
|
||||||
self.reduce_tree(self.reduce_colors);
|
|
||||||
//reduce sets to None and the code below actually removes nodes from the list
|
|
||||||
for level in &mut self.color_list {
|
|
||||||
level.retain(|c| c.is_some());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let table = self.build_color_table();
|
|
||||||
|
|
||||||
let mut imgbuf = RgbaImage::new(image.width(), image.height());
|
|
||||||
for (x, y, pixel) in image.enumerate_pixels() {
|
|
||||||
if let Some(index) = self.get_index_for_color(pixel, &self.root) {
|
|
||||||
let color = &table[index];
|
|
||||||
if let Some(color) = color {
|
|
||||||
imgbuf.put_pixel(x, y, *color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
imgbuf
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_index_for_color<P>(&self, color: &P, node: &Rc<RefCell<OctTreeNode>>) -> Option<usize>
|
|
||||||
where
|
|
||||||
P: Pixel<Subpixel = u8> + 'static,
|
|
||||||
{
|
|
||||||
fn get_index_for_color<P>(
|
|
||||||
quantizer: &OctTreeQuantizer,
|
|
||||||
color: &P,
|
|
||||||
level: usize,
|
|
||||||
node: &Rc<RefCell<OctTreeNode>>,
|
|
||||||
) -> Option<usize>
|
|
||||||
where
|
|
||||||
P: Pixel<Subpixel = u8> + 'static,
|
|
||||||
{
|
|
||||||
if level > MAX_LEVEL {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let node = Rc::clone(node);
|
|
||||||
let index = get_bitmask(color, &level);
|
|
||||||
|
|
||||||
let node_b = node.borrow();
|
|
||||||
let child = &node_b.leaf[index];
|
|
||||||
|
|
||||||
if let Some(child) = child {
|
|
||||||
let child_b = child.borrow();
|
|
||||||
if child_b.is_leaf {
|
|
||||||
return Some(child_b.index);
|
|
||||||
} else {
|
|
||||||
return get_index_for_color(quantizer, color, level + 1, child);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Some(node_b.index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get_index_for_color(&self, color, 0, node)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_color_table(&mut self) -> Vec<Option<Rgba<u8>>> {
|
|
||||||
//nested function that is called recursively
|
|
||||||
fn build_color_table(
|
|
||||||
quantizer: &mut OctTreeQuantizer,
|
|
||||||
node: &Rc<RefCell<OctTreeNode>>,
|
|
||||||
table: &mut Vec<Option<Rgba<u8>>>,
|
|
||||||
index: usize,
|
|
||||||
) -> usize {
|
|
||||||
if quantizer.colors > quantizer.maximum_colors {
|
|
||||||
quantizer.reduce_tree(quantizer.maximum_colors);
|
|
||||||
}
|
|
||||||
if node.borrow().is_leaf {
|
|
||||||
{
|
|
||||||
let node = node.borrow();
|
|
||||||
let count = node.count;
|
|
||||||
table[index] = Some(Rgba::from([
|
|
||||||
(node.total_red / count as u32) as u8,
|
|
||||||
(node.total_green / count as u32) as u8,
|
|
||||||
(node.total_blue / count as u32) as u8,
|
|
||||||
255,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
node.borrow_mut().index = index;
|
|
||||||
|
|
||||||
index + 1
|
|
||||||
} else {
|
|
||||||
let mut result = index;
|
|
||||||
for i in 0..8 {
|
|
||||||
// cannot iterate leaf, because that widens the scope of the borrow (of node)
|
|
||||||
let mut node = node.borrow_mut();
|
|
||||||
if let Some(leaf) = &node.leaf[i] {
|
|
||||||
//could be immutable borrow
|
|
||||||
let new_index = build_color_table(quantizer, leaf, table, result);
|
|
||||||
node.index = index; //but also need mutable borrow here
|
|
||||||
result = new_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut table: Vec<Option<Rgba<u8>>> = vec![None; self.colors];
|
|
||||||
let node = Rc::clone(&self.root);
|
|
||||||
build_color_table(self, &node, &mut table, 0);
|
|
||||||
table
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_color<P>(&mut self, rgb: &P, node: Rc<RefCell<OctTreeNode>>)
|
|
||||||
where
|
|
||||||
P: Pixel<Subpixel = u8> + 'static,
|
|
||||||
{
|
|
||||||
//nested function that is called recursively
|
|
||||||
fn insert_color<P>(
|
|
||||||
quantizer: &mut OctTreeQuantizer,
|
|
||||||
color: &P,
|
|
||||||
level: usize,
|
|
||||||
node: Rc<RefCell<OctTreeNode>>,
|
|
||||||
) where
|
|
||||||
P: Pixel<Subpixel = u8> + 'static,
|
|
||||||
{
|
|
||||||
if level > MAX_LEVEL {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = get_bitmask(color, &level);
|
|
||||||
|
|
||||||
if node.borrow().leaf[index].is_none() {
|
|
||||||
let mut child = OctTreeNode::new();
|
|
||||||
child.parent = Some(Rc::clone(&node));
|
|
||||||
child.p_index = quantizer.color_list[level].len();
|
|
||||||
|
|
||||||
if level == MAX_LEVEL {
|
|
||||||
child.is_leaf = true;
|
|
||||||
child.count = 1;
|
|
||||||
child.total_red = color.channels()[0] as u32;
|
|
||||||
child.total_green = color.channels()[1] as u32;
|
|
||||||
child.total_blue = color.channels()[2] as u32;
|
|
||||||
child.level = level;
|
|
||||||
quantizer.colors += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let child = Rc::new(RefCell::new(child));
|
|
||||||
quantizer.color_list[level].push(Some(Rc::clone(&child)));
|
|
||||||
let clone = Rc::clone(&child);
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut mutnode = node.borrow_mut();
|
|
||||||
mutnode.children += 1;
|
|
||||||
mutnode.is_leaf = false;
|
|
||||||
mutnode.leaf[index] = Some(child);
|
|
||||||
}
|
|
||||||
|
|
||||||
if level < MAX_LEVEL {
|
|
||||||
insert_color(quantizer, color, level + 1, clone);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if node
|
|
||||||
.borrow()
|
|
||||||
.leaf
|
|
||||||
.get(index)
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.borrow()
|
|
||||||
.is_leaf
|
|
||||||
{
|
|
||||||
let mut node = node.borrow_mut();
|
|
||||||
let mut child = node
|
|
||||||
.leaf
|
|
||||||
.get_mut(index)
|
|
||||||
.unwrap()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.borrow_mut();
|
|
||||||
child.count += 1;
|
|
||||||
child.total_red += color.channels()[0] as u32;
|
|
||||||
child.total_green += color.channels()[1] as u32;
|
|
||||||
child.total_blue += color.channels()[2] as u32;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
insert_color(
|
|
||||||
quantizer,
|
|
||||||
color,
|
|
||||||
level + 1,
|
|
||||||
Rc::clone(&(node.borrow().leaf[index]).as_ref().unwrap()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
insert_color(self, rgb, 0, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reduce_tree(&mut self, num_colors: usize) {
|
|
||||||
// Nested function that is called recursively
|
|
||||||
fn reduce_tree(quantizer: &mut OctTreeQuantizer, num_colors: usize, level: isize) {
|
|
||||||
if level < 0 {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
let mut removals = Vec::new();
|
|
||||||
let list = &quantizer.color_list[level as usize];
|
|
||||||
for node in list {
|
|
||||||
if let Some(node) = node {
|
|
||||||
if node.borrow().children > 0 {
|
|
||||||
for i in 0..8 {
|
|
||||||
let mut color: Option<(usize, u32, u32, u32, usize)> = None;
|
|
||||||
|
|
||||||
if let Some(child) = node.borrow().leaf.get(i) {
|
|
||||||
if let Some(child) = child {
|
|
||||||
let child = child.borrow();
|
|
||||||
color = Some((
|
|
||||||
child.count,
|
|
||||||
child.total_red,
|
|
||||||
child.total_green,
|
|
||||||
child.total_blue,
|
|
||||||
child.p_index,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to mutate node, which conflicts with previous borrow to retrieve the child
|
|
||||||
if let Some(color) = color {
|
|
||||||
let mut node = node.borrow_mut();
|
|
||||||
node.count += color.0;
|
|
||||||
node.total_red += color.1;
|
|
||||||
node.total_green += color.2;
|
|
||||||
node.total_blue += color.3;
|
|
||||||
node.leaf[i] = None;
|
|
||||||
node.children -= 1;
|
|
||||||
quantizer.colors -= 1;
|
|
||||||
|
|
||||||
removals.push(color.4); //save for further processing outside loop (and borrow of colorlist)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.borrow_mut().is_leaf = true;
|
|
||||||
quantizer.colors += 1;
|
|
||||||
if quantizer.colors <= num_colors {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let color_list = &mut quantizer.color_list[level as usize + 1];
|
|
||||||
for index in removals {
|
|
||||||
color_list[index] = None; //set to None here, Option removed later
|
|
||||||
}
|
|
||||||
|
|
||||||
reduce_tree(quantizer, num_colors, level - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// call to nested function
|
|
||||||
reduce_tree(self, num_colors, (MAX_LEVEL - 1) as isize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OctTreeNode {
|
|
||||||
children: usize,
|
|
||||||
level: usize,
|
|
||||||
parent: Option<Rc<RefCell<OctTreeNode>>>,
|
|
||||||
leaf: Vec<Option<Rc<RefCell<OctTreeNode>>>>,
|
|
||||||
is_leaf: bool,
|
|
||||||
count: usize,
|
|
||||||
total_red: u32,
|
|
||||||
total_green: u32,
|
|
||||||
total_blue: u32,
|
|
||||||
index: usize,
|
|
||||||
p_index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OctTreeNode {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
children: 0,
|
|
||||||
level: 0,
|
|
||||||
parent: None,
|
|
||||||
leaf: vec![None; 8],
|
|
||||||
is_leaf: false,
|
|
||||||
count: 0,
|
|
||||||
total_red: 0,
|
|
||||||
total_green: 0,
|
|
||||||
total_blue: 0,
|
|
||||||
index: 0,
|
|
||||||
p_index: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_bitmask<P>(color: &P, level: &usize) -> usize
|
|
||||||
where
|
|
||||||
P: Pixel<Subpixel = u8> + 'static,
|
|
||||||
{
|
|
||||||
let bit = 0x80 >> level;
|
|
||||||
|
|
||||||
let mut index = 0;
|
|
||||||
if (color.channels()[0] & bit) != 0 {
|
|
||||||
index += 4;
|
|
||||||
}
|
|
||||||
if (color.channels()[1] & bit) != 0 {
|
|
||||||
index += 2;
|
|
||||||
}
|
|
||||||
if (color.channels()[2] & bit) != 0 {
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
index
|
|
||||||
}
|
|
||||||
|
|
@ -7,7 +7,7 @@ use include_dir::{include_dir, Dir, DirEntry};
|
||||||
static mut SAMPLES: OnceLock<Vec<ColorSample>> = OnceLock::new();
|
static mut SAMPLES: OnceLock<Vec<ColorSample>> = OnceLock::new();
|
||||||
static SAMPLES_DIR: Dir = include_dir!("src/samples");
|
static SAMPLES_DIR: Dir = include_dir!("src/samples");
|
||||||
|
|
||||||
pub fn init() {
|
pub fn read_jpeg_bytes() {
|
||||||
unsafe {
|
unsafe {
|
||||||
SAMPLES.get_or_init(|| {
|
SAMPLES.get_or_init(|| {
|
||||||
log("reading image samples");
|
log("reading image samples");
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 26 KiB |
|
|
@ -1,230 +0,0 @@
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
static MEM: OnceLock<Vec<&'static str>> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn samples() -> &'static Vec<&'static str> {
|
|
||||||
MEM.get_or_init(|| {
|
|
||||||
vec![
|
|
||||||
"12110f", "131211", "21201b", "21201d", "212213", "22211b", "23221d", "23221f",
|
|
||||||
"24231d", "ede4d0", "ede4d2", "ede5b8", "edea91", "edea92", "eee6d1", "efe2b2",
|
|
||||||
"252520", "262527", "262628", "272826", "291f19", "292927", "292a29", "2a2a28",
|
|
||||||
"2a3960", "2a3961", "2b3a5f", "2c2c24", "2c3b64", "2c3c40", "2e2d26", "2e2d28",
|
|
||||||
"2e2d29", "30301c", "303030", "303131", "303632", "31302b", "313131", "32373a",
|
|
||||||
"32373b", "333a73", "334840", "343a6f", "343b73", "344845", "353420", "35342f",
|
|
||||||
"353a39", "353b43", "363531", "363c71", "36414c", "364968", "373530", "373a54",
|
|
||||||
"373b46", "373d2f", "373d47", "373d71", "382f1f", "383732", "383831", "383832",
|
|
||||||
"383a46", "383c4f", "383d48", "384142", "38495a", "384b51", "393b39", "394235",
|
|
||||||
"3a4042", "3a4242", "3b4435", "3b4a6b", "3b4f6b", "3c3b34", "3c4d5f", "3d342a",
|
|
||||||
"3d4343", "3d4a63", "3e3524", "3e3830", "3e3e32", "3e3e4a", "3e3f27", "3e4543",
|
|
||||||
"3f3f3a", "3f404f", "3f423b", "3f4b56", "3f527a", "3f5352", "40434b", "404358",
|
|
||||||
"413327", "413d2c", "413f56", "41403d", "414646", "41494e", "424b4a", "43423c",
|
|
||||||
"43475c", "434943", "434a54", "435969", "44414a", "444933", "444d4e", "453a2c",
|
|
||||||
"453b2f", "454048", "454534", "45453e", "454c48", "454c4c", "463728", "463e36",
|
|
||||||
"464226", "464540", "464832", "464e4d", "473e2f", "47443b", "474550", "475141",
|
|
||||||
"475237", "475f6e", "484841", "484846", "484853", "484b43", "484c49", "485162",
|
|
||||||
"49323c", "493c3b", "494941", "494a3b", "495950", "495c62", "495d6d", "4a4052",
|
|
||||||
"4a4326", "4a4a42", "4a4c3f", "4a4c41", "4a5050", "4a5159", "4a5f6e", "4b3541",
|
|
||||||
"4b4737", "4b4941", "4b4b44", "4b4b57", "4b4c4b", "4b4d4c", "4b4f41", "4c4b57",
|
|
||||||
"4c4f59", "4c505b", "4d4533", "4d4b4b", "4d4b58", "4d4d44", "4d4d45", "4d4e47",
|
|
||||||
"4d4f39", "4d545d", "4d5554", "4d5656", "4d616e", "4e3b2e", "4e3d1d", "4e4752",
|
|
||||||
"4e4f4b", "4e5454", "4f3124", "4f3d29", "4f4735", "4f4b47", "4f4e43", "4f4f4e",
|
|
||||||
"4f5145", "4f514f", "4f584f", "4f5857", "50281e", "50393a", "504543", "504729",
|
|
||||||
"504f3b", "505052", "505440", "505542", "50574e", "505968", "50656a", "513c2e",
|
|
||||||
"514131", "514636", "514c39", "515145", "515241", "515762", "515a57", "51646b",
|
|
||||||
"52524a", "525a61", "525c55", "534836", "534a31", "535250", "535959", "544b3b",
|
|
||||||
"544c3a", "545447", "54544c", "545531", "545650", "545863", "553d28", "55442a",
|
|
||||||
"554936", "554a39", "554e30", "554e31", "555133", "555556", "555f54", "55698e",
|
|
||||||
"556f73", "56422d", "564635", "565648", "565656", "565843", "565e46", "566448",
|
|
||||||
"566454", "573823", "57422c", "574642", "574e3e", "575651", "57574f", "57584b",
|
|
||||||
"575d54", "583f2d", "58462c", "584c3c", "58503f", "585348", "585763", "585a56",
|
|
||||||
"585f69", "594a35", "59594d", "596063", "596068", "596562", "596e6f", "5a4826",
|
|
||||||
"5a4a38", "5a4a3b", "5a552e", "5a5b4e", "5a5f49", "5a6168", "5a624d", "5a6674",
|
|
||||||
"5b4332", "5b4636", "5b492c", "5b4a22", "5b5237", "5b542f", "5b5b50", "5b626c",
|
|
||||||
"5b6651", "5b666e", "5c4331", "5c462e", "5c4831", "5c4a37", "5c4f2b", "5c534e",
|
|
||||||
"5c5c5c", "5c5f68", "5c655f", "5c6f70", "5d3c2b", "5d3c2c", "5d4520", "5d4625",
|
|
||||||
"5d495c", "5d4b31", "5d4b32", "5d4f4a", "5d5539", "5d5644", "5d5d55", "5d5e4f",
|
|
||||||
"5e3d30", "5e3f32", "5e4625", "5e4c29", "5e4c3a", "5e4d34", "5e504c", "5e685d",
|
|
||||||
"5e693e", "5e6b51", "5f402b", "5f4827", "5f4e28", "5f5443", "5f544f", "5f5741",
|
|
||||||
"5f5a33", "5f6259", "5f664f", "5f6670", "5f6768", "5f6a4a", "60432b", "60452b",
|
|
||||||
"604931", "60503a", "606a3e", "607c7b", "614336", "616451", "616839", "616a69",
|
|
||||||
"616d9d", "61786a", "617963", "622f24", "623a26", "623a27", "623e2b", "624035",
|
|
||||||
"62432b", "62492e", "62502f", "625a3a", "625b45", "626361", "63453b", "636547",
|
|
||||||
"636563", "636744", "63674c", "636a61", "636c51", "638f7c", "638f7d", "643327",
|
|
||||||
"64391f", "64482c", "644e3b", "644f2e", "645466", "64586c", "645a70", "645e37",
|
|
||||||
"64645b", "646463", "653933", "654131", "654333", "654438", "65473a", "654f30",
|
|
||||||
"654f42", "655230", "65532e", "655f4e", "656160", "656366", "656559", "656c64",
|
|
||||||
"656c72", "656c75", "65705a", "663429", "664720", "66513d", "665d50", "665e4c",
|
|
||||||
"666047", "66655e", "667e69", "673d2a", "674154", "674232", "674737", "674a2c",
|
|
||||||
"674d37", "67514b", "675339", "675a3e", "675d51", "676568", "676b48", "676b5b",
|
|
||||||
"678353", "682626", "682726", "683326", "683849", "683d2a", "684230", "684635",
|
|
||||||
"684d3f", "68533a", "685340", "68604e", "686148", "68716a", "68726a", "68807d",
|
|
||||||
"691e2c", "69432f", "694d24", "695857", "696051", "696343", "696960", "696f47",
|
|
||||||
"697c90", "6a1f2d", "6a4029", "6a4125", "6a4428", "6a4631", "6a493a", "6a4a29",
|
|
||||||
"6a4e21", "6a4e2f", "6a6a62", "6a7360", "6b2231", "6b3a21", "6b4026", "6b523b",
|
|
||||||
"6b5933", "6b6251", "6b6f5c", "6b705a", "6b7375", "6c312d", "6c3727", "6c372b",
|
|
||||||
"6c392a", "6c412b", "6c4331", "6c4738", "6c5928", "6c5937", "6c5a37", "6c5b34",
|
|
||||||
"6c5e64", "6c616a", "6c633a", "6c6641", "6c7370", "6c8c6e", "6d4624", "6d4834",
|
|
||||||
"6d492a", "6d4f23", "6d5058", "6d522d", "6d5732", "6d5a31", "6d5a33", "6d5a43",
|
|
||||||
"6d6d65", "6d6e5f", "6d8759", "6e4028", "6e4624", "6e4a2e", "6e5034", "6e5631",
|
|
||||||
"6e5b60", "6e635f", "6e6a3b", "6e7251", "6e7776", "6e8a7f", "6f4b39", "6f5233",
|
|
||||||
"6f542f", "6f5830", "6f5936", "6f5958", "6f5d3a", "6f5e33", "6f6072", "6f7d76",
|
|
||||||
"6f828c", "6f8f71", "70391d", "704022", "704620", "704f27", "705022", "70543b",
|
|
||||||
"705528", "70554f", "70673e", "70693e", "706b45", "707369", "70796e", "70916b",
|
|
||||||
"71301e", "714728", "714730", "714831", "714a30", "715c2d", "716068", "716235",
|
|
||||||
"716857", "716a3c", "723932", "72422c", "72492d", "725627", "725733", "725922",
|
|
||||||
"725a22", "725f40", "72603c", "72613a", "72726a", "73342e", "733834", "733e24",
|
|
||||||
"735544", "73572d", "735b5b", "735e4e", "737365", "73794b", "73795b", "737963",
|
|
||||||
"737f77", "742e34", "742f32", "74352d", "743d2e", "744833", "744f35", "745045",
|
|
||||||
"745325", "745739", "74573c", "746234", "746264", "746457", "74705d", "747848",
|
|
||||||
"747d78", "753034", "753d1f", "755d30", "756535", "756a44", "757144", "757365",
|
|
||||||
"757568", "7583a6", "758d72", "763d34", "764650", "764d31", "765c4e", "766452",
|
|
||||||
"766f52", "767241", "767244", "767251", "76766d", "773a3a", "774320", "774429",
|
|
||||||
"77452d", "774b34", "775245", "77593a", "775c2f", "77653f", "776541", "777568",
|
|
||||||
"777767", "783620", "783836", "78383b", "78452a", "78472f", "78482f", "785237",
|
|
||||||
"785433", "785737", "785825", "785f3f", "78612c", "786539", "786879", "787245",
|
|
||||||
"78766b", "787870", "787e67", "793135", "793620", "79383b", "79404d", "794456",
|
|
||||||
"795b29", "79616b", "796853", "79827e", "7a3638", "7a3c48", "7a432c", "7a452d",
|
|
||||||
"7a492d", "7a4b2f", "7a5628", "7a5b29", "7a5d24", "7a6950", "7a754a", "7a7e67",
|
|
||||||
"7a8347", "7a8665", "7b3a27", "7b3c48", "7b3f21", "7b472a", "7b5139", "7b5c2e",
|
|
||||||
"7b5f39", "7b604e", "7b6125", "7b6224", "7b625c", "7b6656", "7b6944", "7b7563",
|
|
||||||
"7b7a6b", "7b7c6f", "7c3b34", "7c3e2a", "7c402d", "7c452b", "7c4542", "7c4a30",
|
|
||||||
"7c4b2e", "7c5032", "7c5437", "7c5928", "7c5e31", "7c8164", "7c8379", "7c867f",
|
|
||||||
"7c8c5d", "7d4624", "7d4d34", "7d5645", "7d5926", "7d5928", "7d602e", "7d6134",
|
|
||||||
"7d806b", "7d8577", "7d877c", "7d877d", "7d8965", "7e4120", "7e472f", "7e4925",
|
|
||||||
"7e4a37", "7e562d", "7e592d", "7e5d28", "7e7266", "7e726e", "7e7763", "7e7764",
|
|
||||||
"7e7964", "7e887d", "7e8c71", "7e8e52", "7e9b69", "7f401e", "7f4838", "7f4d37",
|
|
||||||
"7f5639", "7f5827", "7f5e60", "7f604d", "7f6156", "7f633d", "7f6b2e", "7f7347",
|
|
||||||
"7f827a", "7f846a", "803d29", "803f2b", "80403b", "804937", "804b3d", "804e29",
|
|
||||||
"804e2c", "805130", "805335", "805441", "805644", "805d44", "806051", "806c51",
|
|
||||||
"807a63", "807c53", "807e71", "80855b", "808c68", "813a26", "813e31", "813f31",
|
|
||||||
"814f30", "815230", "815737", "81583d", "815a32", "815b3d", "816b5b", "81734d",
|
|
||||||
"817b5a", "81826e", "818370", "824432", "824e2c", "825037", "82552e", "82562e",
|
|
||||||
"825e50", "826130", "826243", "826321", "826d37", "827443", "82846f", "828b5e",
|
|
||||||
"829399", "833128", "834b31", "835231", "835341", "835747", "835949", "835f56",
|
|
||||||
"836559", "836934", "836e42", "83856c", "838b4d", "84492a", "844d2b", "844f39",
|
|
||||||
"845028", "845231", "845439", "84644f", "84655a", "847b47", "847e68", "84a162",
|
|
||||||
"84a163", "853022", "85481e", "854854", "854c58", "855437", "855535", "85553e",
|
|
||||||
"855b1f", "855e2e", "855e51", "856128", "856836", "856b35", "857a66", "85887c",
|
|
||||||
"859156", "85b0ad", "863d37", "864536", "864a2b", "864c25", "86532e", "865a33",
|
|
||||||
"866345", "866c3b", "866d35", "866e4a", "867348", "868159", "868550", "868c61",
|
|
||||||
"869e61", "873e4f", "874724", "874e26", "874e39", "87522e", "87523c", "875844",
|
|
||||||
"875b30", "875b3f", "875c4a", "875d34", "875d41", "876330", "877163", "87744e",
|
|
||||||
"87744f", "877a54", "877d72", "877f6c", "87b1ad", "884834", "884e2c", "885837",
|
|
||||||
"885c39", "885f41", "886055", "88685d", "88692f", "886a49", "886b31", "886e68",
|
|
||||||
"886f30", "88775d", "887a4a", "887e72", "888168", "88835b", "888492", "8895a0",
|
|
||||||
"894b36", "895c34", "895e4a", "896156", "896d46", "897232", "89726f", "89785b",
|
|
||||||
"8a4a32", "8a5933", "8a5a42", "8a5f30", "8a633b", "8a6727", "8a6a2e", "8a7e46",
|
|
||||||
"8a855b", "8a8e65", "8a9355", "8b4d30", "8b5745", "8b5a49", "8b604f", "8b6c63",
|
|
||||||
"8b773d", "8b7d8e", "8ba784", "8c4d36", "8c552d", "8c5d44", "8c6249", "8c6853",
|
|
||||||
"8c6b34", "8c6c26", "8c6e55", "8c6f36", "8c7754", "8c7d4d", "8c833b", "8c846e",
|
|
||||||
"8c8568", "8c8762", "8c935c", "8c9a6e", "8d4328", "8d532b", "8d5634", "8d5734",
|
|
||||||
"8d5937", "8d6349", "8d643c", "8d6535", "8d692e", "8d7262", "8d7539", "8d7760",
|
|
||||||
"8d895a", "8d96a8", "8d978c", "8d9f7f", "8da96c", "8db29b", "8e4326", "8e4640",
|
|
||||||
"8e4e2f", "8e5635", "8e714f", "8e783a", "8e7e54", "8e866f", "8e8955", "8e8b57",
|
|
||||||
"8e8e4c", "8f4326", "8f4f2e", "8f542c", "8f5624", "8f5a2d", "8f5c37", "8f6141",
|
|
||||||
"8f653b", "8f6659", "8f7065", "8f735c", "8f7631", "8f783b", "8f8f4b", "8fa27e",
|
|
||||||
"90442f", "905121", "905229", "90523b", "905337", "905d3e", "906140", "906a41",
|
|
||||||
"907741", "907b69", "908972", "90996b", "909971", "909c6e", "90aa84", "90ab6f",
|
|
||||||
"913826", "914c27", "914f39", "915352", "915539", "915729", "91603c", "916947",
|
|
||||||
"917849", "917a35", "91804b", "91967c", "91ac8c", "924c50", "92522e", "925428",
|
|
||||||
"925b36", "925b39", "926649", "92694e", "926a4d", "926b42", "926d31", "926d32",
|
|
||||||
"927339", "927a51", "927c32", "92834a", "928652", "934753", "934b26", "934b2d",
|
|
||||||
"935826", "935c33", "936f34", "93724b", "93844d", "939254", "939977", "93a27b",
|
|
||||||
"94533d", "94542f", "945528", "945732", "94573e", "945935", "945940", "94653a",
|
|
||||||
"947454", "947836", "94894c", "948a62", "94915a", "949872", "949b73", "949c64",
|
|
||||||
"949c68", "949d70", "949f72", "94af8c", "94b294", "955234", "95532e", "95532f",
|
|
||||||
"95602a", "956a47", "957044", "957a52", "959c79", "959c7b", "964920", "965a37",
|
|
||||||
"96602a", "966449", "966a46", "966e34", "967758", "967938", "969e70", "96a67f",
|
|
||||||
"975457", "975b29", "976b46", "976c5c", "976e38", "976e50", "977145", "977332",
|
|
||||||
"978560", "97a472", "97a965", "98472f", "98532d", "985a34", "986547", "986b34",
|
|
||||||
"986c54", "98733a", "987a37", "987b6d", "988042", "98915b", "989864", "996a38",
|
|
||||||
"996d41", "997054", "997242", "99915e", "999d7e", "99a096", "9a4c2d", "9a5a2e",
|
|
||||||
"9a5a32", "9a5d2e", "9a642b", "9a661e", "9a7243", "9a7529", "9a772d", "9a7738",
|
|
||||||
"9a7d63", "9a814f", "9a8b94", "9a8c56", "9a8e49", "9a934d", "9a9c71", "9ab594",
|
|
||||||
"9ab595", "9b4a34", "9b502f", "9b5436", "9b5b2e", "9b5d49", "9b6435", "9b6647",
|
|
||||||
"9b6a38", "9b7139", "9b734a", "9b7836", "9b7a44", "9b8352", "9b9559", "9c5121",
|
|
||||||
"9c5932", "9c5d58", "9c6625", "9c6d3d", "9c7644", "9c7737", "9c7b5b", "9c7f3d",
|
|
||||||
"9c8445", "9d3c25", "9d482c", "9d5131", "9d592b", "9d6031", "9d6d38", "9d7451",
|
|
||||||
"9d754c", "9d7e49", "9d803c", "9d8f96", "9d903c", "9d9149", "9d9686", "9da3ad",
|
|
||||||
"9e5137", "9e531f", "9e552d", "9e595c", "9e666b", "9e7849", "9e795c", "9e7b39",
|
|
||||||
"9e8557", "9e9aa8", "9e9b67", "9f4e29", "9f5661", "9f5c3c", "9f6025", "9f632e",
|
|
||||||
"9f633b", "9f642c", "9f673b", "9f6c3b", "9f714b", "9f7354", "9f774d", "9f792f",
|
|
||||||
"9f7e3d", "9f7e6f", "9f804b", "9f876a", "9f9346", "9f9787", "9f9f95", "9fb168",
|
|
||||||
"a04225", "a0462e", "a05227", "a05639", "a0623a", "a06343", "a07154", "a07244",
|
|
||||||
"a08a43", "a0ad87", "a1442a", "a14729", "a15e29", "a16427", "a16730", "a16739",
|
|
||||||
"a16d30", "a17034", "a17b4b", "a17e34", "a19258", "a25031", "a26320", "a26e41",
|
|
||||||
"a2742b", "a28359", "a2844a", "a29eab", "a2a15f", "a2aa7d", "a3432b", "a34a2e",
|
|
||||||
"a35a32", "a35c2a", "a3602a", "a3696e", "a3743a", "a37442", "a3753a", "a37636",
|
|
||||||
"a3773d", "a38f3e", "a39251", "a39d7c", "a3ab84", "a44e2b", "a45132", "a45b20",
|
|
||||||
"a46b35", "a46c77", "a4734d", "a4772e", "a47949", "a47e4f", "a47f5b", "a48455",
|
|
||||||
"a48d5f", "a4a16c", "a4ad80", "a55825", "a56130", "a5613b", "a56639", "a56768",
|
|
||||||
"a5693e", "a56a32", "a57042", "a57b3a", "a57c41", "a5947f", "a66139", "a66827",
|
|
||||||
"a6722c", "a67555", "a67746", "a67a35", "a6814b", "a6823f", "a68630", "a69d54",
|
|
||||||
"a7651c", "a76534", "a76834", "a76a31", "a77039", "a77459", "a7802e", "a7804e",
|
|
||||||
"a78152", "a78336", "a78a54", "a78d5a", "a78e6c", "a79455", "a79f8d", "a7a4af",
|
|
||||||
"a84d2d", "a85431", "a8623a", "a8775b", "a88057", "a8825c", "a88544", "a89671",
|
|
||||||
"a94126", "a94c2f", "a95723", "a95923", "a96d33", "a97a3f", "a97b5b", "a9977e",
|
|
||||||
"a99ba4", "a9a6a9", "aa4226", "aa4a22", "aa4b2e", "aa4d27", "aa5225", "aa6d2c",
|
|
||||||
"aa6e1c", "aa883c", "aa8a75", "aa9144", "aa9163", "aaa6a7", "ab4a2c", "ab5312",
|
|
||||||
"ab734f", "ab7e3d", "ab8146", "ab8d59", "ab954f", "ab965f", "ab994a", "ac5918",
|
|
||||||
"ac682a", "ac6a2f", "ac6b72", "ac795c", "ac7a47", "ac7b5c", "ac8558", "ac8943",
|
|
||||||
"ac8a44", "ac9459", "ad5e27", "ad6b21", "ad6e35", "ad7028", "ad793b", "ad7d3b",
|
|
||||||
"ad8037", "ad8c3b", "ad8c61", "ad924f", "ada874", "adb588", "adc4a9", "ae5032",
|
|
||||||
"ae7024", "ae7945", "ae812e", "ae8241", "ae8654", "ae8667", "ae895b", "ae8b5c",
|
|
||||||
"ae8c36", "ae943c", "ae9546", "af7734", "af8860", "af9747", "af9a62", "af9d7f",
|
|
||||||
"afb486", "afba86", "b04d2c", "b0883e", "b08e45", "b08f53", "b09050", "b0913a",
|
|
||||||
"b0a64a", "b0ba8d", "b1513a", "b15732", "b1682d", "b16a2b", "b18540", "b18f5e",
|
|
||||||
"b19458", "b19955", "b19c5c", "b1a55d", "b1bf9a", "b1c4a6", "b2543f", "b25528",
|
|
||||||
"b26e43", "b2713a", "b27223", "b2731f", "b27329", "b27536", "b27836", "b27b28",
|
|
||||||
"b27f27", "b28429", "b28738", "b28741", "b2894c", "b28f41", "b2944f", "b29b50",
|
|
||||||
"b29c52", "b2a36f", "b2babb", "b2c199", "b34b30", "b37229", "b37944", "b37a47",
|
|
||||||
"b38840", "b3a48c", "b46e1c", "b47149", "b4752a", "b4802f", "b4833c", "b49446",
|
|
||||||
"b49a67", "b49e50", "b49f5c", "b4a04c", "b58a3c", "b58c44", "b58d6d", "b58e47",
|
|
||||||
"b59b53", "b59d56", "b5a05c", "b5b7b4", "b5c6ad", "b6612e", "b68431", "b68b64",
|
|
||||||
"b68f44", "b69642", "b6974a", "b6a56b", "b6b689", "b75129", "b7512e", "b77434",
|
|
||||||
"b7752d", "b78f51", "b79454", "b7975b", "b79939", "b79c53", "b7a04f", "b7a256",
|
|
||||||
"b85132", "b87e42", "b8834a", "b89169", "b89170", "b89248", "b89542", "b89a4c",
|
|
||||||
"b89b5a", "b9581a", "b95d38", "b96c30", "b98138", "b9874d", "b9916a", "b99450",
|
|
||||||
"b99642", "b99843", "b99b4c", "b9a783", "b9a890", "b9bd97", "b9bf95", "ba832c",
|
|
||||||
"ba8a4b", "ba9a5a", "baa053", "bac8af", "bb7d34", "bb8c6a", "bb9235", "bb923d",
|
|
||||||
"bb9b48", "bb9c4d", "bba14d", "bba25d", "bba362", "bba557", "bbab6a", "bc6d23",
|
|
||||||
"bc6f36", "bc7f4f", "bc867e", "bc8e47", "bc9241", "bc9777", "bc9878", "bca967",
|
|
||||||
"bcb9be", "bcc29c", "bd8443", "bd8c46", "bd902b", "bda452", "bdab84", "be5531",
|
|
||||||
"be7330", "be7a2f", "be7e28", "be8355", "be872d", "be8829", "be9876", "be9e48",
|
|
||||||
"bea043", "bea451", "beaa79", "beb98a", "bec298", "becbb2", "bf6d24", "bf8853",
|
|
||||||
"bf8d32", "bf9871", "bf9a6d", "bf9e3f", "bfa065", "bfa383", "bfa862", "bfaa59",
|
|
||||||
"c05d3e", "c0754c", "c07a44", "c07c39", "c07d4f", "c0862f", "c08b2e", "c09198",
|
|
||||||
"c0949d", "c09735", "c09739", "c09e4a", "c0a751", "c0ab58", "c17e32", "c18b59",
|
|
||||||
"c18e5e", "c19e35", "c1a54d", "c1a873", "c1ac5c", "c28321", "c29231", "c29f3d",
|
|
||||||
"c29f5c", "c2a451", "c2cfb5", "c35b3c", "c37f47", "c3882f", "c38b2a", "c39368",
|
|
||||||
"c39585", "c39a40", "c3a544", "c3a57a", "c3a64d", "c3a862", "c3b381", "c47b1c",
|
|
||||||
"c4a249", "c4a75c", "c4a84d", "c4a986", "c4ac78", "c5674d", "c58b35", "c58c2c",
|
|
||||||
"c5a33f", "c5a956", "c5b177", "c5b697", "c5c776", "c67634", "c6b686", "c76e4a",
|
|
||||||
"c78a59", "c78b24", "c78c23", "c78e2d", "c7a84b", "c7b683", "c86646", "c8751b",
|
|
||||||
"c8b26b", "c8b468", "c8b58f", "c96e2c", "c96e33", "c98e34", "c98f2d", "c9953f",
|
|
||||||
"c9a955", "c9b35f", "c9c495", "ca8d59", "ca8f49", "ca943f", "caa863", "caaf62",
|
|
||||||
"cab978", "cab98c", "cab997", "cac1c3", "cad3bb", "cb8427", "cb8727", "cb8b2c",
|
|
||||||
"cb9138", "cbad8a", "cbbc83", "cbbc8b", "cbd5bd", "cc933b", "ccb084", "ccb486",
|
|
||||||
"ccbc72", "ccd2b8", "cd8926", "cd8b5a", "cd8d27", "cd912f", "cd9e41", "cda24b",
|
|
||||||
"cdac5e", "cdac81", "cdaf4d", "cdb65c", "cdb777", "cdbe77", "cdd2b8", "ce7837",
|
|
||||||
"ce783d", "ce8826", "ce9150", "cebe70", "cf781b", "cf7836", "cf922e", "cfae3c",
|
|
||||||
"cfb376", "cfbf83", "d07a39", "d08a56", "d0ae49", "d0b340", "d0b98b", "d0ba62",
|
|
||||||
"d19d70", "d1a849", "d1ab4d", "d1ad50", "d1b14a", "d1b577", "d1b86c", "d1c384",
|
|
||||||
"d29c48", "d2a230", "d2ac4d", "d2b88a", "d2c38c", "d3791b", "d4895f", "d49b62",
|
|
||||||
"d4af4f", "d4bf71", "d4c779", "d57a5b", "d5a376", "d5aa57", "d5b877", "d5bc6f",
|
|
||||||
"d5c09f", "d5c170", "d5c678", "d5c793", "d69649", "d69b72", "d6b664", "d6c3a9",
|
|
||||||
"d7b647", "d7c189", "d7c797", "d8a677", "d8ba4f", "d8c27e", "d8c991", "d8caa9",
|
|
||||||
"d9a268", "d9b849", "d9cb9a", "d9cc65", "daa456", "dab948", "dabb4b", "dac66f",
|
|
||||||
"dac8a4", "daca75", "dacc9a", "dace67", "db9a7c", "dbba4c", "dbca80", "dbce69",
|
|
||||||
"dbd07c", "dcbf6f", "dcc267", "dcd068", "ddb784", "ddc663", "ddd069", "dea94f",
|
|
||||||
"decfa8", "ded18c", "ded1a5", "ded298", "e0c980", "e0d2a5", "e19846", "e1b384",
|
|
||||||
"e1cb56", "e1d3a2", "e1d487", "e2cc6d", "e2d786", "e2d8b6", "e3d162", "e3d4bd",
|
|
||||||
"e3d6ae", "e4a14a", "e4d7a6", "e4d97a", "e5d6a1", "e5d7b2", "e5d89d", "e5d8b1",
|
|
||||||
"e6af8c", "e6cf6b", "e6d888", "e6d988", "e6d9b1", "e7c782", "e7d9a6", "e7db7e",
|
|
||||||
"e7dfc8", "e8ae8a", "e8dab3", "e8db90", "e8ddba", "e8de98", "e9cfa9", "e9de97",
|
|
||||||
"e9e488", "eac551", "eae27f", "eae2c2", "eae37f", "ebc851", "ebcc88", "ebd3ad",
|
|
||||||
"ebe083", "ebe688", "ecc69d", "ece2bb", "ece2d0", "edd75e", "edde8f", "ede0a9",
|
|
||||||
"ede4a5", "f0e5bc", "f0e788",
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -21,9 +21,13 @@ var mainConfig = {
|
||||||
use: ["style-loader", "css-loader"],
|
use: ["style-loader", "css-loader"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(png|svg|jpg|gif)$/,
|
test: /\.(jpg|png|svg|gif)$/,
|
||||||
use: ["file-loader"],
|
type: "asset/resource",
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// test: /\.(png|svg|jpg|gif)$/,
|
||||||
|
// use: ["file-loader"],
|
||||||
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
|
||||||