From 1126997d772a2d005e5dc30d2e818c905539b5b9 Mon Sep 17 00:00:00 2001 From: Vilius Date: Tue, 27 Dec 2016 18:35:52 +0200 Subject: [PATCH] v2.3 - updated image data split for multicore function. --- dist/hermite.js | 3 +- src/hermite.js | 119 +++++++++++++++++++++++------------------------- 2 files changed, 58 insertions(+), 64 deletions(-) diff --git a/dist/hermite.js b/dist/hermite.js index 5b9d1be..7a6cfe4 100644 --- a/dist/hermite.js +++ b/dist/hermite.js @@ -1,5 +1,6 @@ //Hermite resize - fast image resize/resample using Hermite filter. +//Version: 2.2 //Author: ViliusL //https://github.com/viliusle/Hermite-resize //http://viliusle.github.io/miniPaint/ -function Hermite_class(){var a,c,b=[];this.init=function(){a=navigator.hardwareConcurrency||4}(),this.getCores=function(){return a},this.resample_auto=function(a,b,c,d,e){var f=this.getCores();window.Worker&&f>1?this.resample(a,b,c,d,e):(this.resample_single(a,b,c,!0),e())},this.resize_image=function(a,b,c,d,e){var f=document.getElementById(a),g=document.createElement("canvas");g.width=f.width,g.height=f.height;var h=g.getContext("2d");if(h.drawImage(f,0,0),void 0==b&&void 0==c&&void 0!=d&&(b=f.width/100*d,c=f.height/100*d),void 0==c){var i=f.width/b;c=f.height/i}b=Math.round(b),c=Math.round(c);var j=function(){var a=g.toDataURL();f.width=b,f.height=c,f.src=a,delete a,delete g};void 0==e||1==e?this.resample(g,b,c,!0,j):(this.resample_single(g,b,c,!0),j())},this.resample=function(d,e,f,g,h){var i=d.width,j=d.height;e=Math.round(e),f=Math.round(f);var k=Math.ceil(j/f/2);if(b.length>0)for(var l=0;lj&&(q=j-p),q<1||(n[l]=m.getImageData(0,p,i,q),p=Math.ceil(f/a)*l,q=Math.ceil(f/a),p+q>f&&(q=f-p),q<1||(o[l]=!0))}g===!0?(d.width=e,d.height=f):m.clearRect(0,0,i,j);for(var r=0,l=0;l=m||H<0))for(var I=Math.abs(C-(H+.5))/k,J=(t+.5)*h,K=I*I,L=F;L=1)){v=2*N*N*N-3*N*N+1;var O=4*(L+H*d);B+=v*l[O+3],x+=v,l[O+3]<255&&(v=v*l[O+3]/250),y+=v*l[O],z+=v*l[O+1],A+=v*l[O+2],w+=v}}p[u]=y/w,p[u+1]=z/w,p[u+2]=A/w,p[u+3]=B/x}var P={core:b,target:p};postMessage(P,[p.buffer])}}.toString(),")()"],{type:"application/javascript"})),this.resample_single=function(a,b,c,d){var e=a.width,f=a.height;b=Math.round(b),c=Math.round(c);for(var g=e/b,h=f/c,i=Math.ceil(g/2),j=Math.ceil(h/2),k=a.getContext("2d"),l=k.getImageData(0,0,e,f),m=k.createImageData(b,c),n=l.data,o=m.data,p=0;p=1)){s=2*K*K*K-3*K*K+1;var L=4*(I+C*e);y+=s*n[L+3],u+=s,n[L+3]<255&&(s=s*n[L+3]/250),v+=s*n[L],w+=s*n[L+1],x+=s*n[L+2],t+=s}}o[r]=v/t,o[r+1]=w/t,o[r+2]=x/t,o[r+3]=y/u}d===!0?(a.width=b,a.height=c):k.clearRect(0,0,e,f),k.putImageData(m,0,0)}} \ No newline at end of file +function Hermite_class(){var a,c,b=[];this.init=function(){a=navigator.hardwareConcurrency||4}(),this.getCores=function(){return a},this.resample_auto=function(a,b,c,d,e){var f=this.getCores();window.Worker&&f>1?this.resample(a,b,c,d,e):(this.resample_single(a,b,c,!0),e())},this.resize_image=function(a,b,c,d,e){var f=document.getElementById(a),g=document.createElement("canvas");g.width=f.width,g.height=f.height;var h=g.getContext("2d");if(h.drawImage(f,0,0),void 0==b&&void 0==c&&void 0!=d&&(b=f.width/100*d,c=f.height/100*d),void 0==c){var i=f.width/b;c=f.height/i}b=Math.round(b),c=Math.round(c);var j=function(){var a=g.toDataURL();f.width=b,f.height=c,f.src=a,delete a,delete g};void 0==e||1==e?this.resample(g,b,c,!0,j):(this.resample_single(g,b,c,!0),j())},this.resample=function(d,e,f,g,h){var i=d.width,j=d.height;e=Math.round(e),f=Math.round(f);var k=j/f;if(b.length>0)for(var l=0;lj)){p=q+o-1,p=Math.min(p,j-1);var r=o;r=Math.min(o,j-q),n[l]={},n[l].source=m.getImageData(0,q,i,o),n[l].target=!0,n[l].start_y=Math.ceil(q/k),n[l].height=r}}g===!0?(d.width=e,d.height=f):m.clearRect(0,0,i,j);for(var s=0,l=0;l=1)){s=2*K*K*K-3*K*K+1;var L=4*(I+E*c);y+=s*k[L+3],u+=s,k[L+3]<255&&(s=s*k[L+3]/250),v+=s*k[L],w+=s*k[L+1],x+=s*k[L+2],t+=s}}o[r]=v/t,o[r+1]=w/t,o[r+2]=x/t,o[r+3]=y/u}var M={core:b,target:o};postMessage(M,[o.buffer])}}.toString(),")()"],{type:"application/javascript"})),this.resample_single=function(a,b,c,d){var e=a.width,f=a.height;b=Math.round(b),c=Math.round(c);for(var g=e/b,h=f/c,i=Math.ceil(g/2),j=Math.ceil(h/2),k=a.getContext("2d"),l=k.getImageData(0,0,e,f),m=k.createImageData(b,c),n=l.data,o=m.data,p=0;p=1)){s=2*K*K*K-3*K*K+1;var L=4*(I+E*e);y+=s*n[L+3],u+=s,n[L+3]<255&&(s=s*n[L+3]/250),v+=s*n[L],w+=s*n[L+1],x+=s*n[L+2],t+=s}}o[r]=v/t,o[r+1]=w/t,o[r+2]=x/t,o[r+3]=y/u}d===!0?(a.width=b,a.height=c):k.clearRect(0,0,e,f),k.putImageData(m,0,0)}} \ No newline at end of file diff --git a/src/hermite.js b/src/hermite.js index e8d5708..3340eb9 100644 --- a/src/hermite.js +++ b/src/hermite.js @@ -1,6 +1,6 @@ - -/** +/* * Hermite resize - fast image resize/resample using Hermite filter. + * Version: 2.2 * Author: ViliusL * demo: http://viliusle.github.io/miniPaint/ */ @@ -114,7 +114,7 @@ function Hermite_class() { var height_source = canvas.height; width = Math.round(width); height = Math.round(height); - var ratio_h_half = Math.ceil(height_source / height / 2); + var ratio_h = height_source / height; //stop old workers if (workers_archive.length > 0) { @@ -129,32 +129,30 @@ function Hermite_class() { var ctx = canvas.getContext("2d"); //prepare source and target data for workers - var source = new Array(cores); - var target = new Array(cores); + var data_part = []; + var block_height = Math.ceil(height_source / cores / 2) * 2; + var end_y = -1; for (var c = 0; c < cores; c++) { - //source - var offset_y = Math.ceil(height_source / cores) * c; - var block_height = Math.ceil(height_source / cores) + ratio_h_half * cores; - if (offset_y + block_height > height_source) { - block_height = height_source - offset_y; - } - if (block_height < 1) { + //source + var offset_y = end_y + 1; + if (offset_y > height_source) { //size too small, nothing left for this core continue; } - source[c] = ctx.getImageData(0, offset_y, width_source, block_height); + + end_y = offset_y + block_height - 1; + end_y = Math.min(end_y, height_source - 1); + + var current_block_height = block_height; + current_block_height = Math.min(block_height, height_source - offset_y); - //target - offset_y = Math.ceil(height / cores) * c; - block_height = Math.ceil(height / cores); - if (offset_y + block_height > height) { - block_height = height - offset_y; - } - if (block_height < 1) { - //size too small, nothing left for this core - continue; - } - target[c] = true; + //console.log('source split: ', '#'+c, offset_y, end_y, 'height: '+current_block_height); + + data_part[c] = {}; + data_part[c].source = ctx.getImageData(0, offset_y, width_source, block_height); + data_part[c].target = true; + data_part[c].start_y = Math.ceil(offset_y / ratio_h); + data_part[c].height = current_block_height; } //clear and resize canvas @@ -168,7 +166,7 @@ function Hermite_class() { //start var workers_in_use = 0; for (var c = 0; c < cores; c++) { - if (target[c] == undefined) { + if (data_part[c].target == undefined) { //no job for this worker continue; } @@ -183,25 +181,23 @@ function Hermite_class() { delete workers_archive[core]; //draw - target[core] = ctx.createImageData(width, Math.ceil(height / cores)); - target[core].data.set(event.data.target); - var y = Math.ceil(height / cores) * core; - ctx.putImageData(target[core], 0, y); - + var height_part = Math.ceil(data_part[core].height / ratio_h); + data_part[core].target = ctx.createImageData(width, height_part); + data_part[core].target.data.set(event.data.target); + ctx.putImageData(data_part[core].target, 0, data_part[core].start_y); + if (workers_in_use <= 0) { //finish on_finish(); } }; - var objData = { width_source: width_source, - height_source: height_source, + height_source: data_part[c].height, width: width, - height: height, + height: Math.ceil(data_part[c].height / ratio_h), core: c, - cores: cores, - source: source[c].data.buffer, + source: data_part[c].source.data.buffer, }; my_worker.postMessage(objData, [objData.source]); } @@ -213,30 +209,23 @@ function Hermite_class() { //begin worker onmessage = function (event) { var core = event.data.core; - var cores = event.data.cores; var width_source = event.data.width_source; var height_source = event.data.height_source; var width = event.data.width; var height = event.data.height; - + var ratio_w = width_source / width; var ratio_h = height_source / height; var ratio_w_half = Math.ceil(ratio_w / 2); var ratio_h_half = Math.ceil(ratio_h / 2); - + var source = new Uint8ClampedArray(event.data.source); var source_h = source.length / width_source / 4; - - var target_size = width * Math.ceil(height / cores) * 4; + var target_size = width * height * 4; var target_memory = new ArrayBuffer(target_size); var target = new Uint8ClampedArray(target_memory, 0, target_size); - - //j position in original source = j + d_h - d_h_source; - var d_h_source = Math.ceil(height_source / cores) * core; - var d_h = Math.ceil(height / cores) * core; - //calculate - for (var j = 0; j < Math.ceil(height / cores); j++) { + for (var j = 0; j < height; j++) { for (var i = 0; i < width; i++) { var x2 = (i + j * width) * 4; var weight = 0; @@ -246,22 +235,22 @@ function Hermite_class() { var gx_g = 0; var gx_b = 0; var gx_a = 0; - var center_y = (d_h + j + 0.5) * ratio_h - d_h_source; - - var yy_start = Math.floor(j * ratio_h); - var yy_stop = Math.ceil((d_h + j + 1) * ratio_h - d_h_source); + var center_y = j * ratio_h; + var xx_start = Math.floor(i * ratio_w); var xx_stop = Math.ceil((i + 1) * ratio_w); + var yy_start = Math.floor(j * ratio_h); + var yy_stop = Math.ceil((j + 1) * ratio_h); + + xx_stop = Math.min(xx_stop, width_source); + yy_stop = Math.min(yy_stop, height_source); + for (var yy = yy_start; yy < yy_stop; yy++) { - if (yy >= source_h || yy < 0) { - //extra border check - continue; - } - var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half; - var center_x = (i + 0.5) * ratio_w; + var dy = Math.abs(center_y - yy) / ratio_h_half; + var center_x = i * ratio_w; var w0 = dy * dy; //pre-calc part of w for (var xx = xx_start; xx < xx_stop; xx++) { - var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half; + var dx = Math.abs(center_x - xx) / ratio_w_half; var w = Math.sqrt(w0 + dx * dx); if (w >= 1) { //pixel too far @@ -336,17 +325,21 @@ function Hermite_class() { var gx_g = 0; var gx_b = 0; var gx_a = 0; - var center_y = (j + 0.5) * ratio_h; + var center_y = j * ratio_h; + + var xx_start = Math.floor(i * ratio_w); + var xx_stop = Math.ceil((i + 1) * ratio_w); var yy_start = Math.floor(j * ratio_h); var yy_stop = Math.ceil((j + 1) * ratio_h); + xx_stop = Math.min(xx_stop, width_source); + yy_stop = Math.min(yy_stop, height_source); + for (var yy = yy_start; yy < yy_stop; yy++) { - var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half; - var center_x = (i + 0.5) * ratio_w; + var dy = Math.abs(center_y - yy) / ratio_h_half; + var center_x = i * ratio_w; var w0 = dy * dy; //pre-calc part of w - var xx_start = Math.floor(i * ratio_w); - var xx_stop = Math.ceil((i + 1) * ratio_w); for (var xx = xx_start; xx < xx_stop; xx++) { - var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half; + var dx = Math.abs(center_x - xx) / ratio_w_half; var w = Math.sqrt(w0 + dx * dx); if (w >= 1) { //pixel too far