crender.class.js 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151
  1. import color from '../color'
  2. import bezierCurve from '../bezier-curve'
  3. import {
  4. deepClone
  5. } from '../plugin/util'
  6. import allGraph from '../config/graphs'
  7. import Graph from './graph.class'
  8. /**
  9. * @description Class of CRender
  10. * @param {Object} canvas Canvas DOM
  11. * @return {CRender} Instance of CRender
  12. */
  13. export default class CRender {
  14. constructor(canvas, t, cav) {
  15. if (!canvas) {
  16. console.error('CRender Missing parameters!')
  17. return
  18. }
  19. if (cav) {
  20. this.cav = cav;
  21. } else {
  22. this.cav = null;
  23. }
  24. const ctx = canvas;
  25. this.t = t;
  26. // const { clientWidth, clientHeight } = canvas
  27. const area = [ctx.width, ctx.height]
  28. // canvas.setAttribute('width', clientWidth)
  29. // canvas.setAttribute('height', clientHeight)
  30. /**
  31. * @description Context of the canvas
  32. * @type {Object}
  33. * @example ctx = canvas.getContext('2d')
  34. */
  35. this.ctx = ctx
  36. /**
  37. * @description Width and height of the canvas
  38. * @type {Array}
  39. * @example area = [300,100]
  40. */
  41. this.area = area
  42. /**
  43. * @description Whether render is in animation rendering
  44. * @type {Boolean}
  45. * @example animationStatus = true|false
  46. */
  47. this.animationStatus = false
  48. /**
  49. * @description Added graph
  50. * @type {[Graph]}
  51. * @example graphs = [Graph, Graph, ...]
  52. */
  53. this.graphs = []
  54. /**
  55. * @description Color plugin
  56. * @type {Object}
  57. * @link https://github.com/jiaming743/color
  58. */
  59. this.color = color
  60. /**
  61. * @description Bezier Curve plugin
  62. * @type {Object}
  63. * @link https://github.com/jiaming743/BezierCurve
  64. */
  65. this.bezierCurve = bezierCurve
  66. // t.mousedown = mouseDown.bind(this);
  67. // bind event handler
  68. // canvas.addEventListener('mousedown', mouseDown.bind(this))
  69. // canvas.addEventListener('mousemove', mouseMove.bind(this))
  70. // canvas.addEventListener('mouseup', mouseUp.bind(this))
  71. // console.log(canvas);
  72. }
  73. }
  74. /**
  75. * @description Clear canvas drawing area
  76. * @return {Undefined} Void
  77. */
  78. CRender.prototype.clearArea = function() {
  79. const {
  80. area
  81. } = this
  82. this.ctx.clearRect(0, 0, ...area)
  83. }
  84. /**
  85. * @description Add graph to render
  86. * @param {Object} config Graph configuration
  87. * @return {Graph} Graph instance
  88. */
  89. CRender.prototype.add = function(config = {}) {
  90. const {
  91. name
  92. } = config
  93. if (!name) {
  94. console.error('add Missing parameters!')
  95. return
  96. }
  97. const graphConfig = allGraph.get(name)
  98. if (!graphConfig) {
  99. console.warn('No corresponding graph configuration found!')
  100. return
  101. }
  102. const graph = new Graph(graphConfig, config)
  103. if (!graph.validator(graph, this.ctx)) return
  104. graph.render = this
  105. this.graphs.push(graph)
  106. this.sortGraphsByIndex()
  107. this.drawAllGraph()
  108. return graph
  109. }
  110. /**
  111. * @description Sort the graph by index
  112. * @return {Undefined} Void
  113. */
  114. CRender.prototype.sortGraphsByIndex = function() {
  115. const {
  116. graphs
  117. } = this
  118. graphs.sort((a, b) => {
  119. if (a.index > b.index) return 1
  120. if (a.index === b.index) return 0
  121. if (a.index < b.index) return -1
  122. })
  123. }
  124. /**
  125. * @description Delete graph in render
  126. * @param {Graph} graph The graph to be deleted
  127. * @return {Undefined} Void
  128. */
  129. CRender.prototype.delGraph = function(graph) {
  130. if (typeof graph.delProcessor !== 'function') return
  131. graph.delProcessor(this)
  132. this.graphs = this.graphs.filter(graph => graph)
  133. this.drawAllGraph()
  134. }
  135. /**
  136. * @description Delete all graph in render
  137. * @return {Undefined} Void
  138. */
  139. CRender.prototype.delAllGraph = function() {
  140. this.graphs.forEach(graph => graph.delProcessor(this))
  141. this.graphs = this.graphs.filter(graph => graph)
  142. this.drawAllGraph()
  143. }
  144. /**
  145. * @description Draw all the graphs in the render
  146. * @return {Undefined} Void
  147. */
  148. CRender.prototype.drawAllGraph = function() {
  149. this.clearArea()
  150. this.graphs.filter(graph => graph && graph.visible).forEach(graph => graph.drawProcessor(this, graph))
  151. if (this.ctx?.draw) {
  152. this.ctx.draw(true)
  153. }
  154. }
  155. /**
  156. * @description Animate the graph whose animation queue is not empty
  157. * and the animationPause is equal to false
  158. * @return {Promise} Animation Promise
  159. */
  160. CRender.prototype.launchAnimation = function() {
  161. const {
  162. animationStatus
  163. } = this
  164. if (animationStatus) return
  165. this.animationStatus = true
  166. return new Promise(resolve => {
  167. animation.call(this, () => {
  168. this.animationStatus = false
  169. resolve()
  170. }, Date.now())
  171. })
  172. }
  173. function requestAnimationFrame(callback) {
  174. // var now = Date.now();
  175. // var lastTime = 0;
  176. // var nextTime = Math.max(lastTime + 16, now);
  177. // // var idd=555
  178. // // clearTimeout(idd)
  179. // return new Promise(resolve=>setTimeout(function() { callback(resolve(lastTime = nextTime)); },nextTime - now));
  180. let lastFrameTime = 0
  181. var currTime = new Date().getTime();
  182. var timeToCall = Math.max(0, 16 - (currTime - lastFrameTime));
  183. var id = setTimeout(function() {
  184. callback(currTime + timeToCall);
  185. }, timeToCall);
  186. lastFrameTime = currTime + timeToCall;
  187. return id;
  188. };
  189. /**
  190. * @description Try to animate every graph
  191. * @param {Function} callback Callback in animation end
  192. * @param {Number} timeStamp Time stamp of animation start
  193. * @return {Undefined} Void
  194. */
  195. function animation(callback, timeStamp) {
  196. const {
  197. graphs
  198. } = this
  199. if (!animationAble(graphs)) {
  200. callback()
  201. return
  202. }
  203. graphs.forEach(graph => graph.turnNextAnimationFrame(timeStamp))
  204. this.drawAllGraph()
  205. // #ifdef H5
  206. window.requestAnimationFrame(animation.bind(this, callback, timeStamp));
  207. // #endif
  208. // #ifndef H5
  209. if (this.cav) {
  210. this.cav.requestAnimationFrame(animation.bind(this, callback, timeStamp));
  211. } else {
  212. requestAnimationFrame(animation.bind(this, callback, timeStamp))
  213. }
  214. // #endif
  215. }
  216. /**
  217. * @description Find if there are graph that can be animated
  218. * @param {[Graph]} graphs
  219. * @return {Boolean}
  220. */
  221. function animationAble(graphs) {
  222. return graphs.find(graph => !graph.animationPause && graph.animationFrameState.length)
  223. }
  224. CRender.prototype.animateAble = function(graphs) {
  225. return this.graphs.find(graph => !graph.animationPause && graph.animationFrameState.length)
  226. }
  227. /**
  228. * @description Handler of CRender mousedown event
  229. * @return {Undefined} Void
  230. */
  231. function mouseDown(e) {
  232. const {
  233. graphs
  234. } = this
  235. const hoverGraph = graphs.find(graph => graph.status === 'hover')
  236. if (!hoverGraph) return
  237. hoverGraph.status = 'active'
  238. }
  239. /**
  240. * @description Handler of CRender mousemove event
  241. * @return {Undefined} Void
  242. */
  243. function mouseMove(e) {
  244. const {
  245. offsetX,
  246. offsetY
  247. } = e
  248. const position = [offsetX, offsetY]
  249. const {
  250. graphs
  251. } = this
  252. const activeGraph = graphs.find(graph => (graph.status === 'active' || graph.status === 'drag'))
  253. if (activeGraph) {
  254. if (!activeGraph.drag) return
  255. if (typeof activeGraph.move !== 'function') {
  256. console.error('No move method is provided, cannot be dragged!')
  257. return
  258. }
  259. activeGraph.moveProcessor(e)
  260. activeGraph.status = 'drag'
  261. return
  262. }
  263. const hoverGraph = graphs.find(graph => graph.status === 'hover')
  264. const hoverAbleGraphs = graphs.filter(graph =>
  265. (graph.hover && (typeof graph.hoverCheck === 'function' || graph.hoverRect)))
  266. const hoveredGraph = hoverAbleGraphs.find(graph => graph.hoverCheckProcessor(position, graph))
  267. if (hoveredGraph) {
  268. document.body.style.cursor = hoveredGraph.style.hoverCursor
  269. } else {
  270. document.body.style.cursor = 'default'
  271. }
  272. let [hoverGraphMouseOuterIsFun, hoveredGraphMouseEnterIsFun] = [false, false]
  273. if (hoverGraph) hoverGraphMouseOuterIsFun = typeof hoverGraph.mouseOuter === 'function'
  274. if (hoveredGraph) hoveredGraphMouseEnterIsFun = typeof hoveredGraph.mouseEnter === 'function'
  275. if (!hoveredGraph && !hoverGraph) return
  276. if (!hoveredGraph && hoverGraph) {
  277. if (hoverGraphMouseOuterIsFun) hoverGraph.mouseOuter(e, hoverGraph)
  278. hoverGraph.status = 'static'
  279. return
  280. }
  281. if (hoveredGraph && hoveredGraph === hoverGraph) return
  282. if (hoveredGraph && !hoverGraph) {
  283. if (hoveredGraphMouseEnterIsFun) hoveredGraph.mouseEnter(e, hoveredGraph)
  284. hoveredGraph.status = 'hover'
  285. return
  286. }
  287. if (hoveredGraph && hoverGraph && hoveredGraph !== hoverGraph) {
  288. if (hoverGraphMouseOuterIsFun) hoverGraph.mouseOuter(e, hoverGraph)
  289. hoverGraph.status = 'static'
  290. if (hoveredGraphMouseEnterIsFun) hoveredGraph.mouseEnter(e, hoveredGraph)
  291. hoveredGraph.status = 'hover'
  292. }
  293. }
  294. /**
  295. * @description Handler of CRender mouseup event
  296. * @return {Undefined} Void
  297. */
  298. function mouseUp(e) {
  299. const {
  300. graphs
  301. } = this
  302. const activeGraph = graphs.find(graph => graph.status === 'active')
  303. const dragGraph = graphs.find(graph => graph.status === 'drag')
  304. if (activeGraph && typeof activeGraph.click === 'function') activeGraph.click(e, activeGraph)
  305. graphs.forEach(graph => graph && (graph.status = 'static'))
  306. if (activeGraph) activeGraph.status = 'hover'
  307. if (dragGraph) dragGraph.status = 'hover'
  308. }
  309. /**
  310. * @description Clone Graph
  311. * @param {Graph} graph The target to be cloned
  312. * @return {Graph} Cloned graph
  313. */
  314. CRender.prototype.clone = function(graph) {
  315. const style = graph.style.getStyle()
  316. let clonedGraph = {
  317. ...graph,
  318. style
  319. }
  320. delete clonedGraph.render
  321. clonedGraph = deepClone(clonedGraph, true)
  322. return this.add(clonedGraph)
  323. }
  324. function putImageData(ctx, imageData, dx, dy,
  325. dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
  326. var data = imageData.data;
  327. var height = imageData.height;
  328. var width = imageData.width;
  329. dirtyX = dirtyX || 0;
  330. dirtyY = dirtyY || 0;
  331. dirtyWidth = dirtyWidth !== undefined ? dirtyWidth : width;
  332. dirtyHeight = dirtyHeight !== undefined ? dirtyHeight : height;
  333. var limitBottom = dirtyY + dirtyHeight;
  334. var limitRight = dirtyX + dirtyWidth;
  335. ctx.save()
  336. ctx.scale(ctx.scaledpr, ctx.scaledpr)
  337. for (var y = dirtyY; y < limitBottom; y++) {
  338. for (var x = dirtyX; x < limitRight; x++) {
  339. var pos = y * width + x;
  340. ctx.fillStyle = 'rgba(' + data[pos * 4 + 0] +
  341. ',' + data[pos * 4 + 1] +
  342. ',' + data[pos * 4 + 2] +
  343. ',' + (data[pos * 4 + 3] / 255) + ')';
  344. ctx.fillRect(x + dx, y + dy, 1, 1);
  345. }
  346. }
  347. ctx.restore()
  348. }
  349. CRender.prototype.getImageData = function(x, y, w, h) {
  350. let t = this;
  351. let dpr = 1;
  352. // #ifndef H5
  353. dpr = t.ctx.dpr;
  354. console.log(dpr);
  355. // #endif
  356. return new Promise((rs, rj) => {
  357. if (t.ctx.getImageData) {
  358. var data = t.ctx.getImageData(x, y, w * dpr, h * dpr)
  359. rs(data);
  360. } else {
  361. // #ifdef APP-VUE || APP-PLUS
  362. uni.canvasGetImageData({
  363. canvasId: t.ctx.id,
  364. x: x,
  365. y: y,
  366. width: w,
  367. height: h,
  368. success: (res) => rs(res),
  369. fail: (e) => rj(e)
  370. }, t.t)
  371. // #endif
  372. // #ifndef APP-VUE || APP-PLUS
  373. uni.canvasGetImageData({
  374. canvasId: t.ctx.id,
  375. x: x,
  376. y: y,
  377. width: w,
  378. height: h,
  379. success: (res) => rs(res),
  380. fail: (e) => rj(e)
  381. }, t.t)
  382. // #endif
  383. }
  384. })
  385. }
  386. CRender.prototype.putImageData = function(x, y, w, h, data) {
  387. let t = this;
  388. let dpr = 1;
  389. // #ifndef H5
  390. dpr = t.ctx.dpr;
  391. // #endif
  392. t.ctx.clearRect(0, 0, w, h);
  393. uni.showLoading({
  394. title: '...',
  395. mask: true
  396. })
  397. return new Promise((rs, rj) => {
  398. if (t.ctx.putImageData) {
  399. setTimeout(function() {
  400. putImageData(t.ctx, data, x, y, 0, 0, w * dpr, h * dpr);
  401. rs();
  402. uni.hideLoading()
  403. }, 50);
  404. } else {
  405. uni.canvasPutImageData({
  406. canvasId: t.ctx.id,
  407. x: x,
  408. y: y,
  409. width: w,
  410. height: h,
  411. data: data.data,
  412. success: (res) => rs(),
  413. fail: (e) => rj(e),
  414. complete: () => uni.hideLoading()
  415. }, t.t)
  416. }
  417. })
  418. }
  419. // Filters图像效果控制。
  420. /**
  421. * 移植自https://konvajs.org/
  422. * 移植作者:https://jx2d.cn
  423. */
  424. /**
  425. * 模糊图片
  426. * @param {Object} x
  427. * @param {Object} y
  428. * @param {Object} w
  429. * @param {Object} h
  430. * @param {Object} blurRadius
  431. * @param {Object} type = rgba|rgb
  432. */
  433. CRender.prototype.Blur = function(x, y, w, h, blurRadius, type = 'rgba') {
  434. var t = this;
  435. /*
  436. StackBlur - a fast almost Gaussian Blur For Canvas
  437. Version: 0.5
  438. Author: Mario Klingemann
  439. Contact: mario@quasimondo.com
  440. Website: http://www.quasimondo.com/StackBlurForCanvas
  441. Twitter: @quasimondo
  442. In case you find this class useful - especially in commercial projects -
  443. I am not totally unhappy for a small donation to my PayPal account
  444. mario@quasimondo.de
  445. Or support me on flattr:
  446. https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
  447. Copyright (c) 2010 Mario Klingemann
  448. Permission is hereby granted, free of charge, to any person
  449. obtaining a copy of this software and associated documentation
  450. files (the "Software"), to deal in the Software without
  451. restriction, including without limitation the rights to use,
  452. copy, modify, merge, publish, distribute, sublicense, and/or sell
  453. copies of the Software, and to permit persons to whom the
  454. Software is furnished to do so, subject to the following
  455. conditions:
  456. The above copyright notice and this permission notice shall be
  457. included in all copies or substantial portions of the Software.
  458. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  459. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  460. OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  461. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  462. HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  463. WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  464. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  465. OTHER DEALINGS IN THE SOFTWARE.
  466. */
  467. let dpr = 1;
  468. // #ifndef H5
  469. dpr = t.ctx.dpr;
  470. // #endif
  471. var mul_table = [
  472. 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
  473. 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
  474. 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
  475. 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
  476. 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
  477. 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
  478. 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
  479. 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
  480. 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
  481. 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
  482. 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
  483. 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
  484. 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
  485. 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
  486. 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
  487. 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
  488. ];
  489. var shg_table = [
  490. 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
  491. 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
  492. 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
  493. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
  494. 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
  495. 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
  496. 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
  497. 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
  498. 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  499. 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  500. 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
  501. 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  502. 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  503. 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  504. 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  505. 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
  506. ];
  507. function stackBlurCanvasRGBA(imageData, top_x, top_y, width, height, radius) {
  508. var pixels = imageData.data;
  509. var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum,
  510. r_out_sum, g_out_sum, b_out_sum, a_out_sum,
  511. r_in_sum, g_in_sum, b_in_sum, a_in_sum,
  512. pr, pg, pb, pa, rbs;
  513. var div = radius + radius + 1;
  514. var w4 = width << 2;
  515. var widthMinus1 = width - 1;
  516. var heightMinus1 = height - 1;
  517. var radiusPlus1 = radius + 1;
  518. var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;
  519. var stackStart = new BlurStack();
  520. var stack = stackStart;
  521. for (i = 1; i < div; i++) {
  522. stack = stack.next = new BlurStack();
  523. if (i == radiusPlus1) var stackEnd = stack;
  524. }
  525. stack.next = stackStart;
  526. var stackIn = null;
  527. var stackOut = null;
  528. yw = yi = 0;
  529. var mul_sum = mul_table[radius];
  530. var shg_sum = shg_table[radius];
  531. for (y = 0; y < height; y++) {
  532. r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0;
  533. r_out_sum = radiusPlus1 * (pr = pixels[yi]);
  534. g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
  535. b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
  536. a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]);
  537. r_sum += sumFactor * pr;
  538. g_sum += sumFactor * pg;
  539. b_sum += sumFactor * pb;
  540. a_sum += sumFactor * pa;
  541. stack = stackStart;
  542. for (i = 0; i < radiusPlus1; i++) {
  543. stack.r = pr;
  544. stack.g = pg;
  545. stack.b = pb;
  546. stack.a = pa;
  547. stack = stack.next;
  548. }
  549. for (i = 1; i < radiusPlus1; i++) {
  550. p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
  551. r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i);
  552. g_sum += (stack.g = (pg = pixels[p + 1])) * rbs;
  553. b_sum += (stack.b = (pb = pixels[p + 2])) * rbs;
  554. a_sum += (stack.a = (pa = pixels[p + 3])) * rbs;
  555. r_in_sum += pr;
  556. g_in_sum += pg;
  557. b_in_sum += pb;
  558. a_in_sum += pa;
  559. stack = stack.next;
  560. }
  561. stackIn = stackStart;
  562. stackOut = stackEnd;
  563. for (x = 0; x < width; x++) {
  564. pixels[yi + 3] = pa = (a_sum * mul_sum) >> shg_sum;
  565. if (pa != 0) {
  566. pa = 255 / pa;
  567. pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa;
  568. pixels[yi + 1] = ((g_sum * mul_sum) >> shg_sum) * pa;
  569. pixels[yi + 2] = ((b_sum * mul_sum) >> shg_sum) * pa;
  570. } else {
  571. pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0;
  572. }
  573. r_sum -= r_out_sum;
  574. g_sum -= g_out_sum;
  575. b_sum -= b_out_sum;
  576. a_sum -= a_out_sum;
  577. r_out_sum -= stackIn.r;
  578. g_out_sum -= stackIn.g;
  579. b_out_sum -= stackIn.b;
  580. a_out_sum -= stackIn.a;
  581. p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;
  582. r_in_sum += (stackIn.r = pixels[p]);
  583. g_in_sum += (stackIn.g = pixels[p + 1]);
  584. b_in_sum += (stackIn.b = pixels[p + 2]);
  585. a_in_sum += (stackIn.a = pixels[p + 3]);
  586. r_sum += r_in_sum;
  587. g_sum += g_in_sum;
  588. b_sum += b_in_sum;
  589. a_sum += a_in_sum;
  590. stackIn = stackIn.next;
  591. r_out_sum += (pr = stackOut.r);
  592. g_out_sum += (pg = stackOut.g);
  593. b_out_sum += (pb = stackOut.b);
  594. a_out_sum += (pa = stackOut.a);
  595. r_in_sum -= pr;
  596. g_in_sum -= pg;
  597. b_in_sum -= pb;
  598. a_in_sum -= pa;
  599. stackOut = stackOut.next;
  600. yi += 4;
  601. }
  602. yw += width;
  603. }
  604. for (x = 0; x < width; x++) {
  605. g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0;
  606. yi = x << 2;
  607. r_out_sum = radiusPlus1 * (pr = pixels[yi]);
  608. g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
  609. b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
  610. a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]);
  611. r_sum += sumFactor * pr;
  612. g_sum += sumFactor * pg;
  613. b_sum += sumFactor * pb;
  614. a_sum += sumFactor * pa;
  615. stack = stackStart;
  616. for (i = 0; i < radiusPlus1; i++) {
  617. stack.r = pr;
  618. stack.g = pg;
  619. stack.b = pb;
  620. stack.a = pa;
  621. stack = stack.next;
  622. }
  623. yp = width;
  624. for (i = 1; i <= radius; i++) {
  625. yi = (yp + x) << 2;
  626. r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i);
  627. g_sum += (stack.g = (pg = pixels[yi + 1])) * rbs;
  628. b_sum += (stack.b = (pb = pixels[yi + 2])) * rbs;
  629. a_sum += (stack.a = (pa = pixels[yi + 3])) * rbs;
  630. r_in_sum += pr;
  631. g_in_sum += pg;
  632. b_in_sum += pb;
  633. a_in_sum += pa;
  634. stack = stack.next;
  635. if (i < heightMinus1) {
  636. yp += width;
  637. }
  638. }
  639. yi = x;
  640. stackIn = stackStart;
  641. stackOut = stackEnd;
  642. for (y = 0; y < height; y++) {
  643. p = yi << 2;
  644. pixels[p + 3] = pa = (a_sum * mul_sum) >> shg_sum;
  645. if (pa > 0) {
  646. pa = 255 / pa;
  647. pixels[p] = ((r_sum * mul_sum) >> shg_sum) * pa;
  648. pixels[p + 1] = ((g_sum * mul_sum) >> shg_sum) * pa;
  649. pixels[p + 2] = ((b_sum * mul_sum) >> shg_sum) * pa;
  650. } else {
  651. pixels[p] = pixels[p + 1] = pixels[p + 2] = 0;
  652. }
  653. r_sum -= r_out_sum;
  654. g_sum -= g_out_sum;
  655. b_sum -= b_out_sum;
  656. a_sum -= a_out_sum;
  657. r_out_sum -= stackIn.r;
  658. g_out_sum -= stackIn.g;
  659. b_out_sum -= stackIn.b;
  660. a_out_sum -= stackIn.a;
  661. p = (x + (((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width)) << 2;
  662. r_sum += (r_in_sum += (stackIn.r = pixels[p]));
  663. g_sum += (g_in_sum += (stackIn.g = pixels[p + 1]));
  664. b_sum += (b_in_sum += (stackIn.b = pixels[p + 2]));
  665. a_sum += (a_in_sum += (stackIn.a = pixels[p + 3]));
  666. stackIn = stackIn.next;
  667. r_out_sum += (pr = stackOut.r);
  668. g_out_sum += (pg = stackOut.g);
  669. b_out_sum += (pb = stackOut.b);
  670. a_out_sum += (pa = stackOut.a);
  671. r_in_sum -= pr;
  672. g_in_sum -= pg;
  673. b_in_sum -= pb;
  674. a_in_sum -= pa;
  675. stackOut = stackOut.next;
  676. yi += width;
  677. }
  678. }
  679. return pixels;
  680. }
  681. function stackBlurCanvasRGB(imageData, top_x, top_y, width, height, radius) {
  682. var pixels = imageData.data;
  683. var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum,
  684. r_out_sum, g_out_sum, b_out_sum,
  685. r_in_sum, g_in_sum, b_in_sum,
  686. pr, pg, pb, rbs;
  687. var div = radius + radius + 1;
  688. var w4 = width << 2;
  689. var widthMinus1 = width - 1;
  690. var heightMinus1 = height - 1;
  691. var radiusPlus1 = radius + 1;
  692. var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;
  693. var stackStart = new BlurStack();
  694. var stack = stackStart;
  695. for (i = 1; i < div; i++) {
  696. stack = stack.next = new BlurStack();
  697. if (i == radiusPlus1) var stackEnd = stack;
  698. }
  699. stack.next = stackStart;
  700. var stackIn = null;
  701. var stackOut = null;
  702. yw = yi = 0;
  703. var mul_sum = mul_table[radius];
  704. var shg_sum = shg_table[radius];
  705. for (y = 0; y < height; y++) {
  706. r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0;
  707. r_out_sum = radiusPlus1 * (pr = pixels[yi]);
  708. g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
  709. b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
  710. r_sum += sumFactor * pr;
  711. g_sum += sumFactor * pg;
  712. b_sum += sumFactor * pb;
  713. stack = stackStart;
  714. for (i = 0; i < radiusPlus1; i++) {
  715. stack.r = pr;
  716. stack.g = pg;
  717. stack.b = pb;
  718. stack = stack.next;
  719. }
  720. for (i = 1; i < radiusPlus1; i++) {
  721. p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
  722. r_sum += (stack.r = (pr = pixels[p])) * (rbs = radiusPlus1 - i);
  723. g_sum += (stack.g = (pg = pixels[p + 1])) * rbs;
  724. b_sum += (stack.b = (pb = pixels[p + 2])) * rbs;
  725. r_in_sum += pr;
  726. g_in_sum += pg;
  727. b_in_sum += pb;
  728. stack = stack.next;
  729. }
  730. stackIn = stackStart;
  731. stackOut = stackEnd;
  732. for (x = 0; x < width; x++) {
  733. pixels[yi] = (r_sum * mul_sum) >> shg_sum;
  734. pixels[yi + 1] = (g_sum * mul_sum) >> shg_sum;
  735. pixels[yi + 2] = (b_sum * mul_sum) >> shg_sum;
  736. r_sum -= r_out_sum;
  737. g_sum -= g_out_sum;
  738. b_sum -= b_out_sum;
  739. r_out_sum -= stackIn.r;
  740. g_out_sum -= stackIn.g;
  741. b_out_sum -= stackIn.b;
  742. p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;
  743. r_in_sum += (stackIn.r = pixels[p]);
  744. g_in_sum += (stackIn.g = pixels[p + 1]);
  745. b_in_sum += (stackIn.b = pixels[p + 2]);
  746. r_sum += r_in_sum;
  747. g_sum += g_in_sum;
  748. b_sum += b_in_sum;
  749. stackIn = stackIn.next;
  750. r_out_sum += (pr = stackOut.r);
  751. g_out_sum += (pg = stackOut.g);
  752. b_out_sum += (pb = stackOut.b);
  753. r_in_sum -= pr;
  754. g_in_sum -= pg;
  755. b_in_sum -= pb;
  756. stackOut = stackOut.next;
  757. yi += 4;
  758. }
  759. yw += width;
  760. }
  761. for (x = 0; x < width; x++) {
  762. g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0;
  763. yi = x << 2;
  764. r_out_sum = radiusPlus1 * (pr = pixels[yi]);
  765. g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
  766. b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
  767. r_sum += sumFactor * pr;
  768. g_sum += sumFactor * pg;
  769. b_sum += sumFactor * pb;
  770. stack = stackStart;
  771. for (i = 0; i < radiusPlus1; i++) {
  772. stack.r = pr;
  773. stack.g = pg;
  774. stack.b = pb;
  775. stack = stack.next;
  776. }
  777. yp = width;
  778. for (i = 1; i <= radius; i++) {
  779. yi = (yp + x) << 2;
  780. r_sum += (stack.r = (pr = pixels[yi])) * (rbs = radiusPlus1 - i);
  781. g_sum += (stack.g = (pg = pixels[yi + 1])) * rbs;
  782. b_sum += (stack.b = (pb = pixels[yi + 2])) * rbs;
  783. r_in_sum += pr;
  784. g_in_sum += pg;
  785. b_in_sum += pb;
  786. stack = stack.next;
  787. if (i < heightMinus1) {
  788. yp += width;
  789. }
  790. }
  791. yi = x;
  792. stackIn = stackStart;
  793. stackOut = stackEnd;
  794. for (y = 0; y < height; y++) {
  795. p = yi << 2;
  796. pixels[p] = (r_sum * mul_sum) >> shg_sum;
  797. pixels[p + 1] = (g_sum * mul_sum) >> shg_sum;
  798. pixels[p + 2] = (b_sum * mul_sum) >> shg_sum;
  799. r_sum -= r_out_sum;
  800. g_sum -= g_out_sum;
  801. b_sum -= b_out_sum;
  802. r_out_sum -= stackIn.r;
  803. g_out_sum -= stackIn.g;
  804. b_out_sum -= stackIn.b;
  805. p = (x + (((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width)) << 2;
  806. r_sum += (r_in_sum += (stackIn.r = pixels[p]));
  807. g_sum += (g_in_sum += (stackIn.g = pixels[p + 1]));
  808. b_sum += (b_in_sum += (stackIn.b = pixels[p + 2]));
  809. stackIn = stackIn.next;
  810. r_out_sum += (pr = stackOut.r);
  811. g_out_sum += (pg = stackOut.g);
  812. b_out_sum += (pb = stackOut.b);
  813. r_in_sum -= pr;
  814. g_in_sum -= pg;
  815. b_in_sum -= pb;
  816. stackOut = stackOut.next;
  817. yi += width;
  818. }
  819. }
  820. return pixels;
  821. }
  822. function BlurStack() {
  823. this.r = 0;
  824. this.g = 0;
  825. this.b = 0;
  826. this.a = 0;
  827. this.next = null;
  828. }
  829. var b = Math.round(blurRadius);
  830. return this.getImageData(x, y, w, h).then((res) => {
  831. var data;
  832. if (type == 'rgb') {
  833. data = stackBlurCanvasRGB(res, x, y, w * dpr, h * dpr, b);
  834. } else {
  835. data = stackBlurCanvasRGBA(res, x, y, w * dpr, h * dpr, b);
  836. }
  837. console.log(res.height);
  838. return {
  839. width: res.width,
  840. height: res.height,
  841. data: data
  842. };
  843. }).then(res => {
  844. return this.putImageData(x, y, w, h, res);
  845. }).catch(e => console.error(e))
  846. }
  847. /**
  848. * 使图像反色,类似底片
  849. * @param {Object} x
  850. * @param {Object} y
  851. * @param {Object} w
  852. * @param {Object} h
  853. */
  854. CRender.prototype.ColorInvert = function(x, y, w, h) {
  855. let t = this;
  856. return this.getImageData(x, y, w, h).then((res) => {
  857. var data = res.data;
  858. for (var i = 0; i < res.data.length; i += 4) {
  859. var r = data[i];
  860. var g = data[i + 1];
  861. var b = data[i + 2];
  862. data[i] = 255 - r;
  863. data[i + 1] = 255 - g;
  864. data[i + 2] = 255 - b;
  865. }
  866. return {
  867. width: res.width,
  868. height: res.height,
  869. data: data
  870. };
  871. }).then(res => {
  872. return this.putImageData(x, y, w, h, res);
  873. }).catch(e => console.error(e))
  874. }
  875. /**
  876. * 灰度图片。
  877. * @param {Object} x
  878. * @param {Object} y
  879. * @param {Object} w
  880. * @param {Object} h
  881. */
  882. CRender.prototype.Grayscale = function(x, y, w, h) {
  883. let t = this;
  884. return this.getImageData(x, y, w, h).then((res) => {
  885. // 0.34,0.5,0.16为加权的权重。可适当调整。
  886. var data = res.data,
  887. len = data.length,
  888. i, brightness;
  889. for (i = 0; i < len; i += 4) {
  890. brightness = 0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2];
  891. data[i] = brightness;
  892. data[i + 1] = brightness;
  893. data[i + 2] = brightness;
  894. }
  895. return {
  896. width: res.width,
  897. height: res.height,
  898. data: data
  899. };
  900. }).then(res => {
  901. return this.putImageData(x, y, w, h, res);
  902. }).catch(e => console.error(e))
  903. }
  904. /**
  905. * 调整图片的明暗度。
  906. * @param {Object} x
  907. * @param {Object} y
  908. * @param {Object} w
  909. * @param {Object} h
  910. * @param {Object} brightness -1~1,小于0为暗,大于1为亮。0为正常。
  911. */
  912. CRender.prototype.Brighten = function(x, y, w, h, brightness) {
  913. let t = this;
  914. return this.getImageData(x, y, w, h).then((res) => {
  915. // 0.34,0.5,0.16为加权的权重。可适当调整。
  916. brightness = brightness * 255
  917. var data = res.data,
  918. len = data.length,
  919. i;
  920. for (i = 0; i < len; i += 4) {
  921. data[i] += brightness;
  922. data[i + 1] += brightness;
  923. data[i + 2] += brightness;
  924. }
  925. return {
  926. width: res.width,
  927. height: res.height,
  928. data: data
  929. };
  930. }).then(res => {
  931. return this.putImageData(x, y, w, h, res);
  932. }).catch(e => console.error(e))
  933. }
  934. /**
  935. * 调整图片对比度。
  936. * @param {Object} x
  937. * @param {Object} y
  938. * @param {Object} w
  939. * @param {Object} h
  940. * @param {Object} brightness -100~100
  941. */
  942. CRender.prototype.Contrast = function(x, y, w, h, brightness) {
  943. let t = this;
  944. return this.getImageData(x, y, w, h).then((res) => {
  945. var adjust = Math.pow((brightness + 100) / 100, 2);
  946. var data = res.data,
  947. nPixels = data.length,
  948. red = 150,
  949. green = 150,
  950. blue = 150,
  951. i;
  952. for (i = 0; i < nPixels; i += 4) {
  953. red = data[i];
  954. green = data[i + 1];
  955. blue = data[i + 2];
  956. red /= 255;
  957. red -= 0.5;
  958. red *= adjust;
  959. red += 0.5;
  960. red *= 255;
  961. green /= 255;
  962. green -= 0.5;
  963. green *= adjust;
  964. green += 0.5;
  965. green *= 255;
  966. blue /= 255;
  967. blue -= 0.5;
  968. blue *= adjust;
  969. blue += 0.5;
  970. blue *= 255;
  971. red = red < 0 ? 0 : red > 255 ? 255 : red;
  972. green = green < 0 ? 0 : green > 255 ? 255 : green;
  973. blue = blue < 0 ? 0 : blue > 255 ? 255 : blue;
  974. data[i] = red;
  975. data[i + 1] = green;
  976. data[i + 2] = blue;
  977. }
  978. return {
  979. width: res.width,
  980. height: res.height,
  981. data: data
  982. };
  983. }).then(res => {
  984. return this.putImageData(x, y, w, h, res);
  985. }).catch(e => console.error(e))
  986. }
  987. /**
  988. * 浮雕
  989. * @param {Object} x
  990. * @param {Object} y
  991. * @param {Object} w
  992. * @param {Object} h
  993. * @param {Object} embossStrength 浮雕强度 0~1,0为正常。
  994. * @param {Object} embossWhiteLevel 黑白对比强度0~1
  995. * @param {Object} embossDirection 浮雕的方向。top,top-left,top-right,left,right,bottom,bottom-left,bottom-right
  996. * @param {Object} embossBlend 是否显示黑白浮雕
  997. */
  998. CRender.prototype.Emboss = function(x, y, w, h, embossStrength = 0.5, embossWhiteLevel = 0.5, embossDirection =
  999. "top-right", embossBlend = false) {
  1000. let t = this;
  1001. return this.getImageData(x, y, w, h).then((res) => {
  1002. var strength = embossStrength * 10,
  1003. greyLevel = embossWhiteLevel * 255,
  1004. direction = embossDirection,
  1005. blend = !embossBlend,
  1006. dirY = 0,
  1007. dirX = 0,
  1008. data = res.data,
  1009. w = res.width,
  1010. h = res.height,
  1011. w4 = w * 4,
  1012. y = h;
  1013. switch (direction) {
  1014. case 'top-left':
  1015. dirY = -1;
  1016. dirX = -1;
  1017. break;
  1018. case 'top':
  1019. dirY = -1;
  1020. dirX = 0;
  1021. break;
  1022. case 'top-right':
  1023. dirY = -1;
  1024. dirX = 1;
  1025. break;
  1026. case 'right':
  1027. dirY = 0;
  1028. dirX = 1;
  1029. break;
  1030. case 'bottom-right':
  1031. dirY = 1;
  1032. dirX = 1;
  1033. break;
  1034. case 'bottom':
  1035. dirY = 1;
  1036. dirX = 0;
  1037. break;
  1038. case 'bottom-left':
  1039. dirY = 1;
  1040. dirX = -1;
  1041. break;
  1042. case 'left':
  1043. dirY = 0;
  1044. dirX = -1;
  1045. break;
  1046. default:
  1047. Util_1.Util.error('Unknown emboss direction: ' + direction);
  1048. }
  1049. do {
  1050. var offsetY = (y - 1) * w4;
  1051. var otherY = dirY;
  1052. if (y + otherY < 1) {
  1053. otherY = 0;
  1054. }
  1055. if (y + otherY > h) {
  1056. otherY = 0;
  1057. }
  1058. var offsetYOther = (y - 1 + otherY) * w * 4;
  1059. var x = w;
  1060. do {
  1061. var offset = offsetY + (x - 1) * 4;
  1062. var otherX = dirX;
  1063. if (x + otherX < 1) {
  1064. otherX = 0;
  1065. }
  1066. if (x + otherX > w) {
  1067. otherX = 0;
  1068. }
  1069. var offsetOther = offsetYOther + (x - 1 + otherX) * 4;
  1070. var dR = data[offset] - data[offsetOther];
  1071. var dG = data[offset + 1] - data[offsetOther + 1];
  1072. var dB = data[offset + 2] - data[offsetOther + 2];
  1073. var dif = dR;
  1074. var absDif = dif > 0 ? dif : -dif;
  1075. var absG = dG > 0 ? dG : -dG;
  1076. var absB = dB > 0 ? dB : -dB;
  1077. if (absG > absDif) {
  1078. dif = dG;
  1079. }
  1080. if (absB > absDif) {
  1081. dif = dB;
  1082. }
  1083. dif *= strength;
  1084. if (blend) {
  1085. var r = data[offset] + dif;
  1086. var g = data[offset + 1] + dif;
  1087. var b = data[offset + 2] + dif;
  1088. data[offset] = r > 255 ? 255 : r < 0 ? 0 : r;
  1089. data[offset + 1] = g > 255 ? 255 : g < 0 ? 0 : g;
  1090. data[offset + 2] = b > 255 ? 255 : b < 0 ? 0 : b;
  1091. } else {
  1092. var grey = greyLevel - dif;
  1093. if (grey < 0) {
  1094. grey = 0;
  1095. } else if (grey > 255) {
  1096. grey = 255;
  1097. }
  1098. data[offset] = data[offset + 1] = data[offset + 2] = grey;
  1099. }
  1100. } while (--x);
  1101. } while (--y);
  1102. return {
  1103. width: res.width,
  1104. height: res.height,
  1105. data: data
  1106. };
  1107. }).then(res => {
  1108. return this.putImageData(x, y, w, h, res);
  1109. }).catch(e => console.error(e))
  1110. }
  1111. /**
  1112. * 提高饱和度
  1113. * @param {Object} x
  1114. * @param {Object} y
  1115. * @param {Object} w
  1116. * @param {Object} h
  1117. * @param {Object} enhance -1~1
  1118. */
  1119. CRender.prototype.Enhance = function(x, y, w, h, enhance) {
  1120. let t = this;
  1121. return this.getImageData(x, y, w, h).then((res) => {
  1122. function remap(fromValue, fromMin, fromMax, toMin, toMax) {
  1123. var fromRange = fromMax - fromMin,
  1124. toRange = toMax - toMin,
  1125. toValue;
  1126. if (fromRange === 0) {
  1127. return toMin + toRange / 2;
  1128. }
  1129. if (toRange === 0) {
  1130. return toMin;
  1131. }
  1132. toValue = (fromValue - fromMin) / fromRange;
  1133. toValue = toRange * toValue + toMin;
  1134. return toValue;
  1135. }
  1136. var data = res.data,
  1137. nSubPixels = data.length,
  1138. rMin = data[0],
  1139. rMax = rMin,
  1140. r, gMin = data[1],
  1141. gMax = gMin,
  1142. g, bMin = data[2],
  1143. bMax = bMin,
  1144. b, i;
  1145. var enhanceAmount = enhance;
  1146. if (enhanceAmount === 0) {
  1147. return;
  1148. }
  1149. for (i = 0; i < nSubPixels; i += 4) {
  1150. r = data[i + 0];
  1151. if (r < rMin) {
  1152. rMin = r;
  1153. } else if (r > rMax) {
  1154. rMax = r;
  1155. }
  1156. g = data[i + 1];
  1157. if (g < gMin) {
  1158. gMin = g;
  1159. } else if (g > gMax) {
  1160. gMax = g;
  1161. }
  1162. b = data[i + 2];
  1163. if (b < bMin) {
  1164. bMin = b;
  1165. } else if (b > bMax) {
  1166. bMax = b;
  1167. }
  1168. }
  1169. if (rMax === rMin) {
  1170. rMax = 255;
  1171. rMin = 0;
  1172. }
  1173. if (gMax === gMin) {
  1174. gMax = 255;
  1175. gMin = 0;
  1176. }
  1177. if (bMax === bMin) {
  1178. bMax = 255;
  1179. bMin = 0;
  1180. }
  1181. var rMid, rGoalMax, rGoalMin, gMid, gGoalMax, gGoalMin, bMid, bGoalMax, bGoalMin;
  1182. if (enhanceAmount > 0) {
  1183. rGoalMax = rMax + enhanceAmount * (255 - rMax);
  1184. rGoalMin = rMin - enhanceAmount * (rMin - 0);
  1185. gGoalMax = gMax + enhanceAmount * (255 - gMax);
  1186. gGoalMin = gMin - enhanceAmount * (gMin - 0);
  1187. bGoalMax = bMax + enhanceAmount * (255 - bMax);
  1188. bGoalMin = bMin - enhanceAmount * (bMin - 0);
  1189. } else {
  1190. rMid = (rMax + rMin) * 0.5;
  1191. rGoalMax = rMax + enhanceAmount * (rMax - rMid);
  1192. rGoalMin = rMin + enhanceAmount * (rMin - rMid);
  1193. gMid = (gMax + gMin) * 0.5;
  1194. gGoalMax = gMax + enhanceAmount * (gMax - gMid);
  1195. gGoalMin = gMin + enhanceAmount * (gMin - gMid);
  1196. bMid = (bMax + bMin) * 0.5;
  1197. bGoalMax = bMax + enhanceAmount * (bMax - bMid);
  1198. bGoalMin = bMin + enhanceAmount * (bMin - bMid);
  1199. }
  1200. for (i = 0; i < nSubPixels; i += 4) {
  1201. data[i + 0] = remap(data[i + 0], rMin, rMax, rGoalMin, rGoalMax);
  1202. data[i + 1] = remap(data[i + 1], gMin, gMax, gGoalMin, gGoalMax);
  1203. data[i + 2] = remap(data[i + 2], bMin, bMax, bGoalMin, bGoalMax);
  1204. }
  1205. return {
  1206. width: res.width,
  1207. height: res.height,
  1208. data: data
  1209. };
  1210. }).then(res => {
  1211. return this.putImageData(x, y, w, h, res);
  1212. }).catch(e => console.error(e))
  1213. }
  1214. /**
  1215. * 通过HSL调整图像
  1216. * @param {Object} x
  1217. * @param {Object} y
  1218. * @param {Object} w
  1219. * @param {Object} h
  1220. * @param {Object} hue 色相 0~259
  1221. * @param {Object} saturation 饱和度 -2~10
  1222. * @param {Object} luminance 明暗 -2~2
  1223. */
  1224. CRender.prototype.HSL = function(x, y, w, h, hue = 100, saturation = 5, luminance = 0) {
  1225. let t = this;
  1226. return this.getImageData(x, y, w, h).then((res) => {
  1227. var data = res.data,
  1228. nPixels = data.length,
  1229. v = 1,
  1230. s = Math.pow(2, saturation),
  1231. h = Math.abs(hue + 360) % 360,
  1232. l = luminance * 127,
  1233. i;
  1234. var vsu = v * s * Math.cos((h * Math.PI) / 180),
  1235. vsw = v * s * Math.sin((h * Math.PI) / 180);
  1236. var rr = 0.299 * v + 0.701 * vsu + 0.167 * vsw,
  1237. rg = 0.587 * v - 0.587 * vsu + 0.33 * vsw,
  1238. rb = 0.114 * v - 0.114 * vsu - 0.497 * vsw;
  1239. var gr = 0.299 * v - 0.299 * vsu - 0.328 * vsw,
  1240. gg = 0.587 * v + 0.413 * vsu + 0.035 * vsw,
  1241. gb = 0.114 * v - 0.114 * vsu + 0.293 * vsw;
  1242. var br = 0.299 * v - 0.3 * vsu + 1.25 * vsw,
  1243. bg = 0.587 * v - 0.586 * vsu - 1.05 * vsw,
  1244. bb = 0.114 * v + 0.886 * vsu - 0.2 * vsw;
  1245. var r, g, b, a;
  1246. for (i = 0; i < nPixels; i += 4) {
  1247. r = data[i + 0];
  1248. g = data[i + 1];
  1249. b = data[i + 2];
  1250. a = data[i + 3];
  1251. data[i + 0] = rr * r + rg * g + rb * b + l;
  1252. data[i + 1] = gr * r + gg * g + gb * b + l;
  1253. data[i + 2] = br * r + bg * g + bb * b + l;
  1254. data[i + 3] = a;
  1255. }
  1256. return {
  1257. width: res.width,
  1258. height: res.height,
  1259. data: data
  1260. };
  1261. }).then(res => {
  1262. return this.putImageData(x, y, w, h, res);
  1263. }).catch(e => console.error(e))
  1264. }
  1265. /**
  1266. * 调整图像hsv
  1267. * @param {Object} x
  1268. * @param {Object} y
  1269. * @param {Object} w
  1270. * @param {Object} h
  1271. * @param {Object} hue 色相 0~259
  1272. * @param {Object} saturation 饱和度 -2~10
  1273. * @param {Object} value 明暗 -2~2
  1274. */
  1275. CRender.prototype.HSV = function(x, y, w, h, hue = 150, saturation = 0, value = 0) {
  1276. let t = this;
  1277. return this.getImageData(x, y, w, h).then((res) => {
  1278. var data = res.data,
  1279. nPixels = data.length,
  1280. v = Math.pow(2, value),
  1281. s = Math.pow(2, saturation),
  1282. h = Math.abs(hue + 360) % 360,
  1283. i;
  1284. var vsu = v * s * Math.cos((h * Math.PI) / 180),
  1285. vsw = v * s * Math.sin((h * Math.PI) / 180);
  1286. var rr = 0.299 * v + 0.701 * vsu + 0.167 * vsw,
  1287. rg = 0.587 * v - 0.587 * vsu + 0.33 * vsw,
  1288. rb = 0.114 * v - 0.114 * vsu - 0.497 * vsw;
  1289. var gr = 0.299 * v - 0.299 * vsu - 0.328 * vsw,
  1290. gg = 0.587 * v + 0.413 * vsu + 0.035 * vsw,
  1291. gb = 0.114 * v - 0.114 * vsu + 0.293 * vsw;
  1292. var br = 0.299 * v - 0.3 * vsu + 1.25 * vsw,
  1293. bg = 0.587 * v - 0.586 * vsu - 1.05 * vsw,
  1294. bb = 0.114 * v + 0.886 * vsu - 0.2 * vsw;
  1295. var r, g, b, a;
  1296. for (i = 0; i < nPixels; i += 4) {
  1297. r = data[i + 0];
  1298. g = data[i + 1];
  1299. b = data[i + 2];
  1300. a = data[i + 3];
  1301. data[i + 0] = rr * r + rg * g + rb * b;
  1302. data[i + 1] = gr * r + gg * g + gb * b;
  1303. data[i + 2] = br * r + bg * g + bb * b;
  1304. data[i + 3] = a;
  1305. }
  1306. return {
  1307. width: res.width,
  1308. height: res.height,
  1309. data: data
  1310. };
  1311. }).then(res => {
  1312. return this.putImageData(x, y, w, h, res);
  1313. }).catch(e => console.error(e))
  1314. }
  1315. /**
  1316. * 调整图像rgb通道
  1317. * @param {Object} x
  1318. * @param {Object} y
  1319. * @param {Object} w
  1320. * @param {Object} h
  1321. * @param {Object} r
  1322. * @param {Object} g
  1323. * @param {Object} b
  1324. */
  1325. CRender.prototype.RGB = function(x, y, w, h, r = 0, g = 100, b = 30) {
  1326. let t = this;
  1327. return this.getImageData(x, y, w, h).then((res) => {
  1328. var data = res.data,
  1329. nPixels = data.length,
  1330. red = r,
  1331. green = g,
  1332. blue = b,
  1333. i, brightness;
  1334. for (i = 0; i < nPixels; i += 4) {
  1335. brightness =
  1336. (0.34 * data[i] + 0.5 * data[i + 1] + 0.16 * data[i + 2]) / 255;
  1337. data[i] = brightness * red;
  1338. data[i + 1] = brightness * green;
  1339. data[i + 2] = brightness * blue;
  1340. data[i + 3] = data[i + 3];
  1341. }
  1342. return {
  1343. width: res.width,
  1344. height: res.height,
  1345. data: data
  1346. };
  1347. }).then(res => {
  1348. return this.putImageData(x, y, w, h, res);
  1349. }).catch(e => console.error(e))
  1350. }
  1351. /**
  1352. * 使图像色彩反相
  1353. * @param {Object} x
  1354. * @param {Object} y
  1355. * @param {Object} w
  1356. * @param {Object} h
  1357. */
  1358. CRender.prototype.Invert = function(x, y, w, h) {
  1359. let t = this;
  1360. return this.getImageData(x, y, w, h).then((res) => {
  1361. var data = res.data,
  1362. len = data.length,
  1363. i;
  1364. for (i = 0; i < len; i += 4) {
  1365. data[i] = 255 - data[i];
  1366. data[i + 1] = 255 - data[i + 1];
  1367. data[i + 2] = 255 - data[i + 2];
  1368. }
  1369. return {
  1370. width: res.width,
  1371. height: res.height,
  1372. data: data
  1373. };
  1374. }).then(res => {
  1375. return this.putImageData(x, y, w, h, res);
  1376. }).catch(e => console.error(e))
  1377. }
  1378. function pixelAt(idata, x, y) {
  1379. var idx = (y * idata.width + x) * 4;
  1380. var d = [];
  1381. d.push(idata.data[idx++], idata.data[idx++], idata.data[idx++], idata.data[idx++]);
  1382. return d;
  1383. }
  1384. function rgbDistance(p1, p2) {
  1385. return Math.sqrt(Math.pow(p1[0] - p2[0], 2) +
  1386. Math.pow(p1[1] - p2[1], 2) +
  1387. Math.pow(p1[2] - p2[2], 2));
  1388. }
  1389. function rgbMean(pTab) {
  1390. var m = [0, 0, 0];
  1391. for (var i = 0; i < pTab.length; i++) {
  1392. m[0] += pTab[i][0];
  1393. m[1] += pTab[i][1];
  1394. m[2] += pTab[i][2];
  1395. }
  1396. m[0] /= pTab.length;
  1397. m[1] /= pTab.length;
  1398. m[2] /= pTab.length;
  1399. return m;
  1400. }
  1401. function backgroundMask(idata, threshold) {
  1402. var rgbv_no = pixelAt(idata, 0, 0);
  1403. var rgbv_ne = pixelAt(idata, idata.width - 1, 0);
  1404. var rgbv_so = pixelAt(idata, 0, idata.height - 1);
  1405. var rgbv_se = pixelAt(idata, idata.width - 1, idata.height - 1);
  1406. var thres = threshold || 10;
  1407. if (rgbDistance(rgbv_no, rgbv_ne) < thres &&
  1408. rgbDistance(rgbv_ne, rgbv_se) < thres &&
  1409. rgbDistance(rgbv_se, rgbv_so) < thres &&
  1410. rgbDistance(rgbv_so, rgbv_no) < thres) {
  1411. var mean = rgbMean([rgbv_ne, rgbv_no, rgbv_se, rgbv_so]);
  1412. var mask = [];
  1413. for (var i = 0; i < idata.width * idata.height; i++) {
  1414. var d = rgbDistance(mean, [
  1415. idata.data[i * 4],
  1416. idata.data[i * 4 + 1],
  1417. idata.data[i * 4 + 2],
  1418. ]);
  1419. mask[i] = d < thres ? 0 : 255;
  1420. }
  1421. return mask;
  1422. }
  1423. }
  1424. function applyMask(idata, mask) {
  1425. for (var i = 0; i < idata.width * idata.height; i++) {
  1426. idata.data[4 * i + 3] = mask[i];
  1427. }
  1428. }
  1429. function erodeMask(mask, sw, sh) {
  1430. var weights = [1, 1, 1, 1, 0, 1, 1, 1, 1];
  1431. var side = Math.round(Math.sqrt(weights.length));
  1432. var halfSide = Math.floor(side / 2);
  1433. var maskResult = [];
  1434. for (var y = 0; y < sh; y++) {
  1435. for (var x = 0; x < sw; x++) {
  1436. var so = y * sw + x;
  1437. var a = 0;
  1438. for (var cy = 0; cy < side; cy++) {
  1439. for (var cx = 0; cx < side; cx++) {
  1440. var scy = y + cy - halfSide;
  1441. var scx = x + cx - halfSide;
  1442. if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
  1443. var srcOff = scy * sw + scx;
  1444. var wt = weights[cy * side + cx];
  1445. a += mask[srcOff] * wt;
  1446. }
  1447. }
  1448. }
  1449. maskResult[so] = a === 255 * 8 ? 255 : 0;
  1450. }
  1451. }
  1452. return maskResult;
  1453. }
  1454. function dilateMask(mask, sw, sh) {
  1455. var weights = [1, 1, 1, 1, 1, 1, 1, 1, 1];
  1456. var side = Math.round(Math.sqrt(weights.length));
  1457. var halfSide = Math.floor(side / 2);
  1458. var maskResult = [];
  1459. for (var y = 0; y < sh; y++) {
  1460. for (var x = 0; x < sw; x++) {
  1461. var so = y * sw + x;
  1462. var a = 0;
  1463. for (var cy = 0; cy < side; cy++) {
  1464. for (var cx = 0; cx < side; cx++) {
  1465. var scy = y + cy - halfSide;
  1466. var scx = x + cx - halfSide;
  1467. if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
  1468. var srcOff = scy * sw + scx;
  1469. var wt = weights[cy * side + cx];
  1470. a += mask[srcOff] * wt;
  1471. }
  1472. }
  1473. }
  1474. maskResult[so] = a >= 255 * 4 ? 255 : 0;
  1475. }
  1476. }
  1477. return maskResult;
  1478. }
  1479. function smoothEdgeMask(mask, sw, sh) {
  1480. var weights = [1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9, 1 / 9];
  1481. var side = Math.round(Math.sqrt(weights.length));
  1482. var halfSide = Math.floor(side / 2);
  1483. var maskResult = [];
  1484. for (var y = 0; y < sh; y++) {
  1485. for (var x = 0; x < sw; x++) {
  1486. var so = y * sw + x;
  1487. var a = 0;
  1488. for (var cy = 0; cy < side; cy++) {
  1489. for (var cx = 0; cx < side; cx++) {
  1490. var scy = y + cy - halfSide;
  1491. var scx = x + cx - halfSide;
  1492. if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
  1493. var srcOff = scy * sw + scx;
  1494. var wt = weights[cy * side + cx];
  1495. a += mask[srcOff] * wt;
  1496. }
  1497. }
  1498. }
  1499. maskResult[so] = a;
  1500. }
  1501. }
  1502. return maskResult;
  1503. }
  1504. /**
  1505. * 遮罩
  1506. * @param {Object} x
  1507. * @param {Object} y
  1508. * @param {Object} w
  1509. * @param {Object} h
  1510. * @param {Object} threshold 0~300遮罩强度
  1511. */
  1512. CRender.prototype.Mask = function(x, y, w, h, threshold = 100) {
  1513. let t = this;
  1514. return this.getImageData(x, y, w, h).then((res) => {
  1515. var imageData = {
  1516. ...res
  1517. }
  1518. var mask = backgroundMask(imageData, threshold);
  1519. if (mask) {
  1520. mask = erodeMask(mask, imageData.width, imageData.height);
  1521. mask = dilateMask(mask, imageData.width, imageData.height);
  1522. mask = smoothEdgeMask(mask, imageData.width, imageData.height);
  1523. applyMask(imageData, mask);
  1524. }
  1525. return {
  1526. width: res.width,
  1527. height: res.height,
  1528. data: imageData.data
  1529. };
  1530. }).then(res => {
  1531. return this.putImageData(x, y, w, h, res);
  1532. }).catch(e => console.error(e))
  1533. }
  1534. /**
  1535. * 图像噪点
  1536. * @param {Object} x
  1537. * @param {Object} y
  1538. * @param {Object} w
  1539. * @param {Object} h
  1540. * @param {Object} Noise 0~4
  1541. */
  1542. CRender.prototype.Noise = function(x, y, w, h, noise = 0.5) {
  1543. let t = this;
  1544. return this.getImageData(x, y, w, h).then((res) => {
  1545. var amount = noise * 255,
  1546. data = res.data,
  1547. nPixels = data.length,
  1548. half = amount / 2,
  1549. i;
  1550. for (i = 0; i < nPixels; i += 4) {
  1551. data[i + 0] += half - 2 * half * Math.random();
  1552. data[i + 1] += half - 2 * half * Math.random();
  1553. data[i + 2] += half - 2 * half * Math.random();
  1554. }
  1555. return {
  1556. width: res.width,
  1557. height: res.height,
  1558. data: data
  1559. };
  1560. }).then(res => {
  1561. return this.putImageData(x, y, w, h, res);
  1562. }).catch(e => console.error(e))
  1563. }
  1564. /**
  1565. * 图像像素化
  1566. * @param {Object} x
  1567. * @param {Object} y
  1568. * @param {Object} w
  1569. * @param {Object} h
  1570. * @param {Object} size 0~20
  1571. */
  1572. CRender.prototype.Pixelate = function(x, y, w, h, size = 10) {
  1573. let t = this;
  1574. return this.getImageData(x, y, w, h).then((res) => {
  1575. var pixelSize = Math.ceil(size),
  1576. width = res.width,
  1577. height = res.height,
  1578. x, y, i, red, green, blue, alpha, nBinsX = Math.ceil(width / pixelSize),
  1579. nBinsY = Math.ceil(height / pixelSize),
  1580. xBinStart, xBinEnd, yBinStart, yBinEnd, xBin, yBin, pixelsInBin, data = res.data;
  1581. if (pixelSize <= 0) {
  1582. return new Promise.reject("像素点不能小于0");
  1583. }
  1584. for (xBin = 0; xBin < nBinsX; xBin += 1) {
  1585. for (yBin = 0; yBin < nBinsY; yBin += 1) {
  1586. red = 0;
  1587. green = 0;
  1588. blue = 0;
  1589. alpha = 0;
  1590. xBinStart = xBin * pixelSize;
  1591. xBinEnd = xBinStart + pixelSize;
  1592. yBinStart = yBin * pixelSize;
  1593. yBinEnd = yBinStart + pixelSize;
  1594. pixelsInBin = 0;
  1595. for (x = xBinStart; x < xBinEnd; x += 1) {
  1596. if (x >= width) {
  1597. continue;
  1598. }
  1599. for (y = yBinStart; y < yBinEnd; y += 1) {
  1600. if (y >= height) {
  1601. continue;
  1602. }
  1603. i = (width * y + x) * 4;
  1604. red += data[i + 0];
  1605. green += data[i + 1];
  1606. blue += data[i + 2];
  1607. alpha += data[i + 3];
  1608. pixelsInBin += 1;
  1609. }
  1610. }
  1611. red = red / pixelsInBin;
  1612. green = green / pixelsInBin;
  1613. blue = blue / pixelsInBin;
  1614. alpha = alpha / pixelsInBin;
  1615. for (x = xBinStart; x < xBinEnd; x += 1) {
  1616. if (x >= width) {
  1617. continue;
  1618. }
  1619. for (y = yBinStart; y < yBinEnd; y += 1) {
  1620. if (y >= height) {
  1621. continue;
  1622. }
  1623. i = (width * y + x) * 4;
  1624. data[i + 0] = red;
  1625. data[i + 1] = green;
  1626. data[i + 2] = blue;
  1627. data[i + 3] = alpha;
  1628. }
  1629. }
  1630. }
  1631. }
  1632. return {
  1633. width: res.width,
  1634. height: res.height,
  1635. data: data
  1636. };
  1637. }).then(res => {
  1638. return this.putImageData(x, y, w, h, res);
  1639. }).catch(e => console.error(e))
  1640. }
  1641. /**
  1642. * 褐色风格处理
  1643. * @param {Object} x
  1644. * @param {Object} y
  1645. * @param {Object} w
  1646. * @param {Object} h
  1647. */
  1648. CRender.prototype.Sepia = function(x, y, w, h) {
  1649. let t = this;
  1650. return this.getImageData(x, y, w, h).then((res) => {
  1651. var d = res.data;
  1652. for (var i = 0; i < d.length; i += 4) {
  1653. var red = d[i];
  1654. var green = d[i + 1];
  1655. var blue = d[i + 2];
  1656. var alpha = d[i + 3];
  1657. var outRed = (red * .393) + (green * .769) + (blue *
  1658. .189); // calculate value for red channel in pixel
  1659. var outGreen = (red * .349) + (green * .686) + (blue * .168);
  1660. var outBlue = (red * .272) + (green * .534) + (blue * .131);
  1661. d[i] = outRed < 255 ? outRed :
  1662. 255; // check if the value is less than 255, if more set it to 255
  1663. d[i + 1] = outGreen < 255 ? outGreen : 255;
  1664. d[i + 2] = outBlue < 255 ? outBlue : 255
  1665. d[i + 3] = alpha;
  1666. }
  1667. return {
  1668. width: res.width,
  1669. height: res.height,
  1670. data: d
  1671. };
  1672. }).then(res => {
  1673. return this.putImageData(x, y, w, h, res);
  1674. }).catch(e => console.error(e))
  1675. }
  1676. /**
  1677. * 水平翻转图像
  1678. * @param {Object} x
  1679. * @param {Object} y
  1680. * @param {Object} w
  1681. * @param {Object} h
  1682. */
  1683. CRender.prototype.HorizontalFlip = function(x, y, w, h) {
  1684. let t = this;
  1685. let dpr = 1;
  1686. // #ifndef H5
  1687. dpr = t.ctx.dpr;
  1688. // #endif
  1689. return this.getImageData(x, y, w, h).then(async (res) => {
  1690. // 1.获取通信信息
  1691. let imgdata = res;
  1692. let middleAxle /*中轴*/ = (w * dpr * 4) / 2;
  1693. // 2.遍历行
  1694. for (let curRow = 0; curRow < h * dpr; curRow++) {
  1695. let aisleStart /*每行开始的通道位置*/ = curRow * w * dpr * 4,
  1696. aisleEnd /*每行结束的通道位置*/ = (curRow + 1) * w * dpr * 4 - 4,
  1697. curMiddleAxle /*每一行中轴所在的位置*/ = aisleEnd - middleAxle;
  1698. // 3.遍历当前行的列作为内循环,把列的左边像素按照轴对称和右边的像素互换
  1699. for (; aisleStart <= curMiddleAxle; aisleStart += 4, aisleEnd -= 4) {
  1700. // 临时存放
  1701. let tr = imgdata.data[aisleStart],
  1702. tg = imgdata.data[aisleStart + 1],
  1703. tb = imgdata.data[aisleStart + 2],
  1704. ta = imgdata.data[aisleStart + 3];
  1705. imgdata.data[aisleStart] = imgdata.data[aisleEnd];
  1706. imgdata.data[aisleStart + 1] = imgdata.data[aisleEnd + 1];
  1707. imgdata.data[aisleStart + 2] = imgdata.data[aisleEnd + 2];
  1708. imgdata.data[aisleStart + 3] = imgdata.data[aisleEnd + 3];
  1709. imgdata.data[aisleEnd] = tr;
  1710. imgdata.data[aisleEnd + 1] = tg;
  1711. imgdata.data[aisleEnd + 2] = tb;
  1712. imgdata.data[aisleEnd + 3] = ta;
  1713. }
  1714. }
  1715. return {
  1716. width: res.width,
  1717. height: res.height,
  1718. data: imgdata.data
  1719. };
  1720. }).then(res => {
  1721. return this.putImageData(x, y, w, h, res);
  1722. }).catch(e => console.error(e))
  1723. }
  1724. /**
  1725. * 垂直翻转图像
  1726. * @param {Object} x
  1727. * @param {Object} y
  1728. * @param {Object} w
  1729. * @param {Object} h
  1730. */
  1731. CRender.prototype.VerticallyFlip = function(x, y, w, h) {
  1732. let t = this;
  1733. let dpr = 1;
  1734. // #ifndef H5
  1735. dpr = t.ctx.dpr;
  1736. // #endif
  1737. return this.getImageData(x, y, w, h).then(async (res) => {
  1738. // 1.获取图像信息
  1739. let imgdata = res;
  1740. let middleAxle /*中轴*/ = Math.floor(h / 2),
  1741. rowAisles = w * 4;
  1742. // 2.遍历总行数一半的每一行作为外循环(向下取整)
  1743. for (var curRow = 0; curRow < middleAxle; curRow++) {
  1744. //
  1745. let aisleStart /*开始的通道位置*/ = curRow * rowAisles,
  1746. mirrorStart /*中轴对称的开始位置*/ = (h - curRow - 1) * rowAisles;
  1747. // 3.遍历当前行的列作为内循环,把列的每个通道按照水平轴对称和镜像里的通道互换
  1748. for (; aisleStart < rowAisles * (curRow + 1); aisleStart += 4, mirrorStart += 4) {
  1749. var tr = imgdata.data[aisleStart],
  1750. tg = imgdata.data[aisleStart + 1],
  1751. tb = imgdata.data[aisleStart + 2],
  1752. ta = imgdata.data[aisleStart + 3];
  1753. imgdata.data[aisleStart] = imgdata.data[mirrorStart];
  1754. imgdata.data[aisleStart + 1] = imgdata.data[mirrorStart + 1];
  1755. imgdata.data[aisleStart + 2] = imgdata.data[mirrorStart + 2];
  1756. imgdata.data[aisleStart + 3] = imgdata.data[mirrorStart + 3];
  1757. imgdata.data[mirrorStart] = tr;
  1758. imgdata.data[mirrorStart + 1] = tg;
  1759. imgdata.data[mirrorStart + 2] = tb;
  1760. imgdata.data[mirrorStart + 3] = ta;
  1761. }
  1762. }
  1763. return {
  1764. width: res.width,
  1765. height: res.height,
  1766. data: imgdata.data
  1767. };
  1768. }).then(res => {
  1769. return this.putImageData(x, y, w, h, res);
  1770. }).catch(e => console.error(e))
  1771. }
  1772. /**
  1773. * 水平镜像,左右居中对称图像
  1774. * @param {Object} x
  1775. * @param {Object} y
  1776. * @param {Object} w
  1777. * @param {Object} h
  1778. */
  1779. CRender.prototype.HorizontalMirror = function(x, y, w, h) {
  1780. let t = this;
  1781. let dpr = 1;
  1782. // #ifndef H5
  1783. dpr = t.ctx.dpr;
  1784. // #endif
  1785. return this.getImageData(x, y, w, h).then(async (res) => {
  1786. var output =res;
  1787. var w = res.width;
  1788. var h = res.height;
  1789. var dst = output.data;
  1790. var d = res.data;
  1791. for (var y=0; y<h; y++) {
  1792. for (var x=0; x<w; x++) {
  1793. var off = (y*w+x)*4;
  1794. var dstOff = (y*w+(w-x-1))*4;
  1795. dst[dstOff] = d[off];
  1796. dst[dstOff+1] = d[off+1];
  1797. dst[dstOff+2] = d[off+2];
  1798. dst[dstOff+3] = d[off+3];
  1799. }
  1800. }
  1801. return {
  1802. width: res.width,
  1803. height: res.height,
  1804. data: dst
  1805. };
  1806. }).then(res => {
  1807. return this.putImageData(x, y, w, h, res);
  1808. }).catch(e => console.error(e))
  1809. }
  1810. /**
  1811. * 垂直镜像,上下居中对称图像
  1812. * @param {Object} x
  1813. * @param {Object} y
  1814. * @param {Object} w
  1815. * @param {Object} h
  1816. */
  1817. CRender.prototype.VerticallyMirror = function(x, y, w, h) {
  1818. let t = this;
  1819. let dpr = 1;
  1820. // #ifndef H5
  1821. dpr = t.ctx.dpr;
  1822. // #endif
  1823. return this.getImageData(x, y, w, h).then(async (res) => {
  1824. var output =res;
  1825. var w = res.width;
  1826. var h = res.height;
  1827. var dst = output.data;
  1828. var d = res.data;
  1829. for (var y=0; y<h; y++) {
  1830. for (var x=0; x<w; x++) {
  1831. var off = (y*w+x)*4;
  1832. var dstOff = ((h-y-1)*w+x)*4;
  1833. dst[dstOff] = d[off];
  1834. dst[dstOff+1] = d[off+1];
  1835. dst[dstOff+2] = d[off+2];
  1836. dst[dstOff+3] = d[off+3];
  1837. }
  1838. }
  1839. return {
  1840. width: res.width,
  1841. height: res.height,
  1842. data: dst
  1843. };
  1844. }).then(res => {
  1845. return this.putImageData(x, y, w, h, res);
  1846. }).catch(e => console.error(e))
  1847. }