This commit is contained in:
itorr
2022-08-11 13:35:53 +08:00
parent 981757e2ce
commit 1d4c50bcde
6 changed files with 1105 additions and 0 deletions

104
html/bezier-easing.js Normal file
View File

@@ -0,0 +1,104 @@
/**
* https://github.com/gre/bezier-easing
* BezierEasing - use bezier curve for transition easing function
* by Gaëtan Renaudeau 2014 - 2015 MIT License
*/
// These values are established by empiricism with tests (tradeoff: performance VS precision)
var NEWTON_ITERATIONS = 4;
var NEWTON_MIN_SLOPE = 0.001;
var SUBDIVISION_PRECISION = 0.0000001;
var SUBDIVISION_MAX_ITERATIONS = 10;
var kSplineTableSize = 11;
var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
var float32ArraySupported = typeof Float32Array === 'function';
function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
function C (aA1) { return 3.0 * aA1; }
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; }
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); }
function binarySubdivide (aX, aA, aB, mX1, mX2) {
var currentX, currentT, i = 0;
do {
currentT = aA + (aB - aA) / 2.0;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0.0) {
aB = currentT;
} else {
aA = currentT;
}
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
return currentT;
}
function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
var currentSlope = getSlope(aGuessT, mX1, mX2);
if (currentSlope === 0.0) {
return aGuessT;
}
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
function LinearEasing (x) {
return x;
}
function bezier (mX1, mY1, mX2, mY2) {
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
throw new Error('bezier x values must be in [0, 1] range');
}
if (mX1 === mY1 && mX2 === mY2) {
return LinearEasing;
}
// Precompute samples table
var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
for (var i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
function getTForX (aX) {
var intervalStart = 0.0;
var currentSample = 1;
var lastSample = kSplineTableSize - 1;
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;
// Interpolate to provide an initial guess for t
var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
var guessForT = intervalStart + dist * kSampleStepSize;
var initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= NEWTON_MIN_SLOPE) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
} else if (initialSlope === 0.0) {
return guessForT;
} else {
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
}
}
return function BezierEasing (x) {
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
if (x === 0 || x === 1) {
return x;
}
return calcBezier(getTForX(x), mY1, mY2);
};
};

111
html/color.js Normal file
View File

@@ -0,0 +1,111 @@
const 转换十进制=function(num){
return parseInt(num,16)
};
const 十六进制转换十进制=function(color){
if(!color)
color='EEEEEE'
return color.match(/\w\w/g).map(转换十进制)
};
const 转换十六进制=function(num){
num=num.toString(16);
return num.length==2?num:('0'+num)
};
const 十进制颜色转换十六进制=function(arr){
return arr.map(转换十六进制).join('');
};
const rgb2hsl=function(o){
var
r=o[0],
g=o[1],
b=o[2];
r /= 255, g /= 255, b /= 255;
var
max = Math.max(r, g, b),
min = Math.min(r, g, b);
var
h,
s,
l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
//h *= 60;
}
// h=Math.round(h*14);
// s=Math.round(s*7);
// l=Math.round(l*7);
return [h, s, l];
};
const hsl2rgb=function(hsl) {
// const hsl = /hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(hslValue);
var h = hsl[0];// / 360;
var s = hsl[1];// / 100;
var l = hsl[2];// / 100;
// console.log(h,s,l);
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var r, g, b;
if (s == 0) {
r = g = b = l;
} else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [
Math.round(r*255),
Math.round(g*255),
Math.round(b*255)
]
// return `rgb(${r * 255},${g * 255},${b * 255})`;
};
const hax2burn=function(hax,burn){
const hsl=rgb2hsl(十六进制转换十进制(hax))
hsl[1] = hsl[1]/5 + 0.6
hsl[2]= burn
// console.log(hsl)
return 十进制颜色转换十六进制(hsl2rgb(hsl))
};
const hax2light=function(hax,l){
const hsl=rgb2hsl(十六进制转换十进制(hax))
hsl[2]= l
// console.log(hsl)
return 十进制颜色转换十六进制(hsl2rgb(hsl))
};
// export {
// 十六进制转换十进制,
// hax2burn,
// hax2light
// }

229
html/document.js Normal file
View File

@@ -0,0 +1,229 @@
const readFileToURL = (file,onOver)=>{
var reader = new FileReader();
reader.onload = ()=>{
const src = reader.result;
onOver(src);
};
reader.readAsDataURL(file);
};
const readFileAndSetIMGSrc = file=>{
readFileToURL(file,src=>{
// const img = new Image();
const img = document.querySelector('img')
img.src = src;
img.onload = ()=>{
app.img = img;
louvre(img,app.style, src=>{
app.src = src;
});
}
});
};
const isImageRegex = /^image\/(jpeg|gif|png|bmp|webp)$/;
document.addEventListener('paste',e=>{
// console.log(e.clipboardData,e.clipboardData.files);
const clipboardData = e.clipboardData;
if(clipboardData.items[0]){
let file = clipboardData.items[0].getAsFile();
if(file && isImageRegex.test(file.type)){
return readFileAndSetIMGSrc(file);
}
}
if(clipboardData.files.length){
for(let i = 0;i<clipboardData.files.length;i++){
if(isImageRegex.test(clipboardData.files[i].type)){
console.log(clipboardData.files[i])
readFileAndSetIMGSrc(clipboardData.files[i]);
}
}
}
});
document.addEventListener('dragover',e=>{
e.preventDefault();
});
document.addEventListener('drop',e=>{
e.preventDefault();
const file = e.dataTransfer.files[0];
if(file && file.type.match(isImageRegex)){
readFileAndSetIMGSrc(file);
}
});
const _louvre = (img,style,callback)=>{
clearTimeout(louvre.T);
louvre.T = setTimeout(()=>{
louvre(img,style,callback);
app.saveData();
},100);
};
const deepCopy = o=>JSON.parse(JSON.stringify(o));
const creatConvoluteCenterHigh = (w,centerV)=>{
const arr = [];
const c = Math.floor((w*w)/2);
for(let x = 0; x < w; x++){
for(let y = 0; y < w; y++){
let i = x * w + y;
arr[i] = -1;
if(i===c){
arr[i] = centerV;
}
}
}
return arr;
}
const creatConvoluteAverage = (w)=>new Array(w*w).fill(1/(w*w))
const Convolutes = {
// '右倾': [
// 0, -1, 0,
// -1, 2, 2,
// 0, -1, 0
// ],
// '左倾': [
// 0, -1, 0,
// 3, 2, -2,
// 0, -1, 0
// ],
'极精细': creatConvoluteAverage(3),
'精细': creatConvoluteAverage(5),
'一般': creatConvoluteAverage(7),
'粗线条': creatConvoluteAverage(9),
'超粗': creatConvoluteAverage(11),
// '12421': [
// -3,2,-3,
// 2,4, 2,
// -3,2,-3,
// ],
// '9,-1,8': [
// -1 ,-1 ,-1 ,
// -1 , 9 ,-1 ,
// -1 ,-1 ,-1 ,
// ],
'25,-1,24':creatConvoluteCenterHigh(5,24),
// '25,-1,25': creatConvoluteCenterHigh(5,25),
// '25,-1,26': [
// -1 , -1 , -1 , -1 , -1 ,
// -1 , -1 , -1 , -1 , -1 ,
// -1 , -1 , 26 , -1 , -1 ,
// -1 , -1 , -1 , -1 , -1 ,
// -1 , -1 , -1 , -1 , -1 ,
// ],
// '-1,0,16': [
// -1 , -1 , -1 , -1 , -1 ,
// -1 , 0 , 0 , 0 , -1 ,
// -1 , 0 , 17 , 0 , -1 ,
// -1 , 0 , 0 , 0 , -1 ,
// -1 , -1 , -1 , -1 , -1 ,
// ],
'浮雕': [
1, 1, 1,
1, 1, -1,
-1, -1, -1
]
}
const style = {
zoom:1.3,
light:0,
s:80,
l:50,
black:true,
convoluteName: '一般',
convolute1Diff: true,
convoluteName2: null,
Convolutes,
contrast: 30,
invertLight: false,
hue:false,
hueGroup:255,
lightGroup:1,
lightCut: 128,
darkCut: 120,
};
const data = {
img:null,
style
};
const chooseFile = callback=>{
chooseFile.form.reset();
chooseFile.input.onchange = function(){
if(!this.files||!this.files[0])return;
callback(this.files[0]);
};
chooseFile.input.click();
};
chooseFile.form = document.createElement('form');
chooseFile.input = document.createElement('input');
chooseFile.input.type = 'file';
chooseFile.form.appendChild(chooseFile.input);
const app = new Vue({
el:'.app',
data,
methods: {
_louvre(){
clearTimeout(app.T)
app.T = setTimeout(this.louvre,300)
},
louvre(){
app.src = louvre(this.img,{
...this.style,
Convolutes,
});
},
setImageAndDraw(e){
let img = e.target;
console.log(img);
this.img = img;
this.louvre();
},
output(){
const a = document.createElement('a');
a.href = this.src;
a.download = `[lab.magiconch.com][90s-time-machine]-${+Date.now()}.jpg`;
a.click();
},
chooseFile(){
chooseFile(readFileAndSetIMGSrc)
},
},
watch:{
style:{
deep:true,
handler(){
this._louvre();
}
}
}
});

BIN
html/images/asuka.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

120
html/index.html Normal file
View File

@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>卷积</title>
<style>
canvas{
background:#FFF;
}
</style>
<meta name="viewport" content="width=device-width,user-scalable=0">
</head>
<body>
<div class="app">
<div class="ctrl-box">
<img src="images/Evangelion.3.0+1.01.Thrice.Upon.a.Time.2021.1080p.AMZN.WEB-DL.DD+.5.1.H.264-RMB-0025.jpg" @load="setImageAndDraw">
<div class="label-box" style="padding:4px 0;">
<b>light</b>
<input type="range" v-model.number="style.light"
min="-100" max="100" step=".1">
<span v-text="style.light"></span>
</div>
<div class="label-box" style="padding:4px 0;">
<b>s</b>
<input type="range" v-model.number="style.s"
min="0" max="100" step="1">
<span v-text="style.s"></span>
</div>
<div class="label-box" style="padding:4px 0;">
<b>l</b>
<input type="range" v-model.number="style.l"
min="0" max="100" step="1">
<span v-text="style.l"></span>
</div>
<div class="label-box" style="padding:10px 0;">
<b>线条方案1</b>
<label>
<input type="radio" v-model="style.convoluteName" :value="null">null
</label>
<label v-for="con,convoluteName in Convolutes">
<input type="radio" v-model="style.convoluteName" :value="convoluteName">{{convoluteName}}
</label>
</div>
<!-- <div class="label-box" style="padding:4px 0;">
<label>
<input type="checkbox" v-model="style.convolute1Diff">卷曲diff
</label>
</div> -->
<div class="label-box" style="padding:4px 0;">
<label>
<input type="checkbox" v-model="style.black">铺调子
</label>
</div>
<!-- <div class="label-box" style="padding:4px 0;">
<b>contrast</b>
<input type="range" v-model.number="style.contrast"
min="1" max="100" step=".1">
<span v-text="style.contrast"></span>
</div> -->
<!-- <div class="label-box" style="padding:10px 0;">
<b>线条方案2</b>
<label>
<input type="radio" v-model="style.convoluteName2" :value="null">null
</label>
<label v-for="con,convoluteName in Convolutes">
<input type="radio" v-model="style.convoluteName2" :value="convoluteName">{{convoluteName}}
</label>
</div> -->
<!-- <div class="label-box" style="padding:4px 0;">
<label>
<input type="checkbox" v-model="style.invertLight">颠倒黑白
</label>
</div> -->
<!-- <div class="label-box" style="padding:4px 0;">
<b>明度观察层</b><input type="range" v-model.number="style.lightGroup"
min="1" max="10" step="1">
<span v-text="style.lightGroup"></span>
</div> -->
<!-- <div class="label-box" style="padding:4px 0;">
<b>高亮切断</b>
<input type="range" v-model.number="style.lightCut"
min="0" max="128" step="1">
<span v-text="style.lightCut"></span>
</div> -->
<div class="label-box" style="padding:4px 0;">
<b>黑场切断</b>
<input type="range" v-model.number="style.darkCut"
min="0" max="127" step="1">
<span v-text="style.darkCut"></span>
</div>
<!-- <div class="label-box" style="padding:4px 0;">
<label>
<input type="checkbox" v-model="style.hue">色轮
</label>
</div>
<div class="label-box" style="padding:4px 0;">
<b>色轮压缩</b><input type="range" v-model.number="style.hueGroup"
min="10" max="255" step="1">
<span v-text="style.hueGroup"></span>
</div> -->
</div>
</div>
<script src="/vue.2.6.11.min.js"></script>
<script src="color.js"></script>
<script src="bezier-easing.js"></script>
<script src="louvre.js"></script>
<script src="document.js"></script>
</body>
</html>

541
html/louvre.js Normal file
View File

@@ -0,0 +1,541 @@
/**
* @author itorr<https://github.com/itorr>
* @date 2020-04-08
* @Description 蒸汽波风格化处理
* */
const rgb2yuv = (r, g, b) => {
var y, u, v;
y = r * .299000 + g * .587000 + b * .114000;
u = r * -.168736 + g * -.331264 + b * .500000 + 128;
v = r * .500000 + g * -.418688 + b * -.081312 + 128;
y = Math.floor(y);
u = Math.floor(u);
v = Math.floor(v);
return [y, u, v];
};
const yuv2rgb = (y, u, v) => {
var r, g, b;
r = y + 1.4075 * (v - 128);
g = y - 0.3455 * (u - 128) - (0.7169 * (v - 128));
b = y + 1.7790 * (u - 128);
r = Math.floor(r);
g = Math.floor(g);
b = Math.floor(b);
r = (r < 0) ? 0 : r;
r = (r > 255) ? 255 : r;
g = (g < 0) ? 0 : g;
g = (g > 255) ? 255 : g;
b = (b < 0) ? 0 : b;
b = (b > 255) ? 255 : b;
return [r, g, b];
};
const randRange = (a, b) => Math.floor(Math.random() * (b - a) + a);
const inputImageEl = document.querySelector('#input');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas)
let width = 640;
let height = 480;
let scale = width / height;
let lastConfigString = null;
const canvasBlack = document.createElement('canvas');
const canvasBlackMin = document.createElement('canvas');
const canvasMin = document.createElement('canvas');
const louvre = (img, config, callback) => {
if (!img || !config) return;
const configString = [
JSON.stringify(config),
img.src,
].join('-');
if (lastConfigString === configString) return;
console.time('louvre');
lastConfigString = configString;
const oriWidth = img.naturalWidth;
const oriHeight = img.naturalHeight;
let oriScale = oriWidth / oriHeight;
// const _width = Math.floor( width / config.zoom );
// const _height = Math.floor( height / config.zoom );
const _width = Math.floor( oriWidth / config.zoom );
const _height = Math.floor( oriHeight / config.zoom );
// const _width = 800;
// const _height = 800;
canvas.width = _width;
canvas.height = _height;
let cutLeft = 0;
let cutTop = 0;
let calcWidth = oriWidth;
let calcHeight = oriHeight;
// if(oriScale > 1){
// cutLeft = (oriScale - 1) * oriHeight / 2;
// calcWidth = oriHeight;
// }else{
// cutTop = (1 - oriScale) * oriHeight / 2;
// calcHeight = oriWidth;
// }
let setLeft = 0;
let setTop = 0;
let setWidth = _width;
let setHeight = _height;
ctx.drawImage(
img,
cutLeft, cutTop,
calcWidth, calcHeight,
setLeft, setTop,
setWidth, setHeight
);
let pixel = ctx.getImageData(0, 0, _width, _height);
let pixelData = pixel.data;
for (let i = 0; i < pixelData.length; i += 4) {
// let yuv = rgb2yuv(
// pixelData[i],
// pixelData[i + 1],
// pixelData[i + 2],
// );
const r = pixelData[i];
const g = pixelData[i + 1];
const b = pixelData[i + 2];
let y = r * .299000 + g * .587000 + b * .114000;
y = Math.floor(y);
// if(i%10000) console.log(y);
pixelData[i ] = y;
pixelData[i + 1] = y;
pixelData[i + 2] = y;
}
let blackPixel;
if(config.black){
// 处理暗面
blackPixel = ctx.createImageData(_width, _height);
for (let i = 0; i < pixelData.length; i += 4) {
let y = pixelData[i];
y = y > 80 ? 0 : (40 + Math.random() * 40 - 20);
// y = Math.max(255-y) * 0.6;
blackPixel.data[i ] = y;
blackPixel.data[i + 1] = 128;
blackPixel.data[i + 2] = 128;
blackPixel.data[i + 3] = Math.floor(Math.random() * 255)//Math.ceil( y + Math.random() * 40 - 20);
}
// /*
// document.body.appendChild(canvasBlack)
const ctxBlack = canvasBlack.getContext('2d');
const ctxBlackMin = canvasBlackMin.getContext('2d');
canvasBlack.width = _width;
canvasBlack.height = _height;
console.log({blackPixel})
ctxBlack.putImageData(blackPixel, 0, 0);
// ctxBlack.fillText('123233',50,50);
const blackZoom = 4;
canvasBlackMin.width = Math.floor(_width/blackZoom);
canvasBlackMin.height = Math.floor(_height/blackZoom);
ctxBlackMin.drawImage(
canvasBlack,
0,0,
canvasBlackMin.width,canvasBlackMin.height
);
ctxBlack.clearRect(0,0,_width,_height)
ctxBlack.drawImage(
canvasBlackMin,
0,0,
_width,_height
);
blackPixel = ctxBlack.getImageData(0,0,_width,_height);
}
const {
light = 0,
} = config;
if(light){
for (let i = 0; i < pixelData.length; i += 4) {
let y = pixelData[i];
y = y + y * (light/100);
pixelData[i ] = y;
pixelData[i + 1] = y;
pixelData[i + 2] = y;
}
// ctx.putImageData(pixel, 0, 0);
// pixel = ctx.getImageData(0, 0, _width, _height);
}
let pixel1 = config.convoluteName ? convoluteY(
pixel,
config.Convolutes[config.convoluteName]
) : pixel;
// if(config.contrast){
// for (let i = 0; i < pixel1.data.length; i +=4) {
// let r = (pixel1.data[i]-128) * config.contrast + 128;
// pixel1.data[i ] = r;
// pixel1.data[i+1] = r;
// pixel1.data[i+2] = r;
// pixel1.data[i+3] = 255;
// }
// }
if(config.convolute1Diff){
let pixel2 = config.convoluteName2 ? convoluteY(
pixel,
config.Convolutes[config.convoluteName2]
) : pixel;
console.log(/pixel2/,config.Convolutes[config.convoluteName2],pixel2);
// pixelData
for (let i = 0; i < pixel2.data.length; i +=4) {
let r = 128 + pixel2.data[i ] - pixel1.data[i ];
pixel2.data[i ] = r;
pixel2.data[i+1] = r;
pixel2.data[i+2] = r;
pixel2.data[i+3] = 255;
}
pixel = pixel2;
}else{
// 不对比
pixel = pixel1;
}
pixelData = pixel.data;
if(config.invertLight){
for (let i = 0; i < pixelData.length; i += 4) {
let r = 255 - pixelData[i]
pixelData[i ] = r
pixelData[i+1 ] = r
pixelData[i+2 ] = r
}
}
if(config.lightGroup!==1){
for (let i = 0; i < pixelData.length; i += 4) {
let y = pixelData[i];
const isOdd = Math.floor(y / (255/config.lightGroup)) % 2 === 0;
y = y % (255/config.lightGroup) * config.lightGroup;
if(isOdd) y = 255 - y;
pixelData[i+0 ] = y
pixelData[i+1 ] = y
pixelData[i+2 ] = y
}
}
if(config.lightCut || config.darkCut){
const scale = 255 / (255 - config.lightCut - config.darkCut);
for (let i = 0; i < pixelData.length; i += 4) {
let y = pixelData[i];
y = (y - config.darkCut) * scale;
y = Math.max(0,y);
pixelData[i+0 ] = y
pixelData[i+1 ] = y
pixelData[i+2 ] = y
pixelData[i+3 ] = 255
}
}
const hStart = 30;
const hEnd = -184;
const be = bezier(0.57, 0.01, 0.43, 0.99);
const s = config.s/100;
const gradient = ctx.createLinearGradient(0,0, _width,_height);
// Add three color stops
gradient.addColorStop(0, '#fbba30');
gradient.addColorStop(0.4, '#fc7235');
gradient.addColorStop(.6, '#fc354e');
gradient.addColorStop(0.7, '#cf36df');
gradient.addColorStop(.8, '#37b5d9');
gradient.addColorStop(1, '#3eb6da');
// Set the fill style and draw a rectangle
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, _width, _height);
let gradientPixel = ctx.getImageData(0, 0, _width, _height);
for (let i = 0; i < pixelData.length; i += 4) {
let y = pixelData[i];
let p = Math.floor(i / 4);
let _h = Math.floor(p/_width);
let _w = p % _width;
/*
// const
// hScale = hScale * hScale;
let hScale = (_h + _w)/(_width + _height);
hScale = hScale * hScale;
hScale = be(hScale);
// let h = Math.floor((hStart + (hScale) * (hEnd - hStart)));
let [h] = rgb2hsl([
gradientPixel.data[i + 0],
gradientPixel.data[i + 1],
gradientPixel.data[i + 2],
]);
const l = y/255;
const rgb = hsl2rgb([h, s, l * (1 - config.l/100) + (config.l/100)]);
if(i%5677===0){
// console.log(h,y,l,l * (config.l/100) + (1 - config.l/100))
// console.log((_h + _w)/(_width + _height),hScale)
}
pixelData[i+0 ] = rgb[0];
pixelData[i+1 ] = rgb[1];
pixelData[i+2 ] = rgb[2];
pixelData[i+3 ] = 255;
*/
pixelData[i+0 ] = gradientPixel.data[i + 0];
pixelData[i+1 ] = gradientPixel.data[i + 1];
pixelData[i+2 ] = gradientPixel.data[i + 2];
y = 255 - y;
if(config.black){
y = Math.max(
y,
blackPixel.data[i]
);
}
pixelData[i+3 ] = y
}
if(config.hue){
for (let i = 0; i < pixelData.length; i += 4) {
let y = pixelData[i ]
y = y % config.hueGroup * (255/config.hueGroup) /255;
const [r,g,b] = hsl2rgb([y,.7,.5])
pixelData[i+0 ] = r
pixelData[i+1 ] = g
pixelData[i+2 ] = b
}
}
// for(let i = 0;i < pixelData.length;i += 4){
// let _rgb = yuv2rgb(
// pixelData[i],
// pixelData[i+1],
// pixelData[i+2],
// );
// pixelData[i ] = _rgb[0];
// pixelData[i+1 ] = _rgb[1];
// pixelData[i+2 ] = _rgb[2];
// }
// blurC();
ctx.putImageData(pixel, 0, 0);
const ctxMin = canvasMin.getContext('2d');
canvasMin.width = Math.floor(_width/1.4);
canvasMin.height = Math.floor(_height/1.3);
ctxMin.clearRect(0,0,canvasMin.width,canvasMin.height)
ctxMin.drawImage(
canvas,
0,0,
canvasMin.width,canvasMin.height
);
ctx.clearRect(0,0,_width,_height)
ctx.drawImage(
canvasMin,
0,0,
canvasMin.width,canvasMin.height,
0,0,_width,_height
);
console.timeEnd('louvre');
// return canvas.toDataURL('image/png');
};
let convolute = (pixels, weights) => {
const side = Math.round(Math.sqrt(weights.length));
const halfSide = Math.floor(side / 2);
const src = pixels.data;
const sw = pixels.width;
const sh = pixels.height;
const w = sw;
const h = sh;
const output = ctx.createImageData(w, h);
const dst = output.data;
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const sy = y;
const sx = x;
const dstOff = (y * w + x) * 4;
let r = 0, g = 0, b = 0;
for (let cy = 0; cy < side; cy++) {
for (let cx = 0; cx < side; cx++) {
const scy = Math.min(sh - 1, Math.max(0, sy + cy - halfSide));
const scx = Math.min(sw - 1, Math.max(0, sx + cx - halfSide));
const srcOff = (scy * sw + scx) * 4;
const wt = weights[cy * side + cx];
r += src[srcOff] * wt;
g += src[srcOff + 1] * wt;
b += src[srcOff + 2] * wt;
}
}
dst[dstOff] = r;
dst[dstOff + 1] = g;
dst[dstOff + 2] = b;
dst[dstOff + 3] = 255;
}
}
// for (let y=0; y<h; y++) {
// for (let x=0; x<w; x++) {
// const srcOff = (y*w+x)*4;
// src[srcOff] = dst[srcOff];
// }
// }
return output;
};
const convoluteY = (pixels, weights) => {
const side = Math.round( Math.sqrt( weights.length ) );
const halfSide = Math.floor(side / 2);
const src = pixels.data;
const w = pixels.width;
const h = pixels.height;
const output = ctx.createImageData(w, h);
const dst = output.data;
for (let sy = 0; sy < h; sy++) {
for (let sx = 0; sx < w; sx++) {
const dstOff = (sy * w + sx) * 4;
let r = 0, g = 0, b = 0;
for (let cy = 0; cy < side; cy++) {
for (let cx = 0; cx < side; cx++) {
const scy = Math.min(h - 1, Math.max(0, sy + cy - halfSide));
const scx = Math.min(w - 1, Math.max(0, sx + cx - halfSide));
const srcOff = (scy * w + scx) * 4;
const wt = weights[cy * side + cx];
r += src[srcOff] * wt;
// g += src[srcOff + 1] * wt;
// b += src[srcOff + 2] * wt;
}
}
dst[dstOff] = r;
dst[dstOff + 1] = r;
dst[dstOff + 2] = r;
dst[dstOff + 3] = 255;
}
}
// for (let y=0; y<h; y++) {
// for (let x=0; x<w; x++) {
// const srcOff = (y*w+x)*4;
// src[srcOff] = dst[srcOff];
// }
// }
return output;
};