mirror of
https://github.com/itorr/one-last-image.git
synced 2026-01-02 11:56:02 +08:00
水印功能
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,2 +1,4 @@
|
||||
one.psd
|
||||
html/images/*
|
||||
html/images/*
|
||||
参考/*
|
||||
note.md
|
||||
|
||||
@@ -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
19
html/document.less
Normal 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{
|
||||
|
||||
}
|
||||
232
html/index.html
232
html/index.html
@@ -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>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" v-model="style.hajimei">初回限定
|
||||
</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>
|
||||
</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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
BIN
html/one-last-image-logo2.png
Normal file
BIN
html/one-last-image-logo2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 KiB |
BIN
one-last-image-logo-color.png
Normal file
BIN
one-last-image-logo-color.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
BIN
one-last-image-logo.png
Normal file
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
BIN
one-last-image-logo.psd
Normal file
Binary file not shown.
Reference in New Issue
Block a user