水印功能

This commit is contained in:
itorr
2022-08-12 13:41:13 +08:00
parent 2215b4baec
commit 3cacb083a3
9 changed files with 211 additions and 129 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
one.psd
html/images/*
参考/*
note.md

View File

@@ -152,6 +152,8 @@ const style = {
l:50,
black:true,
kuma:true,
hajimei:false,
watermark: false,
convoluteName: '一般',
convolute1Diff: true,
convoluteName2: null,
@@ -199,10 +201,14 @@ const app = new Vue({
clearTimeout(app.T)
app.T = setTimeout(this.louvre,300)
},
louvre(){
app.src = louvre(this.img,{
...this.style,
Convolutes,
async louvre(){
app.src = await louvre({
img: this.img,
canvas: this.$refs['canvas'],
config: {
...this.style,
Convolutes,
}
});
},
setImageAndDraw(e){

19
html/document.less Normal file
View File

@@ -0,0 +1,19 @@
:root{
--background-color: #c3c3c3;
}
html{
background:var(--background-color);
}
canvas{
background:#FFF;
}
img,canvas{
max-width:800px;
}
/* .app[data-cover="true"] canvas{
width:480px;
height:480px;
} */
h1{
}

View File

@@ -3,132 +3,140 @@
<head>
<meta charset="UTF-8">
<title>One Last Image - One Last Kiss 风格 图片转线稿 生成器</title>
<style>
canvas{
background:#FFF;
}
img,canvas{
max-width:800px;
}
</style>
<link rel="stylesheet" href="document.css">
<meta name="viewport" content="width=device-width,user-scalable=0">
</head>
<body>
<div class="app">
<div class="app" :data-cover="style.cover">
<header>
<h1>
One Last Image
</h1>
<img src="../one-last-image-logo-color.png">
</header>
<div class="ctrl-box">
<img src="images/asuka-8.jpg" @load="setImageAndDraw">
<div class="config-box">
<!-- <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;">
<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.convolute1Diff">卷曲diff
</label>
</div> -->
<div class="label-box" style="padding:4px 0;">
<label>
<input type="checkbox" v-model="style.black">铺调子
</label>
<label>
<input type="checkbox" v-model="style.cover">方形封面
</label>
<label>
<input type="checkbox" v-model="style.kuma">Kiss
</label>
<label>
<input type="checkbox" v-model="style.watermark">watermark
</label>
<div class="label-box" style="padding:4px 0;">
<label>
<input type="checkbox" v-model="style.black">铺调子
</label>
<label>
<input type="checkbox" v-model="style.cover">方形封面
</label>
<label>
<input type="checkbox" v-model="style.kuma">Kiss
</label>
</div>
<label>
<input type="checkbox" v-model="style.hajimei">初回限定
</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: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: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;">
<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="80" max="126" step="1">
<span v-text="style.darkCut"></span>
<!-- <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="80" max="126" step="1">
<span v-text="style.darkCut"></span>
</div>
<div class="label-box" style="padding:4px 0;">
<b>调子数量</b>
<input type="range" v-model.number="style.blackLimit"
min="20" max="140" step="1">
<span v-text="style.blackLimit"></span>
</div>
<div class="label-box" style="padding:4px 0;">
<b>调子轻重</b>
<input type="range" v-model.number="style.blackLight"
min="10" max="70" step="1">
<span v-text="style.blackLight"></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 class="label-box" style="padding:4px 0;">
<b>调子数量</b>
<input type="range" v-model.number="style.blackLimit"
min="20" max="140" step="1">
<span v-text="style.blackLimit"></span>
</div>
<div class="label-box" style="padding:4px 0;">
<b>调子轻重</b>
<input type="range" v-model.number="style.blackLight"
min="10" max="70" step="1">
<span v-text="style.blackLight"></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> -->
<canvas ref="canvas"></canvas>
</div>
</div>

View File

@@ -10,10 +10,7 @@ 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;
@@ -28,7 +25,7 @@ const canvasBlack = document.createElement('canvas');
const canvasBlackMin = document.createElement('canvas');
const canvasMin = document.createElement('canvas');
const louvre = (img, config, callback) => {
const louvre = async ({img, canvas, config, callback}) => {
if (!img || !config) return;
const configString = [
@@ -91,10 +88,12 @@ const louvre = (img, config, callback) => {
let setHeight = _height;
canvas.width = _width;
canvas.height = _height;
const ctx = canvas.getContext('2d');
ctx.drawImage(
img,
cutLeft, cutTop,
@@ -213,7 +212,8 @@ const louvre = (img, config, callback) => {
let pixel1 = config.convoluteName ? convoluteY(
pixel,
config.Convolutes[config.convoluteName]
config.Convolutes[config.convoluteName],
ctx
) : pixel;
// if(config.contrast){
@@ -229,7 +229,8 @@ const louvre = (img, config, callback) => {
if(config.convolute1Diff){
let pixel2 = config.convoluteName2 ? convoluteY(
pixel,
config.Convolutes[config.convoluteName2]
config.Convolutes[config.convoluteName2],
ctx
) : pixel;
console.log(/pixel2/,config.Convolutes[config.convoluteName2],pixel2);
@@ -303,7 +304,6 @@ const louvre = (img, config, callback) => {
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');
@@ -311,7 +311,6 @@ const louvre = (img, config, callback) => {
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);
@@ -416,13 +415,61 @@ const louvre = (img, config, callback) => {
0,0,_width,_height
);
// one-last-image-logo-color.png
if(config.watermark){
// const watermarkImageEl = await loadImagePromise('one-last-image-logo2.png');
const watermarkImageWidth = watermarkImageEl.naturalWidth;
const watermarkImageHeight = watermarkImageEl.naturalHeight / 2;
let setWidth = _width * 0.3;
let setHeight = setWidth / watermarkImageWidth * watermarkImageHeight;
if( _width / _height > 1.1 ){
setHeight = _height * 0.15;
setWidth = setHeight / watermarkImageHeight * watermarkImageWidth;
}
let cutTop = 0;
if(config.hajimei){
cutTop = watermarkImageHeight;
}
let setLeft = _width - setWidth - setHeight * 0.2;
let setTop = _height - setHeight - setHeight * 0.16;
ctx.drawImage(
watermarkImageEl,
0,cutTop,
watermarkImageWidth,watermarkImageHeight,
setLeft, setTop,
setWidth, setHeight
);
}
console.timeEnd('louvre');
// return canvas.toDataURL('image/png');
};
let loadImage = (url,onOver)=>{
const el = new Image();
el.onload = _=>onOver(el);
el.src = url;
};
let loadImagePromise = async url=>{
return new Promise(function(resolve, reject){
setTimeout(function(){
const el = new Image();
el.onload = _=>resolve(el);
el.onerror = e=>reject(e);
el.src = url;
}, 2000);
});
}
let convolute = (pixels, weights) => {
let watermarkImageEl;
loadImage('one-last-image-logo2.png',el=>watermarkImageEl = el);
let convolute = (pixels, weights, ctx) => {
const side = Math.round(Math.sqrt(weights.length));
const halfSide = Math.floor(side / 2);
@@ -473,7 +520,7 @@ let convolute = (pixels, weights) => {
const convoluteY = (pixels, weights) => {
const convoluteY = (pixels, weights, ctx) => {
const side = Math.round( Math.sqrt( weights.length ) );
const halfSide = Math.floor(side / 2);

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
one-last-image-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
one-last-image-logo.psd Normal file

Binary file not shown.