working version

This commit is contained in:
Shautvast 2024-04-10 14:56:02 +02:00
parent 42f2ccffac
commit 6a09094d54
16 changed files with 954 additions and 724 deletions

View file

@ -2,29 +2,59 @@
<html> <html>
<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>
<div class="main"> <h1>Klaer Lightende Spiegel der Verfkonst</h1>
<label> <h4>Paints your image in the colors of the
<h2>Blur factor</h2> <a class="link" target="_blank"
<h4 id="msg">(blurring can take up to 15 seconds, please be patient)</h4> href="https://nl.wikipedia.org/wiki/Klaer_Lightende_Spiegel_der_Verfkonst">handpainted
</label> book by A. Boogert</a>,
<div class="slidecontainer"> <img id="spieghel" alt="spieghel" src="" class="hide">
<input type="range" id="slider" value="0" min="0" max="100" class="slider" /> the 1692 version of the Pantone color book.</h4>
</div>
</label>
<div class="main_content">
<div id="progress"></div>
<div class="content" id="images">
<div id="image_container"></div>
<canvas id="canvas" style="visibility: hidden;"></canvas> <div class="main">
</div>
<label>
<h4>Upload or drag&drop a picture and move the slider, for a quick preview</h4>
<h4>Then press Apply</h4>
</label>
<div><input id="upload" type="file" accept="image/jpeg"></div>
<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>
</label>
<div class="main-content">
<div class="content" id="images">
<div id="image-container" class="border" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
<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>
<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>

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 MiB

View file

@ -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");
const slider = document.getElementById("slider"); applyButton.onclick = (event) => {
const progress = document.getElementById("progress"); filterImage(event, false);
slider.onchange = filterImage; };
slider.value = 0;
function filterImage(event) { function filterImage(event, preview) {
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);
// module.gaussian_blur(rust_image, blur_factor); let rust_image = module.open_image(canvas, ctx);
// module.median(rust_image, blur_factor, blur_factor); const out= module.spiegel(rust_image, strokeSize, preview);
module.spiegel(rust_image, blur_factor); module.putImageData(canvas, ctx, out);
module.putImageData(canvas, ctx, rust_image); canvas.setAttribute(
const image_container = document.getElementById("image_container"); "style",
let rect = image_container.getBoundingClientRect(); `visibility:visible;position:absolute;top:${canvasTop}px`,
canvas.setAttribute( );
"style", }
`visibility:visible;position:absolute;top:${rect.top};z-index:100`,
);
// image_container.setAttribute("style", "visibility:hidden");
}
function setUpCanvas() {
let element = document.getElementById("image_container");
element.appendChild(sourceImage);
canvas = document.getElementById("canvas");
canvas.width = sourceImage.width;
canvas.height = sourceImage.height;
sourceImage.setAttribute("style", "width:50vw");
ctx = canvas.getContext("2d");
ctx.drawImage(sourceImage, 0, 0);
}
}); });
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() {
canvas = document.querySelector("#canvas");
originalWidth = sourceImage.width;
originalHeight = sourceImage.height;
canvas.width = originalWidth;
canvas.height = originalHeight;
sourceImage.setAttribute("style", "width:50vw");
const imageContainer = document.querySelector("#image-container");
const rect = imageContainer.getBoundingClientRect();
canvasTop = rect.top;
ctx = canvas.getContext("2d");
}
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");
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

778
webclient/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -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"]

View file

@ -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);

View file

@ -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
}

View file

@ -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");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -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",
]
})
}

View file

@ -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: [