2152 lines
52 KiB
JavaScript
2152 lines
52 KiB
JavaScript
import color from '../color'
|
||
import bezierCurve from '../bezier-curve'
|
||
|
||
import {
|
||
deepClone
|
||
} from '../plugin/util'
|
||
|
||
import allGraph from '../config/graphs'
|
||
|
||
import Graph from './graph.class'
|
||
|
||
|
||
/**
|
||
* @description Class of CRender
|
||
* @param {Object} canvas Canvas DOM
|
||
* @return {CRender} Instance of CRender
|
||
*/
|
||
export default class CRender {
|
||
constructor(canvas, t, cav) {
|
||
if (!canvas) {
|
||
console.error('CRender Missing parameters!')
|
||
|
||
return
|
||
}
|
||
if (cav) {
|
||
this.cav = cav;
|
||
} else {
|
||
this.cav = null;
|
||
}
|
||
const ctx = canvas;
|
||
this.t = t;
|
||
// const { clientWidth, clientHeight } = canvas
|
||
|
||
const area = [ctx.width, ctx.height]
|
||
|
||
// canvas.setAttribute('width', clientWidth)
|
||
// canvas.setAttribute('height', clientHeight)
|
||
|
||
/**
|
||
* @description Context of the canvas
|
||
* @type {Object}
|
||
* @example ctx = canvas.getContext('2d')
|
||
*/
|
||
this.ctx = ctx
|
||
/**
|
||
* @description Width and height of the canvas
|
||
* @type {Array}
|
||
* @example area = [300,100]
|
||
*/
|
||
this.area = area
|
||
/**
|
||
* @description Whether render is in animation rendering
|
||
* @type {Boolean}
|
||
* @example animationStatus = true|false
|
||
*/
|
||
this.animationStatus = false
|
||
/**
|
||
* @description Added graph
|
||
* @type {[Graph]}
|
||
* @example graphs = [Graph, Graph, ...]
|
||
*/
|
||
this.graphs = []
|
||
/**
|
||
* @description Color plugin
|
||
* @type {Object}
|
||
* @link https://github.com/jiaming743/color
|
||
*/
|
||
this.color = color
|
||
/**
|
||
* @description Bezier Curve plugin
|
||
* @type {Object}
|
||
* @link https://github.com/jiaming743/BezierCurve
|
||
*/
|
||
this.bezierCurve = bezierCurve
|
||
// t.mousedown = mouseDown.bind(this);
|
||
|
||
// bind event handler
|
||
// canvas.addEventListener('mousedown', mouseDown.bind(this))
|
||
// canvas.addEventListener('mousemove', mouseMove.bind(this))
|
||
// canvas.addEventListener('mouseup', mouseUp.bind(this))
|
||
// console.log(canvas);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @description Clear canvas drawing area
|
||
* @return {Undefined} Void
|
||
*/
|
||
CRender.prototype.clearArea = function() {
|
||
const {
|
||
area
|
||
} = this
|
||
|
||
this.ctx.clearRect(0, 0, ...area)
|
||
}
|
||
|
||
/**
|
||
* @description Add graph to render
|
||
* @param {Object} config Graph configuration
|
||
* @return {Graph} Graph instance
|
||
*/
|
||
CRender.prototype.add = function(config = {}) {
|
||
const {
|
||
name
|
||
} = config
|
||
|
||
if (!name) {
|
||
console.error('add Missing parameters!')
|
||
|
||
return
|
||
}
|
||
|
||
const graphConfig = allGraph.get(name)
|
||
|
||
if (!graphConfig) {
|
||
console.warn('No corresponding graph configuration found!')
|
||
|
||
return
|
||
}
|
||
|
||
const graph = new Graph(graphConfig, config)
|
||
|
||
if (!graph.validator(graph, this.ctx)) return
|
||
|
||
graph.render = this
|
||
|
||
this.graphs.push(graph)
|
||
|
||
this.sortGraphsByIndex()
|
||
|
||
this.drawAllGraph()
|
||
|
||
return graph
|
||
}
|
||
|
||
/**
|
||
* @description Sort the graph by index
|
||
* @return {Undefined} Void
|
||
*/
|
||
CRender.prototype.sortGraphsByIndex = function() {
|
||
const {
|
||
graphs
|
||
} = this
|
||
|
||
graphs.sort((a, b) => {
|
||
if (a.index > b.index) return 1
|
||
if (a.index === b.index) return 0
|
||
if (a.index < b.index) return -1
|
||
})
|
||
}
|
||
|
||
/**
|
||
* @description Delete graph in render
|
||
* @param {Graph} graph The graph to be deleted
|
||
* @return {Undefined} Void
|
||
*/
|
||
CRender.prototype.delGraph = function(graph) {
|
||
if (typeof graph.delProcessor !== 'function') return
|
||
|
||
graph.delProcessor(this)
|
||
|
||
this.graphs = this.graphs.filter(graph => graph)
|
||
|
||
this.drawAllGraph()
|
||
}
|
||
|
||
/**
|
||
* @description Delete all graph in render
|
||
* @return {Undefined} Void
|
||
*/
|
||
CRender.prototype.delAllGraph = function() {
|
||
this.graphs.forEach(graph => graph.delProcessor(this))
|
||
|
||
this.graphs = this.graphs.filter(graph => graph)
|
||
|
||
this.drawAllGraph()
|
||
}
|
||
|
||
/**
|
||
* @description Draw all the graphs in the render
|
||
* @return {Undefined} Void
|
||
*/
|
||
CRender.prototype.drawAllGraph = function() {
|
||
this.clearArea()
|
||
this.graphs.filter(graph => graph && graph.visible).forEach(graph => graph.drawProcessor(this, graph))
|
||
|
||
if (this.ctx?.draw) {
|
||
this.ctx.draw(true)
|
||
}
|
||
|
||
}
|
||
|
||
/**
|
||
* @description Animate the graph whose animation queue is not empty
|
||
* and the animationPause is equal to false
|
||
* @return {Promise} Animation Promise
|
||
*/
|
||
CRender.prototype.launchAnimation = function() {
|
||
const {
|
||
animationStatus
|
||
} = this
|
||
|
||
if (animationStatus) return
|
||
|
||
this.animationStatus = true
|
||
|
||
return new Promise(resolve => {
|
||
animation.call(this, () => {
|
||
this.animationStatus = false
|
||
|
||
resolve()
|
||
}, Date.now())
|
||
})
|
||
}
|
||
|
||
function requestAnimationFrame(callback) {
|
||
// var now = Date.now();
|
||
// var lastTime = 0;
|
||
// var nextTime = Math.max(lastTime + 16, now);
|
||
// // var idd=555
|
||
// // clearTimeout(idd)
|
||
// return new Promise(resolve=>setTimeout(function() { callback(resolve(lastTime = nextTime)); },nextTime - now));
|
||
let lastFrameTime = 0
|
||
var currTime = new Date().getTime();
|
||
var timeToCall = Math.max(0, 16 - (currTime - lastFrameTime));
|
||
var id = setTimeout(function() {
|
||
callback(currTime + timeToCall);
|
||
}, timeToCall);
|
||
lastFrameTime = currTime + timeToCall;
|
||
return id;
|
||
|
||
};
|
||
|
||
/**
|
||
* @description Try to animate every graph
|
||
* @param {Function} callback Callback in animation end
|
||
* @param {Number} timeStamp Time stamp of animation start
|
||
* @return {Undefined} Void
|
||
*/
|
||
function animation(callback, timeStamp) {
|
||
const {
|
||
graphs
|
||
} = this
|
||
|
||
if (!animationAble(graphs)) {
|
||
callback()
|
||
|
||
return
|
||
}
|
||
|
||
graphs.forEach(graph => graph.turnNextAnimationFrame(timeStamp))
|
||
|
||
this.drawAllGraph()
|
||
|
||
// #ifdef H5
|
||
window.requestAnimationFrame(animation.bind(this, callback, timeStamp));
|
||
// #endif
|
||
// #ifndef H5
|
||
if (this.cav) {
|
||
this.cav.requestAnimationFrame(animation.bind(this, callback, timeStamp));
|
||
|
||
} else {
|
||
requestAnimationFrame(animation.bind(this, callback, timeStamp))
|
||
}
|
||
|
||
// #endif
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* @description Find if there are graph that can be animated
|
||
* @param {[Graph]} graphs
|
||
* @return {Boolean}
|
||
*/
|
||
function animationAble(graphs) {
|
||
return graphs.find(graph => !graph.animationPause && graph.animationFrameState.length)
|
||
}
|
||
CRender.prototype.animateAble = function(graphs) {
|
||
|
||
return this.graphs.find(graph => !graph.animationPause && graph.animationFrameState.length)
|
||
}
|
||
/**
|
||
* @description Handler of CRender mousedown event
|
||
* @return {Undefined} Void
|
||
*/
|
||
function mouseDown(e) {
|
||
const {
|
||
graphs
|
||
} = this
|
||
const hoverGraph = graphs.find(graph => graph.status === 'hover')
|
||
|
||
if (!hoverGraph) return
|
||
|
||
hoverGraph.status = 'active'
|
||
}
|
||
|
||
/**
|
||
* @description Handler of CRender mousemove event
|
||
* @return {Undefined} Void
|
||
*/
|
||
function mouseMove(e) {
|
||
const {
|
||
offsetX,
|
||
offsetY
|
||
} = e
|
||
const position = [offsetX, offsetY]
|
||
|
||
const {
|
||
graphs
|
||
} = this
|
||
|
||
const activeGraph = graphs.find(graph => (graph.status === 'active' || graph.status === 'drag'))
|
||
|
||
if (activeGraph) {
|
||
if (!activeGraph.drag) return
|
||
|
||
if (typeof activeGraph.move !== 'function') {
|
||
console.error('No move method is provided, cannot be dragged!')
|
||
|
||
return
|
||
}
|
||
|
||
activeGraph.moveProcessor(e)
|
||
|
||
activeGraph.status = 'drag'
|
||
|
||
return
|
||
}
|
||
|
||
const hoverGraph = graphs.find(graph => graph.status === 'hover')
|
||
|
||
const hoverAbleGraphs = graphs.filter(graph =>
|
||
(graph.hover && (typeof graph.hoverCheck === 'function' || graph.hoverRect)))
|
||
|
||
const hoveredGraph = hoverAbleGraphs.find(graph => graph.hoverCheckProcessor(position, graph))
|
||
|
||
if (hoveredGraph) {
|
||
document.body.style.cursor = hoveredGraph.style.hoverCursor
|
||
} else {
|
||
document.body.style.cursor = 'default'
|
||
}
|
||
|
||
let [hoverGraphMouseOuterIsFun, hoveredGraphMouseEnterIsFun] = [false, false]
|
||
|
||
if (hoverGraph) hoverGraphMouseOuterIsFun = typeof hoverGraph.mouseOuter === 'function'
|
||
if (hoveredGraph) hoveredGraphMouseEnterIsFun = typeof hoveredGraph.mouseEnter === 'function'
|
||
|
||
if (!hoveredGraph && !hoverGraph) return
|
||
|
||
if (!hoveredGraph && hoverGraph) {
|
||
if (hoverGraphMouseOuterIsFun) hoverGraph.mouseOuter(e, hoverGraph)
|
||
|
||
hoverGraph.status = 'static'
|
||
|
||
return
|
||
}
|
||
|
||
if (hoveredGraph && hoveredGraph === hoverGraph) return
|
||
|
||
if (hoveredGraph && !hoverGraph) {
|
||
if (hoveredGraphMouseEnterIsFun) hoveredGraph.mouseEnter(e, hoveredGraph)
|
||
|
||
hoveredGraph.status = 'hover'
|
||
|
||
return
|
||
}
|
||
|
||
if (hoveredGraph && hoverGraph && hoveredGraph !== hoverGraph) {
|
||
if (hoverGraphMouseOuterIsFun) hoverGraph.mouseOuter(e, hoverGraph)
|
||
|
||
hoverGraph.status = 'static'
|
||
|
||
if (hoveredGraphMouseEnterIsFun) hoveredGraph.mouseEnter(e, hoveredGraph)
|
||
|
||
hoveredGraph.status = 'hover'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @description Handler of CRender mouseup event
|
||
* @return {Undefined} Void
|
||
*/
|
||
function mouseUp(e) {
|
||
const {
|
||
graphs
|
||
} = this
|
||
|
||
const activeGraph = graphs.find(graph => graph.status === 'active')
|
||
const dragGraph = graphs.find(graph => graph.status === 'drag')
|
||
|
||
if (activeGraph && typeof activeGraph.click === 'function') activeGraph.click(e, activeGraph)
|
||
|
||
graphs.forEach(graph => graph && (graph.status = 'static'))
|
||
|
||
if (activeGraph) activeGraph.status = 'hover'
|
||
if (dragGraph) dragGraph.status = 'hover'
|
||
}
|
||
|
||
/**
|
||
* @description Clone Graph
|
||
* @param {Graph} graph The target to be cloned
|
||
* @return {Graph} Cloned graph
|
||
*/
|
||
CRender.prototype.clone = function(graph) {
|
||
const style = graph.style.getStyle()
|
||
|
||
let clonedGraph = {
|
||
...graph,
|
||
style
|
||
}
|
||
|
||
delete clonedGraph.render
|
||
|
||
clonedGraph = deepClone(clonedGraph, true)
|
||
|
||
return this.add(clonedGraph)
|
||
}
|
||
|
||
function putImageData(ctx, imageData, dx, dy,
|
||
dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
|
||
var data = imageData.data;
|
||
var height = imageData.height;
|
||
var width = imageData.width;
|
||
|
||
dirtyX = dirtyX || 0;
|
||
dirtyY = dirtyY || 0;
|
||
dirtyWidth = dirtyWidth !== undefined ? dirtyWidth : width;
|
||
dirtyHeight = dirtyHeight !== undefined ? dirtyHeight : height;
|
||
var limitBottom = dirtyY + dirtyHeight;
|
||
var limitRight = dirtyX + dirtyWidth;
|
||
ctx.save()
|
||
ctx.scale(ctx.scaledpr, ctx.scaledpr)
|
||
for (var y = dirtyY; y < limitBottom; y++) {
|
||
for (var x = dirtyX; x < limitRight; x++) {
|
||
var pos = y * width + x;
|
||
ctx.fillStyle = 'rgba(' + data[pos * 4 + 0] +
|
||
',' + data[pos * 4 + 1] +
|
||
',' + data[pos * 4 + 2] +
|
||
',' + (data[pos * 4 + 3] / 255) + ')';
|
||
ctx.fillRect(x + dx, y + dy, 1, 1);
|
||
}
|
||
}
|
||
ctx.restore()
|
||
}
|
||
|
||
|
||
|
||
|
||
CRender.prototype.getImageData = function(x, y, w, h) {
|
||
let t = this;
|
||
let dpr = 1;
|
||
// #ifndef H5
|
||
dpr = t.ctx.dpr;
|
||
console.log(dpr);
|
||
// #endif
|
||
return new Promise((rs, rj) => {
|
||
if (t.ctx.getImageData) {
|
||
var data = t.ctx.getImageData(x, y, w * dpr, h * dpr)
|
||
rs(data);
|
||
|
||
} else {
|
||
// #ifdef APP-VUE || APP-PLUS
|
||
uni.canvasGetImageData({
|
||
canvasId: t.ctx.id,
|
||
x: x,
|
||
y: y,
|
||
width: w,
|
||
height: h,
|
||
success: (res) => rs(res),
|
||
fail: (e) => rj(e)
|
||
}, t.t)
|
||
// #endif
|
||
// #ifndef APP-VUE || APP-PLUS
|
||
uni.canvasGetImageData({
|
||
canvasId: t.ctx.id,
|
||
x: x,
|
||
y: y,
|
||
width: w,
|
||
height: h,
|
||
success: (res) => rs(res),
|
||
fail: (e) => rj(e)
|
||
}, t.t)
|
||
// #endif
|
||
}
|
||
})
|
||
}
|
||
CRender.prototype.putImageData = function(x, y, w, h, data) {
|
||
let t = this;
|
||
let dpr = 1;
|
||
// #ifndef H5
|
||
dpr = t.ctx.dpr;
|
||
// #endif
|
||
t.ctx.clearRect(0, 0, w, h);
|
||
uni.showLoading({
|
||
title: '...',
|
||
mask: true
|
||
})
|
||
|
||
return new Promise((rs, rj) => {
|
||
|
||
if (t.ctx.putImageData) {
|
||
setTimeout(function() {
|
||
putImageData(t.ctx, data, x, y, 0, 0, w * dpr, h * dpr);
|
||
rs();
|
||
uni.hideLoading()
|
||
}, 50);
|
||
} else {
|
||
|
||
uni.canvasPutImageData({
|
||
canvasId: t.ctx.id,
|
||
x: x,
|
||
y: y,
|
||
width: w,
|
||
height: h,
|
||
data: data.data,
|
||
success: (res) => rs(),
|
||
fail: (e) => rj(e),
|
||
complete: () => uni.hideLoading()
|
||
}, t.t)
|
||
}
|
||
})
|
||
}
|
||
// Filters图像效果控制。
|
||
/**
|
||
* 移植自https://konvajs.org/
|
||
* 移植作者:https://jx2d.cn
|
||
*/
|
||
|
||
/**
|
||
* 模糊图片
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} blurRadius
|
||
* @param {Object} type = rgba|rgb
|
||
*/
|
||
CRender.prototype.Blur = function(x, y, w, h, blurRadius, type = 'rgba') {
|
||
var t = this;
|
||
|
||
/*
|
||
StackBlur - a fast almost Gaussian Blur For Canvas
|
||
Version: 0.5
|
||
Author: Mario Klingemann
|
||
Contact: mario@quasimondo.com
|
||
Website: http://www.quasimondo.com/StackBlurForCanvas
|
||
Twitter: @quasimondo
|
||
In case you find this class useful - especially in commercial projects -
|
||
I am not totally unhappy for a small donation to my PayPal account
|
||
mario@quasimondo.de
|
||
Or support me on flattr:
|
||
https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
|
||
Copyright (c) 2010 Mario Klingemann
|
||
Permission is hereby granted, free of charge, to any person
|
||
obtaining a copy of this software and associated documentation
|
||
files (the "Software"), to deal in the Software without
|
||
restriction, including without limitation the rights to use,
|
||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
copies of the Software, and to permit persons to whom the
|
||
Software is furnished to do so, subject to the following
|
||
conditions:
|
||
The above copyright notice and this permission notice shall be
|
||
included in all copies or substantial portions of the Software.
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||
OTHER DEALINGS IN THE SOFTWARE.
|
||
*/
|
||
let dpr = 1;
|
||
// #ifndef H5
|
||
dpr = t.ctx.dpr;
|
||
// #endif
|
||
var mul_table = [
|
||
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
|
||
454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
|
||
482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
|
||
437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
|
||
497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
|
||
320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
|
||
446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
|
||
329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
|
||
505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
|
||
399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
|
||
324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
|
||
268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
|
||
451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
|
||
385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
|
||
332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
|
||
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
|
||
];
|
||
|
||
|
||
var shg_table = [
|
||
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
|
||
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
|
||
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
|
||
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
|
||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
|
||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
|
||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
|
||
];
|
||
|
||
|
||
|
||
function stackBlurCanvasRGBA(imageData, top_x, top_y, width, height, radius) {
|
||
|
||
|
||
var pixels = imageData.data;
|
||
|
||
var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum,
|
||
r_out_sum, g_out_sum, b_out_sum, a_out_sum,
|
||
r_in_sum, g_in_sum, b_in_sum, a_in_sum,
|
||
pr, pg, pb, pa, rbs;
|
||
|
||
var div = radius + radius + 1;
|
||
var w4 = width << 2;
|
||
var widthMinus1 = width - 1;
|
||
var heightMinus1 = height - 1;
|
||
var radiusPlus1 = radius + 1;
|
||
var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;
|
||
|
||
var stackStart = new BlurStack();
|
||
var stack = stackStart;
|
||
for (i = 1; i < div; i++) {
|
||
stack = stack.next = new BlurStack();
|
||
if (i == radiusPlus1) var stackEnd = stack;
|
||
}
|
||
stack.next = stackStart;
|
||
var stackIn = null;
|
||
var stackOut = null;
|
||
|
||
yw = yi = 0;
|
||
|
||
var mul_sum = mul_table[radius];
|
||
var shg_sum = shg_table[radius];
|
||
|
||
for (y = 0; y < height; y++) {
|
||
r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0;
|
||
|
||
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
||
g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||
b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||
a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]);
|
||
|
||
r_sum += sumFactor * pr;
|
||
g_sum += sumFactor * pg;
|
||
b_sum += sumFactor * pb;
|
||
a_sum += sumFactor * pa;
|
||
|
||
stack = stackStart;
|
||
|
||
for (i = 0; i < radiusPlus1; i++) {
|
||
stack.r = pr;
|
||
stack.g = pg;
|
||
stack.b = pb;
|
||
stack.a = pa;
|
||
stack = stack.next;
|
||
}
|
||
|
||
for (i = 1; i < radiusPlus1; i++) {
|
||
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
||
r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i);
|
||
g_sum += (stack.g = (pg = pixels[p + 1])) * rbs;
|
||
b_sum += (stack.b = (pb = pixels[p + 2])) * rbs;
|
||
a_sum += (stack.a = (pa = pixels[p + 3])) * rbs;
|
||
|
||
r_in_sum += pr;
|
||
g_in_sum += pg;
|
||
b_in_sum += pb;
|
||
a_in_sum += pa;
|
||
|
||
stack = stack.next;
|
||
}
|
||
|
||
|
||
stackIn = stackStart;
|
||
stackOut = stackEnd;
|
||
for (x = 0; x < width; x++) {
|
||
pixels[yi + 3] = pa = (a_sum * mul_sum) >> shg_sum;
|
||
if (pa != 0) {
|
||
pa = 255 / pa;
|
||
pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa;
|
||
pixels[yi + 1] = ((g_sum * mul_sum) >> shg_sum) * pa;
|
||
pixels[yi + 2] = ((b_sum * mul_sum) >> shg_sum) * pa;
|
||
} else {
|
||
pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0;
|
||
}
|
||
|
||
r_sum -= r_out_sum;
|
||
g_sum -= g_out_sum;
|
||
b_sum -= b_out_sum;
|
||
a_sum -= a_out_sum;
|
||
|
||
r_out_sum -= stackIn.r;
|
||
g_out_sum -= stackIn.g;
|
||
b_out_sum -= stackIn.b;
|
||
a_out_sum -= stackIn.a;
|
||
|
||
p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;
|
||
|
||
r_in_sum += (stackIn.r = pixels[p]);
|
||
g_in_sum += (stackIn.g = pixels[p + 1]);
|
||
b_in_sum += (stackIn.b = pixels[p + 2]);
|
||
a_in_sum += (stackIn.a = pixels[p + 3]);
|
||
|
||
r_sum += r_in_sum;
|
||
g_sum += g_in_sum;
|
||
b_sum += b_in_sum;
|
||
a_sum += a_in_sum;
|
||
|
||
stackIn = stackIn.next;
|
||
|
||
r_out_sum += (pr = stackOut.r);
|
||
g_out_sum += (pg = stackOut.g);
|
||
b_out_sum += (pb = stackOut.b);
|
||
a_out_sum += (pa = stackOut.a);
|
||
|
||
r_in_sum -= pr;
|
||
g_in_sum -= pg;
|
||
b_in_sum -= pb;
|
||
a_in_sum -= pa;
|
||
|
||
stackOut = stackOut.next;
|
||
|
||
yi += 4;
|
||
}
|
||
yw += width;
|
||
}
|
||
|
||
|
||
for (x = 0; x < width; x++) {
|
||
g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0;
|
||
|
||
yi = x << 2;
|
||
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
||
g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||
b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||
a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]);
|
||
|
||
r_sum += sumFactor * pr;
|
||
g_sum += sumFactor * pg;
|
||
b_sum += sumFactor * pb;
|
||
a_sum += sumFactor * pa;
|
||
|
||
stack = stackStart;
|
||
|
||
for (i = 0; i < radiusPlus1; i++) {
|
||
stack.r = pr;
|
||
stack.g = pg;
|
||
stack.b = pb;
|
||
stack.a = pa;
|
||
stack = stack.next;
|
||
}
|
||
|
||
yp = width;
|
||
|
||
for (i = 1; i <= radius; i++) {
|
||
yi = (yp + x) << 2;
|
||
|
||
r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i);
|
||
g_sum += (stack.g = (pg = pixels[yi + 1])) * rbs;
|
||
b_sum += (stack.b = (pb = pixels[yi + 2])) * rbs;
|
||
a_sum += (stack.a = (pa = pixels[yi + 3])) * rbs;
|
||
|
||
r_in_sum += pr;
|
||
g_in_sum += pg;
|
||
b_in_sum += pb;
|
||
a_in_sum += pa;
|
||
|
||
stack = stack.next;
|
||
|
||
if (i < heightMinus1) {
|
||
yp += width;
|
||
}
|
||
}
|
||
|
||
yi = x;
|
||
stackIn = stackStart;
|
||
stackOut = stackEnd;
|
||
for (y = 0; y < height; y++) {
|
||
p = yi << 2;
|
||
pixels[p + 3] = pa = (a_sum * mul_sum) >> shg_sum;
|
||
if (pa > 0) {
|
||
pa = 255 / pa;
|
||
pixels[p] = ((r_sum * mul_sum) >> shg_sum) * pa;
|
||
pixels[p + 1] = ((g_sum * mul_sum) >> shg_sum) * pa;
|
||
pixels[p + 2] = ((b_sum * mul_sum) >> shg_sum) * pa;
|
||
} else {
|
||
pixels[p] = pixels[p + 1] = pixels[p + 2] = 0;
|
||
}
|
||
|
||
r_sum -= r_out_sum;
|
||
g_sum -= g_out_sum;
|
||
b_sum -= b_out_sum;
|
||
a_sum -= a_out_sum;
|
||
|
||
r_out_sum -= stackIn.r;
|
||
g_out_sum -= stackIn.g;
|
||
b_out_sum -= stackIn.b;
|
||
a_out_sum -= stackIn.a;
|
||
|
||
p = (x + (((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width)) << 2;
|
||
|
||
r_sum += (r_in_sum += (stackIn.r = pixels[p]));
|
||
g_sum += (g_in_sum += (stackIn.g = pixels[p + 1]));
|
||
b_sum += (b_in_sum += (stackIn.b = pixels[p + 2]));
|
||
a_sum += (a_in_sum += (stackIn.a = pixels[p + 3]));
|
||
|
||
stackIn = stackIn.next;
|
||
|
||
r_out_sum += (pr = stackOut.r);
|
||
g_out_sum += (pg = stackOut.g);
|
||
b_out_sum += (pb = stackOut.b);
|
||
a_out_sum += (pa = stackOut.a);
|
||
|
||
r_in_sum -= pr;
|
||
g_in_sum -= pg;
|
||
b_in_sum -= pb;
|
||
a_in_sum -= pa;
|
||
|
||
stackOut = stackOut.next;
|
||
|
||
yi += width;
|
||
}
|
||
}
|
||
|
||
return pixels;
|
||
|
||
}
|
||
|
||
|
||
function stackBlurCanvasRGB(imageData, top_x, top_y, width, height, radius) {
|
||
|
||
|
||
var pixels = imageData.data;
|
||
|
||
var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum,
|
||
r_out_sum, g_out_sum, b_out_sum,
|
||
r_in_sum, g_in_sum, b_in_sum,
|
||
pr, pg, pb, rbs;
|
||
|
||
var div = radius + radius + 1;
|
||
var w4 = width << 2;
|
||
var widthMinus1 = width - 1;
|
||
var heightMinus1 = height - 1;
|
||
var radiusPlus1 = radius + 1;
|
||
var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;
|
||
|
||
var stackStart = new BlurStack();
|
||
var stack = stackStart;
|
||
for (i = 1; i < div; i++) {
|
||
stack = stack.next = new BlurStack();
|
||
if (i == radiusPlus1) var stackEnd = stack;
|
||
}
|
||
stack.next = stackStart;
|
||
var stackIn = null;
|
||
var stackOut = null;
|
||
|
||
yw = yi = 0;
|
||
|
||
var mul_sum = mul_table[radius];
|
||
var shg_sum = shg_table[radius];
|
||
|
||
for (y = 0; y < height; y++) {
|
||
r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0;
|
||
|
||
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
||
g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||
b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||
|
||
r_sum += sumFactor * pr;
|
||
g_sum += sumFactor * pg;
|
||
b_sum += sumFactor * pb;
|
||
|
||
stack = stackStart;
|
||
|
||
for (i = 0; i < radiusPlus1; i++) {
|
||
stack.r = pr;
|
||
stack.g = pg;
|
||
stack.b = pb;
|
||
stack = stack.next;
|
||
}
|
||
|
||
for (i = 1; i < radiusPlus1; i++) {
|
||
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
||
r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i);
|
||
g_sum += (stack.g = (pg = pixels[p + 1])) * rbs;
|
||
b_sum += (stack.b = (pb = pixels[p + 2])) * rbs;
|
||
|
||
r_in_sum += pr;
|
||
g_in_sum += pg;
|
||
b_in_sum += pb;
|
||
|
||
stack = stack.next;
|
||
}
|
||
|
||
|
||
stackIn = stackStart;
|
||
stackOut = stackEnd;
|
||
for (x = 0; x < width; x++) {
|
||
pixels[yi] = (r_sum * mul_sum) >> shg_sum;
|
||
pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum;
|
||
pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum;
|
||
|
||
r_sum -= r_out_sum;
|
||
g_sum -= g_out_sum;
|
||
b_sum -= b_out_sum;
|
||
|
||
r_out_sum -= stackIn.r;
|
||
g_out_sum -= stackIn.g;
|
||
b_out_sum -= stackIn.b;
|
||
|
||
p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;
|
||
|
||
r_in_sum += (stackIn.r = pixels[p]);
|
||
g_in_sum += (stackIn.g = pixels[p + 1]);
|
||
b_in_sum += (stackIn.b = pixels[p + 2]);
|
||
|
||
r_sum += r_in_sum;
|
||
g_sum += g_in_sum;
|
||
b_sum += b_in_sum;
|
||
|
||
stackIn = stackIn.next;
|
||
|
||
r_out_sum += (pr = stackOut.r);
|
||
g_out_sum += (pg = stackOut.g);
|
||
b_out_sum += (pb = stackOut.b);
|
||
|
||
r_in_sum -= pr;
|
||
g_in_sum -= pg;
|
||
b_in_sum -= pb;
|
||
|
||
stackOut = stackOut.next;
|
||
|
||
yi += 4;
|
||
}
|
||
yw += width;
|
||
}
|
||
|
||
|
||
for (x = 0; x < width; x++) {
|
||
g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0;
|
||
|
||
yi = x << 2;
|
||
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
||
g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||
b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||
|
||
r_sum += sumFactor * pr;
|
||
g_sum += sumFactor * pg;
|
||
b_sum += sumFactor * pb;
|
||
|
||
stack = stackStart;
|
||
|
||
for (i = 0; i < radiusPlus1; i++) {
|
||
stack.r = pr;
|
||
stack.g = pg;
|
||
stack.b = pb;
|
||
stack = stack.next;
|
||
}
|
||
|
||
yp = width;
|
||
|
||
for (i = 1; i <= radius; i++) {
|
||
yi = (yp + x) << 2;
|
||
|
||
r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i);
|
||
g_sum += (stack.g = (pg = pixels[yi + 1])) * rbs;
|
||
b_sum += (stack.b = (pb = pixels[yi + 2])) * rbs;
|
||
|
||
r_in_sum += pr;
|
||
g_in_sum += pg;
|
||
b_in_sum += pb;
|
||
|
||
stack = stack.next;
|
||
|
||
if (i < heightMinus1) {
|
||
yp += width;
|
||
}
|
||
}
|
||
|
||
yi = x;
|
||
stackIn = stackStart;
|
||
stackOut = stackEnd;
|
||
for (y = 0; y < height; y++) {
|
||
p = yi << 2;
|
||
pixels[p] = (r_sum * mul_sum) >> shg_sum;
|
||
pixels[p + 1] = (g_sum * mul_sum) >> shg_sum;
|
||
pixels[p + 2] = (b_sum * mul_sum) >> shg_sum;
|
||
|
||
r_sum -= r_out_sum;
|
||
g_sum -= g_out_sum;
|
||
b_sum -= b_out_sum;
|
||
|
||
r_out_sum -= stackIn.r;
|
||
g_out_sum -= stackIn.g;
|
||
b_out_sum -= stackIn.b;
|
||
|
||
p = (x + (((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width)) << 2;
|
||
|
||
r_sum += (r_in_sum += (stackIn.r = pixels[p]));
|
||
g_sum += (g_in_sum += (stackIn.g = pixels[p + 1]));
|
||
b_sum += (b_in_sum += (stackIn.b = pixels[p + 2]));
|
||
|
||
stackIn = stackIn.next;
|
||
|
||
r_out_sum += (pr = stackOut.r);
|
||
g_out_sum += (pg = stackOut.g);
|
||
b_out_sum += (pb = stackOut.b);
|
||
|
||
r_in_sum -= pr;
|
||
g_in_sum -= pg;
|
||
b_in_sum -= pb;
|
||
|
||
stackOut = stackOut.next;
|
||
|
||
yi += width;
|
||
}
|
||
}
|
||
|
||
return pixels;
|
||
|
||
}
|
||
|
||
function BlurStack() {
|
||
this.r = 0;
|
||
this.g = 0;
|
||
this.b = 0;
|
||
this.a = 0;
|
||
this.next = null;
|
||
}
|
||
|
||
|
||
var b = Math.round(blurRadius);
|
||
|
||
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var data;
|
||
if (type == 'rgb') {
|
||
data = stackBlurCanvasRGB(res, x, y, w * dpr, h * dpr, b);
|
||
} else {
|
||
data = stackBlurCanvasRGBA(res, x, y, w * dpr, h * dpr, b);
|
||
}
|
||
console.log(res.height);
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 使图像反色,类似底片
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
*/
|
||
CRender.prototype.ColorInvert = function(x, y, w, h) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var data = res.data;
|
||
for (var i = 0; i < res.data.length; i += 4) {
|
||
var r = data[i];
|
||
var g = data[i + 1];
|
||
var b = data[i + 2];
|
||
|
||
data[i] = 255 - r;
|
||
data[i + 1] = 255 - g;
|
||
data[i + 2] = 255 - b;
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 灰度图片。
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
*/
|
||
CRender.prototype.Grayscale = function(x, y, w, h) {
|
||
let t = this;
|
||
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
// 0.34,0.5,0.16为加权的权重。可适当调整。
|
||
var data = res.data,
|
||
len = data.length,
|
||
i, brightness;
|
||
for (i = 0; i < len; i += 4) {
|
||
brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
|
||
data[i] = brightness;
|
||
data[i + 1] = brightness;
|
||
data[i + 2] = brightness;
|
||
}
|
||
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 调整图片的明暗度。
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} brightness -1~1,小于0为暗,大于1为亮。0为正常。
|
||
*/
|
||
CRender.prototype.Brighten = function(x, y, w, h, brightness) {
|
||
let t = this;
|
||
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
// 0.34,0.5,0.16为加权的权重。可适当调整。
|
||
brightness = brightness * 255
|
||
var data = res.data,
|
||
len = data.length,
|
||
i;
|
||
for (i = 0; i < len; i += 4) {
|
||
data[i] += brightness;
|
||
data[i + 1] += brightness;
|
||
data[i + 2] += brightness;
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 调整图片对比度。
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} brightness -100~100
|
||
*/
|
||
CRender.prototype.Contrast = function(x, y, w, h, brightness) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var adjust = Math.pow((brightness + 100) / 100, 2);
|
||
var data = res.data,
|
||
nPixels = data.length,
|
||
red = 150,
|
||
green = 150,
|
||
blue = 150,
|
||
i;
|
||
for (i = 0; i < nPixels; i += 4) {
|
||
red = data[i];
|
||
green = data[i + 1];
|
||
blue = data[i + 2];
|
||
red /= 255;
|
||
red -= 0.5;
|
||
red *= adjust;
|
||
red += 0.5;
|
||
red *= 255;
|
||
green /= 255;
|
||
green -= 0.5;
|
||
green *= adjust;
|
||
green += 0.5;
|
||
green *= 255;
|
||
blue /= 255;
|
||
blue -= 0.5;
|
||
blue *= adjust;
|
||
blue += 0.5;
|
||
blue *= 255;
|
||
red = red < 0 ? 0 : red > 255 ? 255 : red;
|
||
green = green < 0 ? 0 : green > 255 ? 255 : green;
|
||
blue = blue < 0 ? 0 : blue > 255 ? 255 : blue;
|
||
data[i] = red;
|
||
data[i + 1] = green;
|
||
data[i + 2] = blue;
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
}
|
||
|
||
/**
|
||
* 浮雕
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} embossStrength 浮雕强度 0~1,0为正常。
|
||
* @param {Object} embossWhiteLevel 黑白对比强度0~1
|
||
* @param {Object} embossDirection 浮雕的方向。top,top-left,top-right,left,right,bottom,bottom-left,bottom-right
|
||
* @param {Object} embossBlend 是否显示黑白浮雕
|
||
*/
|
||
CRender.prototype.Emboss = function(x, y, w, h, embossStrength = 0.5, embossWhiteLevel = 0.5, embossDirection =
|
||
"top-right", embossBlend = false) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var strength = embossStrength * 10,
|
||
greyLevel = embossWhiteLevel * 255,
|
||
direction = embossDirection,
|
||
blend = !embossBlend,
|
||
dirY = 0,
|
||
dirX = 0,
|
||
data = res.data,
|
||
w = res.width,
|
||
h = res.height,
|
||
w4 = w * 4,
|
||
y = h;
|
||
switch (direction) {
|
||
case 'top-left':
|
||
dirY = -1;
|
||
dirX = -1;
|
||
break;
|
||
case 'top':
|
||
dirY = -1;
|
||
dirX = 0;
|
||
break;
|
||
case 'top-right':
|
||
dirY = -1;
|
||
dirX = 1;
|
||
break;
|
||
case 'right':
|
||
dirY = 0;
|
||
dirX = 1;
|
||
break;
|
||
case 'bottom-right':
|
||
dirY = 1;
|
||
dirX = 1;
|
||
break;
|
||
case 'bottom':
|
||
dirY = 1;
|
||
dirX = 0;
|
||
break;
|
||
case 'bottom-left':
|
||
dirY = 1;
|
||
dirX = -1;
|
||
break;
|
||
case 'left':
|
||
dirY = 0;
|
||
dirX = -1;
|
||
break;
|
||
default:
|
||
Util_1.Util.error('Unknown emboss direction: ' + direction);
|
||
}
|
||
do {
|
||
var offsetY = (y - 1) * w4;
|
||
var otherY = dirY;
|
||
if (y + otherY < 1) {
|
||
otherY = 0;
|
||
}
|
||
if (y + otherY > h) {
|
||
otherY = 0;
|
||
}
|
||
var offsetYOther = (y - 1 + otherY) * w * 4;
|
||
var x = w;
|
||
do {
|
||
var offset = offsetY + (x - 1) * 4;
|
||
var otherX = dirX;
|
||
if (x + otherX < 1) {
|
||
otherX = 0;
|
||
}
|
||
if (x + otherX > w) {
|
||
otherX = 0;
|
||
}
|
||
var offsetOther = offsetYOther + (x - 1 + otherX) * 4;
|
||
var dR = data[offset] - data[offsetOther];
|
||
var dG = data[offset + 1] - data[offsetOther + 1];
|
||
var dB = data[offset + 2] - data[offsetOther + 2];
|
||
var dif = dR;
|
||
var absDif = dif > 0 ? dif : -dif;
|
||
var absG = dG > 0 ? dG : -dG;
|
||
var absB = dB > 0 ? dB : -dB;
|
||
if (absG > absDif) {
|
||
dif = dG;
|
||
}
|
||
if (absB > absDif) {
|
||
dif = dB;
|
||
}
|
||
dif *= strength;
|
||
if (blend) {
|
||
var r = data[offset] + dif;
|
||
var g = data[offset + 1] + dif;
|
||
var b = data[offset + 2] + dif;
|
||
data[offset] = r > 255 ? 255 : r < 0 ? 0 : r;
|
||
data[offset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
|
||
data[offset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
|
||
} else {
|
||
var grey = greyLevel - dif;
|
||
if (grey < 0) {
|
||
grey = 0;
|
||
} else if (grey > 255) {
|
||
grey = 255;
|
||
}
|
||
data[offset] = data[offset + 1] = data[offset + 2] = grey;
|
||
}
|
||
} while (--x);
|
||
} while (--y);
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
}
|
||
|
||
/**
|
||
* 提高饱和度
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} enhance -1~1
|
||
*/
|
||
CRender.prototype.Enhance = function(x, y, w, h, enhance) {
|
||
let t = this;
|
||
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
function remap(fromValue, fromMin, fromMax, toMin, toMax) {
|
||
var fromRange = fromMax - fromMin,
|
||
toRange = toMax - toMin,
|
||
toValue;
|
||
if (fromRange === 0) {
|
||
return toMin + toRange / 2;
|
||
}
|
||
if (toRange === 0) {
|
||
return toMin;
|
||
}
|
||
toValue = (fromValue - fromMin) / fromRange;
|
||
toValue = toRange * toValue + toMin;
|
||
return toValue;
|
||
}
|
||
var data = res.data,
|
||
nSubPixels = data.length,
|
||
rMin = data[0],
|
||
rMax = rMin,
|
||
r, gMin = data[1],
|
||
gMax = gMin,
|
||
g, bMin = data[2],
|
||
bMax = bMin,
|
||
b, i;
|
||
var enhanceAmount = enhance;
|
||
if (enhanceAmount === 0) {
|
||
return;
|
||
}
|
||
for (i = 0; i < nSubPixels; i += 4) {
|
||
r = data[i + 0];
|
||
if (r < rMin) {
|
||
rMin = r;
|
||
} else if (r > rMax) {
|
||
rMax = r;
|
||
}
|
||
g = data[i + 1];
|
||
if (g < gMin) {
|
||
gMin = g;
|
||
} else if (g > gMax) {
|
||
gMax = g;
|
||
}
|
||
b = data[i + 2];
|
||
if (b < bMin) {
|
||
bMin = b;
|
||
} else if (b > bMax) {
|
||
bMax = b;
|
||
}
|
||
}
|
||
if (rMax === rMin) {
|
||
rMax = 255;
|
||
rMin = 0;
|
||
}
|
||
if (gMax === gMin) {
|
||
gMax = 255;
|
||
gMin = 0;
|
||
}
|
||
if (bMax === bMin) {
|
||
bMax = 255;
|
||
bMin = 0;
|
||
}
|
||
var rMid, rGoalMax, rGoalMin, gMid, gGoalMax, gGoalMin, bMid, bGoalMax, bGoalMin;
|
||
if (enhanceAmount > 0) {
|
||
rGoalMax = rMax + enhanceAmount * (255 - rMax);
|
||
rGoalMin = rMin - enhanceAmount * (rMin - 0);
|
||
gGoalMax = gMax + enhanceAmount * (255 - gMax);
|
||
gGoalMin = gMin - enhanceAmount * (gMin - 0);
|
||
bGoalMax = bMax + enhanceAmount * (255 - bMax);
|
||
bGoalMin = bMin - enhanceAmount * (bMin - 0);
|
||
} else {
|
||
rMid = (rMax + rMin) * 0.5;
|
||
rGoalMax = rMax + enhanceAmount * (rMax - rMid);
|
||
rGoalMin = rMin + enhanceAmount * (rMin - rMid);
|
||
gMid = (gMax + gMin) * 0.5;
|
||
gGoalMax = gMax + enhanceAmount * (gMax - gMid);
|
||
gGoalMin = gMin + enhanceAmount * (gMin - gMid);
|
||
bMid = (bMax + bMin) * 0.5;
|
||
bGoalMax = bMax + enhanceAmount * (bMax - bMid);
|
||
bGoalMin = bMin + enhanceAmount * (bMin - bMid);
|
||
}
|
||
for (i = 0; i < nSubPixels; i += 4) {
|
||
data[i + 0] = remap(data[i + 0], rMin, rMax, rGoalMin, rGoalMax);
|
||
data[i + 1] = remap(data[i + 1], gMin, gMax, gGoalMin, gGoalMax);
|
||
data[i + 2] = remap(data[i + 2], bMin, bMax, bGoalMin, bGoalMax);
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 通过HSL调整图像
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} hue 色相 0~259
|
||
* @param {Object} saturation 饱和度 -2~10
|
||
* @param {Object} luminance 明暗 -2~2
|
||
*/
|
||
CRender.prototype.HSL = function(x, y, w, h, hue = 100, saturation = 5, luminance = 0) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var data = res.data,
|
||
nPixels = data.length,
|
||
v = 1,
|
||
s = Math.pow(2, saturation),
|
||
h = Math.abs(hue + 360) % 360,
|
||
l = luminance * 127,
|
||
i;
|
||
var vsu = v * s * Math.cos((h * Math.PI) / 180),
|
||
vsw = v * s * Math.sin((h * Math.PI) / 180);
|
||
var rr = 0.299 * v + 0.701 * vsu + 0.167 * vsw,
|
||
rg = 0.587 * v - 0.587 * vsu + 0.33 * vsw,
|
||
rb = 0.114 * v - 0.114 * vsu - 0.497 * vsw;
|
||
var gr = 0.299 * v - 0.299 * vsu - 0.328 * vsw,
|
||
gg = 0.587 * v + 0.413 * vsu + 0.035 * vsw,
|
||
gb = 0.114 * v - 0.114 * vsu + 0.293 * vsw;
|
||
var br = 0.299 * v - 0.3 * vsu + 1.25 * vsw,
|
||
bg = 0.587 * v - 0.586 * vsu - 1.05 * vsw,
|
||
bb = 0.114 * v + 0.886 * vsu - 0.2 * vsw;
|
||
var r, g, b, a;
|
||
for (i = 0; i < nPixels; i += 4) {
|
||
r = data[i + 0];
|
||
g = data[i + 1];
|
||
b = data[i + 2];
|
||
a = data[i + 3];
|
||
data[i + 0] = rr * r + rg * g + rb * b + l;
|
||
data[i + 1] = gr * r + gg * g + gb * b + l;
|
||
data[i + 2] = br * r + bg * g + bb * b + l;
|
||
data[i + 3] = a;
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
/**
|
||
* 调整图像hsv
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} hue 色相 0~259
|
||
* @param {Object} saturation 饱和度 -2~10
|
||
* @param {Object} value 明暗 -2~2
|
||
*/
|
||
CRender.prototype.HSV = function(x, y, w, h, hue = 150, saturation = 0, value = 0) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var data = res.data,
|
||
nPixels = data.length,
|
||
v = Math.pow(2, value),
|
||
s = Math.pow(2, saturation),
|
||
h = Math.abs(hue + 360) % 360,
|
||
i;
|
||
var vsu = v * s * Math.cos((h * Math.PI) / 180),
|
||
vsw = v * s * Math.sin((h * Math.PI) / 180);
|
||
var rr = 0.299 * v + 0.701 * vsu + 0.167 * vsw,
|
||
rg = 0.587 * v - 0.587 * vsu + 0.33 * vsw,
|
||
rb = 0.114 * v - 0.114 * vsu - 0.497 * vsw;
|
||
var gr = 0.299 * v - 0.299 * vsu - 0.328 * vsw,
|
||
gg = 0.587 * v + 0.413 * vsu + 0.035 * vsw,
|
||
gb = 0.114 * v - 0.114 * vsu + 0.293 * vsw;
|
||
var br = 0.299 * v - 0.3 * vsu + 1.25 * vsw,
|
||
bg = 0.587 * v - 0.586 * vsu - 1.05 * vsw,
|
||
bb = 0.114 * v + 0.886 * vsu - 0.2 * vsw;
|
||
var r, g, b, a;
|
||
for (i = 0; i < nPixels; i += 4) {
|
||
r = data[i + 0];
|
||
g = data[i + 1];
|
||
b = data[i + 2];
|
||
a = data[i + 3];
|
||
data[i + 0] = rr * r + rg * g + rb * b;
|
||
data[i + 1] = gr * r + gg * g + gb * b;
|
||
data[i + 2] = br * r + bg * g + bb * b;
|
||
data[i + 3] = a;
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
/**
|
||
* 调整图像rgb通道
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} r
|
||
* @param {Object} g
|
||
* @param {Object} b
|
||
*/
|
||
CRender.prototype.RGB = function(x, y, w, h, r = 0, g = 100, b = 30) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var data = res.data,
|
||
nPixels = data.length,
|
||
red = r,
|
||
green = g,
|
||
blue = b,
|
||
i, brightness;
|
||
for (i = 0; i < nPixels; i += 4) {
|
||
brightness =
|
||
(0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]) / 255;
|
||
data[i] = brightness * red;
|
||
data[i + 1] = brightness * green;
|
||
data[i + 2] = brightness * blue;
|
||
data[i + 3] = data[i + 3];
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
/**
|
||
* 使图像色彩反相
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
*/
|
||
CRender.prototype.Invert = function(x, y, w, h) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var data = res.data,
|
||
len = data.length,
|
||
i;
|
||
for (i = 0; i < len; i += 4) {
|
||
data[i] = 255 - data[i];
|
||
data[i + 1] = 255 - data[i + 1];
|
||
data[i + 2] = 255 - data[i + 2];
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
function pixelAt(idata, x, y) {
|
||
var idx = (y * idata.width + x) * 4;
|
||
var d = [];
|
||
d.push(idata.data[idx++], idata.data[idx++], idata.data[idx++], idata.data[idx++]);
|
||
return d;
|
||
}
|
||
|
||
function rgbDistance(p1, p2) {
|
||
return Math.sqrt(Math.pow(p1[0] - p2[0], 2) +
|
||
Math.pow(p1[1] - p2[1], 2) +
|
||
Math.pow(p1[2] - p2[2], 2));
|
||
}
|
||
|
||
function rgbMean(pTab) {
|
||
var m = [0, 0, 0];
|
||
for (var i = 0; i < pTab.length; i++) {
|
||
m[0] += pTab[i][0];
|
||
m[1] += pTab[i][1];
|
||
m[2] += pTab[i][2];
|
||
}
|
||
m[0] /= pTab.length;
|
||
m[1] /= pTab.length;
|
||
m[2] /= pTab.length;
|
||
return m;
|
||
}
|
||
|
||
function backgroundMask(idata, threshold) {
|
||
var rgbv_no = pixelAt(idata, 0, 0);
|
||
var rgbv_ne = pixelAt(idata, idata.width - 1, 0);
|
||
var rgbv_so = pixelAt(idata, 0, idata.height - 1);
|
||
var rgbv_se = pixelAt(idata, idata.width - 1, idata.height - 1);
|
||
var thres = threshold || 10;
|
||
if (rgbDistance(rgbv_no, rgbv_ne) < thres &&
|
||
rgbDistance(rgbv_ne, rgbv_se) < thres &&
|
||
rgbDistance(rgbv_se, rgbv_so) < thres &&
|
||
rgbDistance(rgbv_so, rgbv_no) < thres) {
|
||
var mean = rgbMean([rgbv_ne, rgbv_no, rgbv_se, rgbv_so]);
|
||
var mask = [];
|
||
for (var i = 0; i < idata.width * idata.height; i++) {
|
||
var d = rgbDistance(mean, [
|
||
idata.data[i * 4],
|
||
idata.data[i * 4 + 1],
|
||
idata.data[i * 4 + 2],
|
||
]);
|
||
mask[i] = d < thres ? 0 : 255;
|
||
}
|
||
return mask;
|
||
}
|
||
}
|
||
|
||
function applyMask(idata, mask) {
|
||
for (var i = 0; i < idata.width * idata.height; i++) {
|
||
idata.data[4 * i + 3] = mask[i];
|
||
}
|
||
}
|
||
|
||
function erodeMask(mask, sw, sh) {
|
||
var weights = [1, 1, 1, 1, 0, 1, 1, 1, 1];
|
||
var side = Math.round(Math.sqrt(weights.length));
|
||
var halfSide = Math.floor(side / 2);
|
||
var maskResult = [];
|
||
for (var y = 0; y < sh; y++) {
|
||
for (var x = 0; x < sw; x++) {
|
||
var so = y * sw + x;
|
||
var a = 0;
|
||
for (var cy = 0; cy < side; cy++) {
|
||
for (var cx = 0; cx < side; cx++) {
|
||
var scy = y + cy - halfSide;
|
||
var scx = x + cx - halfSide;
|
||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||
var srcOff = scy * sw + scx;
|
||
var wt = weights[cy * side + cx];
|
||
a += mask[srcOff] * wt;
|
||
}
|
||
}
|
||
}
|
||
maskResult[so] = a === 255 * 8 ? 255 : 0;
|
||
}
|
||
}
|
||
return maskResult;
|
||
}
|
||
|
||
function dilateMask(mask, sw, sh) {
|
||
var weights = [1, 1, 1, 1, 1, 1, 1, 1, 1];
|
||
var side = Math.round(Math.sqrt(weights.length));
|
||
var halfSide = Math.floor(side / 2);
|
||
var maskResult = [];
|
||
for (var y = 0; y < sh; y++) {
|
||
for (var x = 0; x < sw; x++) {
|
||
var so = y * sw + x;
|
||
var a = 0;
|
||
for (var cy = 0; cy < side; cy++) {
|
||
for (var cx = 0; cx < side; cx++) {
|
||
var scy = y + cy - halfSide;
|
||
var scx = x + cx - halfSide;
|
||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||
var srcOff = scy * sw + scx;
|
||
var wt = weights[cy * side + cx];
|
||
a += mask[srcOff] * wt;
|
||
}
|
||
}
|
||
}
|
||
maskResult[so] = a >= 255 * 4 ? 255 : 0;
|
||
}
|
||
}
|
||
return maskResult;
|
||
}
|
||
|
||
function smoothEdgeMask(mask, sw, sh) {
|
||
var weights = [1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9];
|
||
var side = Math.round(Math.sqrt(weights.length));
|
||
var halfSide = Math.floor(side / 2);
|
||
var maskResult = [];
|
||
for (var y = 0; y < sh; y++) {
|
||
for (var x = 0; x < sw; x++) {
|
||
var so = y * sw + x;
|
||
var a = 0;
|
||
for (var cy = 0; cy < side; cy++) {
|
||
for (var cx = 0; cx < side; cx++) {
|
||
var scy = y + cy - halfSide;
|
||
var scx = x + cx - halfSide;
|
||
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
|
||
var srcOff = scy * sw + scx;
|
||
var wt = weights[cy * side + cx];
|
||
a += mask[srcOff] * wt;
|
||
}
|
||
}
|
||
}
|
||
maskResult[so] = a;
|
||
}
|
||
}
|
||
return maskResult;
|
||
}
|
||
|
||
/**
|
||
* 遮罩
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} threshold 0~300遮罩强度
|
||
*/
|
||
CRender.prototype.Mask = function(x, y, w, h, threshold = 100) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var imageData = {
|
||
...res
|
||
}
|
||
|
||
var mask = backgroundMask(imageData, threshold);
|
||
if (mask) {
|
||
mask = erodeMask(mask, imageData.width, imageData.height);
|
||
mask = dilateMask(mask, imageData.width, imageData.height);
|
||
mask = smoothEdgeMask(mask, imageData.width, imageData.height);
|
||
applyMask(imageData, mask);
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: imageData.data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
/**
|
||
* 图像噪点
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} Noise 0~4
|
||
*/
|
||
CRender.prototype.Noise = function(x, y, w, h, noise = 0.5) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
var amount = noise * 255,
|
||
data = res.data,
|
||
nPixels = data.length,
|
||
half = amount / 2,
|
||
i;
|
||
for (i = 0; i < nPixels; i += 4) {
|
||
data[i + 0] += half - 2 * half * Math.random();
|
||
data[i + 1] += half - 2 * half * Math.random();
|
||
data[i + 2] += half - 2 * half * Math.random();
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
/**
|
||
* 图像像素化
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
* @param {Object} size 0~20
|
||
*/
|
||
CRender.prototype.Pixelate = function(x, y, w, h, size = 10) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
|
||
var pixelSize = Math.ceil(size),
|
||
width = res.width,
|
||
height = res.height,
|
||
x, y, i, red, green, blue, alpha, nBinsX = Math.ceil(width / pixelSize),
|
||
nBinsY = Math.ceil(height / pixelSize),
|
||
xBinStart, xBinEnd, yBinStart, yBinEnd, xBin, yBin, pixelsInBin, data = res.data;
|
||
if (pixelSize <= 0) {
|
||
return new Promise.reject("像素点不能小于0");
|
||
}
|
||
for (xBin = 0; xBin < nBinsX; xBin += 1) {
|
||
for (yBin = 0; yBin < nBinsY; yBin += 1) {
|
||
red = 0;
|
||
green = 0;
|
||
blue = 0;
|
||
alpha = 0;
|
||
xBinStart = xBin * pixelSize;
|
||
xBinEnd = xBinStart + pixelSize;
|
||
yBinStart = yBin * pixelSize;
|
||
yBinEnd = yBinStart + pixelSize;
|
||
pixelsInBin = 0;
|
||
for (x = xBinStart; x < xBinEnd; x += 1) {
|
||
if (x >= width) {
|
||
continue;
|
||
}
|
||
for (y = yBinStart; y < yBinEnd; y += 1) {
|
||
if (y >= height) {
|
||
continue;
|
||
}
|
||
i = (width * y + x) * 4;
|
||
red += data[i + 0];
|
||
green += data[i + 1];
|
||
blue += data[i + 2];
|
||
alpha += data[i + 3];
|
||
pixelsInBin += 1;
|
||
}
|
||
}
|
||
red = red / pixelsInBin;
|
||
green = green / pixelsInBin;
|
||
blue = blue / pixelsInBin;
|
||
alpha = alpha / pixelsInBin;
|
||
for (x = xBinStart; x < xBinEnd; x += 1) {
|
||
if (x >= width) {
|
||
continue;
|
||
}
|
||
for (y = yBinStart; y < yBinEnd; y += 1) {
|
||
if (y >= height) {
|
||
continue;
|
||
}
|
||
i = (width * y + x) * 4;
|
||
data[i + 0] = red;
|
||
data[i + 1] = green;
|
||
data[i + 2] = blue;
|
||
data[i + 3] = alpha;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
/**
|
||
* 褐色风格处理
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
*/
|
||
CRender.prototype.Sepia = function(x, y, w, h) {
|
||
let t = this;
|
||
return this.getImageData(x, y, w, h).then((res) => {
|
||
|
||
var d = res.data;
|
||
|
||
for (var i = 0; i < d.length; i += 4) {
|
||
var red = d[i];
|
||
var green = d[i + 1];
|
||
var blue = d[i + 2];
|
||
var alpha = d[i + 3];
|
||
|
||
var outRed = (red * .393) + (green * .769) + (blue *
|
||
.189); // calculate value for red channel in pixel
|
||
var outGreen = (red * .349) + (green * .686) + (blue * .168);
|
||
var outBlue = (red * .272) + (green * .534) + (blue * .131);
|
||
|
||
d[i] = outRed < 255 ? outRed :
|
||
255; // check if the value is less than 255, if more set it to 255
|
||
d[i + 1] = outGreen < 255 ? outGreen : 255;
|
||
d[i + 2] = outBlue < 255 ? outBlue : 255
|
||
d[i + 3] = alpha;
|
||
}
|
||
|
||
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: d
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 水平翻转图像
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
*/
|
||
CRender.prototype.HorizontalFlip = function(x, y, w, h) {
|
||
let t = this;
|
||
let dpr = 1;
|
||
// #ifndef H5
|
||
dpr = t.ctx.dpr;
|
||
// #endif
|
||
return this.getImageData(x, y, w, h).then(async (res) => {
|
||
// 1.获取通信信息
|
||
let imgdata = res;
|
||
let middleAxle /*中轴*/ = (w * dpr * 4) / 2;
|
||
// 2.遍历行
|
||
for (let curRow = 0; curRow < h * dpr; curRow++) {
|
||
let aisleStart /*每行开始的通道位置*/ = curRow * w * dpr * 4,
|
||
aisleEnd /*每行结束的通道位置*/ = (curRow + 1) * w * dpr * 4 - 4,
|
||
curMiddleAxle /*每一行中轴所在的位置*/ = aisleEnd - middleAxle;
|
||
|
||
// 3.遍历当前行的列作为内循环,把列的左边像素按照轴对称和右边的像素互换
|
||
for (; aisleStart <= curMiddleAxle; aisleStart += 4, aisleEnd -= 4) {
|
||
// 临时存放
|
||
let tr = imgdata.data[aisleStart],
|
||
tg = imgdata.data[aisleStart + 1],
|
||
tb = imgdata.data[aisleStart + 2],
|
||
ta = imgdata.data[aisleStart + 3];
|
||
|
||
imgdata.data[aisleStart] = imgdata.data[aisleEnd];
|
||
imgdata.data[aisleStart + 1] = imgdata.data[aisleEnd + 1];
|
||
imgdata.data[aisleStart + 2] = imgdata.data[aisleEnd + 2];
|
||
imgdata.data[aisleStart + 3] = imgdata.data[aisleEnd + 3];
|
||
|
||
imgdata.data[aisleEnd] = tr;
|
||
imgdata.data[aisleEnd + 1] = tg;
|
||
imgdata.data[aisleEnd + 2] = tb;
|
||
imgdata.data[aisleEnd + 3] = ta;
|
||
}
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: imgdata.data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 垂直翻转图像
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
*/
|
||
CRender.prototype.VerticallyFlip = function(x, y, w, h) {
|
||
let t = this;
|
||
let dpr = 1;
|
||
// #ifndef H5
|
||
dpr = t.ctx.dpr;
|
||
// #endif
|
||
return this.getImageData(x, y, w, h).then(async (res) => {
|
||
// 1.获取图像信息
|
||
let imgdata = res;
|
||
let middleAxle /*中轴*/ = Math.floor(h / 2),
|
||
rowAisles = w * 4;
|
||
|
||
// 2.遍历总行数一半的每一行作为外循环(向下取整)
|
||
for (var curRow = 0; curRow < middleAxle; curRow++) {
|
||
//
|
||
let aisleStart /*开始的通道位置*/ = curRow * rowAisles,
|
||
mirrorStart /*中轴对称的开始位置*/ = (h - curRow - 1) * rowAisles;
|
||
|
||
// 3.遍历当前行的列作为内循环,把列的每个通道按照水平轴对称和镜像里的通道互换
|
||
for (; aisleStart < rowAisles * (curRow + 1); aisleStart += 4, mirrorStart += 4) {
|
||
var tr = imgdata.data[aisleStart],
|
||
tg = imgdata.data[aisleStart + 1],
|
||
tb = imgdata.data[aisleStart + 2],
|
||
ta = imgdata.data[aisleStart + 3];
|
||
|
||
imgdata.data[aisleStart] = imgdata.data[mirrorStart];
|
||
imgdata.data[aisleStart + 1] = imgdata.data[mirrorStart + 1];
|
||
imgdata.data[aisleStart + 2] = imgdata.data[mirrorStart + 2];
|
||
imgdata.data[aisleStart + 3] = imgdata.data[mirrorStart + 3];
|
||
|
||
imgdata.data[mirrorStart] = tr;
|
||
imgdata.data[mirrorStart + 1] = tg;
|
||
imgdata.data[mirrorStart + 2] = tb;
|
||
imgdata.data[mirrorStart + 3] = ta;
|
||
}
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: imgdata.data
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 水平镜像,左右居中对称图像
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
*/
|
||
CRender.prototype.HorizontalMirror = function(x, y, w, h) {
|
||
let t = this;
|
||
let dpr = 1;
|
||
// #ifndef H5
|
||
dpr = t.ctx.dpr;
|
||
// #endif
|
||
return this.getImageData(x, y, w, h).then(async (res) => {
|
||
var output =res;
|
||
var w = res.width;
|
||
var h = res.height;
|
||
var dst = output.data;
|
||
var d = res.data;
|
||
for (var y=0; y<h; y++) {
|
||
for (var x=0; x<w; x++) {
|
||
var off = (y*w+x)*4;
|
||
var dstOff = (y*w+(w-x-1))*4;
|
||
dst[dstOff] = d[off];
|
||
dst[dstOff+1] = d[off+1];
|
||
dst[dstOff+2] = d[off+2];
|
||
dst[dstOff+3] = d[off+3];
|
||
}
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: dst
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
}
|
||
/**
|
||
* 垂直镜像,上下居中对称图像
|
||
* @param {Object} x
|
||
* @param {Object} y
|
||
* @param {Object} w
|
||
* @param {Object} h
|
||
*/
|
||
CRender.prototype.VerticallyMirror = function(x, y, w, h) {
|
||
let t = this;
|
||
let dpr = 1;
|
||
// #ifndef H5
|
||
dpr = t.ctx.dpr;
|
||
// #endif
|
||
return this.getImageData(x, y, w, h).then(async (res) => {
|
||
var output =res;
|
||
var w = res.width;
|
||
var h = res.height;
|
||
var dst = output.data;
|
||
var d = res.data;
|
||
for (var y=0; y<h; y++) {
|
||
for (var x=0; x<w; x++) {
|
||
var off = (y*w+x)*4;
|
||
var dstOff = ((h-y-1)*w+x)*4;
|
||
dst[dstOff] = d[off];
|
||
dst[dstOff+1] = d[off+1];
|
||
dst[dstOff+2] = d[off+2];
|
||
dst[dstOff+3] = d[off+3];
|
||
}
|
||
}
|
||
return {
|
||
width: res.width,
|
||
height: res.height,
|
||
data: dst
|
||
};
|
||
}).then(res => {
|
||
return this.putImageData(x, y, w, h, res);
|
||
}).catch(e => console.error(e))
|
||
|
||
|
||
}
|