前几天因为团队须要,折腾了一下图像液化的处理过程。
如今来整理一下思路,作个记录。canvas
用到公式以下,网上拿来的ui
话很少说,上代码
原本想尽可能写出点逼格。。。后来发现怎么写也仍是几个function搞定,就那样了。spa
(function(global) { // 计算两点距离平方 function distanceSqr( x1, y1, x2, y2 ) { return sqr(x1-x2) + sqr(y1-y2); } // 计算平方 function sqr(x) { return x*x; } // 遍历一个指定圆内的全部点 // 经过callback传入回调方法,回调传出每个点的相关信息 function eachCircleDot( imageData, ox, oy, r, callback ) { var imgWidth = imageData.width, imgHeight = imageData.height, data = imageData.data, left = ox-r, right = ox+r, top = oy-r, bottom = oy+r, dotRedOffset,dotGreenOffset,dotBlueOffset,alphaOffset; for( var x = left; x < right; x++ ) for( var y = top; y < bottom; y++ ) if( distanceSqr( x, y, ox, oy ) <= sqr(r) ) { dotRedOffset = y*imgWidth*4+x*4; dotGreenOffset = dotRedOffset + 1; dotBlueOffset = dotGreenOffset + 1; alphaOffset = dotBlueOffset + 1; callback( // 当前点的坐标 { x:x, y:y }, // 点的RGBA四个份量对应字节的下标 { r: dotRedOffset, g: dotGreenOffset, b: dotBlueOffset, a: alphaOffset, }, // 传进来的ImageData的data部分 data ); } } // 复制一个imageData的data到一个buff里 function copyImageDataBuff( imgData ) { var data = imgData.data, imgDataBuff = []; for( var i in data ) imgDataBuff[i] = data[i]; return imgDataBuff; } // 从buff按照指定坐标复制像素点数据到目标imageData里 function moveDot( imgData, dataBuff, x, y, srcX, srcY ) { var imgWidth = imgData.width, imgHeight = imgData.height, data = imgData.data; x = Math.floor(x); y = Math.floor(y); srcX = Math.floor(srcX); srcY = Math.floor(srcY); var targetStartOffset = y*imgHeight*4 + x*4, srcStartOffset = srcY*imgHeight*4 + srcX*4; for( var i = 0; i < 4; i++ ) data[ targetStartOffset + i ] = dataBuff[ srcStartOffset + i ]; } // 执行液化过程 // imgData 经过canvas的getImageData方法获得的数据对象 // cx,cy 圆心坐标 // mx,my 移动目标坐标 // r 做用半径 // strength 力度百分比(1-100) function liquify( imgData, cx, cy, mx, my, r, strenth ) { var imgDataBuff = copyImageDataBuff(imgData); eachCircleDot( imgData, cx, cy, r, function( posi ) { var tx = posi.x, ty = posi.y; var u = transFormula( cx, cy, mx, my, tx, ty, r, strenth ); moveDot( imgData, imgDataBuff, tx, ty, u.x, u.y ); function transFormula( cx, cy, mx, my, tx, ty, r, strenth ) { strenth = strenth || 100; var relativity = sqr(r) - distanceSqr( tx, ty, cx, cy ); var distanceMovedSqr = distanceSqr( mx, my, cx, cy ); var rate = sqr( relativity / ( relativity + distanceMovedSqr*(100/strenth) ) ); var ux = tx - rate * (mx-cx), uy = ty - rate * (my-cy); return { x:ux, y:uy }; } }); } // 挂到全局对象 global.LiquifyFilter = { liquify: liquify }; })(window);
使用它3d
先用canvas的code
getImageData();
方法获取到要处理图片的imageDataorm
全局做用域下调用对象
LiquifyFilter.liquify( imageData, 圆心X, 圆心Y, 目标点X, 目标点Y, 做用半径, [力度百分比] );
完成转换。blog
而后再用canvas的图片
puImageData();
把转换后的imageData输出到canvas中作用域
效果图以下