有这样一个 js 动画

原图

代码如下,问一下怎么样转成 C# 代码

原图

代码如下,问一下怎么样转成 C# 代码
<!DOCTYPE html>
<canvas class="center" id="canvas_4" style="border: 1px solid blue" width="319" height="250">sorry, no canvas, please, upgrade your browser</canvas>
<img border="1" id="imgTest" width="319" height="250" src="..."/>
<script type="text/javascript">
function getCanvas(canvasId) {
//alert(canvasId)
"use strict";
if (canvasId) {
return document.getElementById(canvasId);
} else {
// If not "canvasId" param is present, use the default canvasId registered for this function
return document.getElementById(getCanvas.defaultCanvasId);
}
}
//getCanvas.defaultCanvasId = "canvas_1";
function getContext2d(canvasId) {
"use strict";
return getCanvas(canvasId).getContext("2d");
}
function grabImageData(canvasId, onPixelsLoadedCallback) {
"use strict";
var canvas, context2d, width, height, img, imageData;
canvas = getCanvas(canvasId);
context2d = getContext2d(canvasId);
// read the width and height of the canvas that
// matches the image dimensions on purpose (just to keep the code simple)
width = canvas.width;
height = canvas.height;
img = new Image();
img.src = document.getElementById("imgTest").src;
// We have to wait for the image to load, after the loading completes, this callback will be executed
img.onload = function() {
// Draw the loaded image into the canvas
context2d.drawImage(img, 0, 0);
// Retrieve the pixels from the canvas and pass them to the callback as a parameter
onPixelsLoadedCallback(canvasId, context2d.getImageData(0, 0, width, height));
}
}
function createCompatibleImageData(canvasId, imgData) {
"use strict";
var context2d = getContext2d(canvasId);
return context2d.createImageData(imgData.width, imgData.height);
}
// This renders the "imageData" parameter into the canvas
function drawPixels(canvasId, imageData) {
"use strict";
var context2d = getContext2d(canvasId);
context2d.putImageData(imageData, 0, 0);
}
// Copy the pixels of the "srcPixels" ImageData parameter
// into the "dstPixels" parameter
function copyImageData(srcPixels, dstPixels, width, height) {
"use strict";
var x, y, position;
for (y = 0; y < height; ++y) {
for (x = 0; x < width; ++x) {
position = y * width + x;
position *= 4;
dstPixels[position + 0] = srcPixels[position + 0];
dstPixels[position + 1] = srcPixels[position + 1];
dstPixels[position + 2] = srcPixels[position + 2];
dstPixels[position + 3] = srcPixels[position + 3];
}
}
}
function liquify(canvasId, originalImageData) {
"use strict";
var x, y, width, height, size, radius, centerX, centerY, sourcePosition, destPosition;
var sourceImgData = originalImageData;
var destImgData = createCompatibleImageData(canvasId, sourceImgData);
var srcPixels = sourceImgData.data;
var dstPixels = destImgData.data;
var radiusSquared;
width = originalImageData.width;
height = originalImageData.height;
centerX = Math.floor(width / 2);
centerY = Math.floor(height / 2);
size = width < height ? width : height;
radius = Math.floor(size / 2);
//radius = 50
radiusSquared = radius * radius;
copyImageData(srcPixels, dstPixels, width, height);
drawPixels(canvasId, destImgData);
function animate(c, growConstant) {
"use strict";
var r, alpha, angle, sourcePosition, destPosition, newX, newY, degrees, delayBetweenFrames;
var k, pos0, pos1, pos2, pos3, deltaX, deltaY, x0, xf, y0, yf, componentX0, componentX1, finalPixelComponent;
var interpolationFactor;
// Iterate over the interest square region
for (y = -radius; y < radius; ++y) {
for (x = -radius; x < radius; ++x) {
// Check if the pixel is inside the effect circle
if (x * x + y * y <= radiusSquared) {
// Get the pixel array position
destPosition = (y + centerY) * width + x + centerX;
destPosition *= 4;
// Transform the pixel Cartesian coordinates (x, y) to polar coordinates (r, alpha)
r = Math.sqrt(x * x + y * y);
alpha = Math.atan2(y, x);
// Remember that the angle alpha is in radians, transform it to degrees
degrees = (alpha * 180.0) / Math.PI;
// Calculate the interpolation factor
interpolationFactor = r / radius;
// Do the interpolation
r = interpolationFactor * r + (1.0 - interpolationFactor) * c * Math.sqrt(r);
// r/rmax * r + (1-r/rmax) *c * √r
// Transform back from polar coordinates to Cartesian
alpha = (degrees * Math.PI) / 180.0;
newY = r * Math.sin(alpha);
newX = r * Math.cos(alpha);
// Calculate the (x, y) coordinates of the transformation and keep
// the fractional in the delta variables
x0 = Math.floor(newX);
xf = x0 + 1;
y0 = Math.floor(newY);
yf = y0 + 1;
deltaX = newX - x0;
deltaY = newY - y0;
// Calculate the array position for the pixels (x, y), (x + 1, y), (x, y + 1) and (x + 1, y + 1)
pos0 = ((y0 + centerY) * width + x0 + centerX) * 4;
pos1 = ((y0 + centerY) * width + xf + centerX) * 4;
pos2 = ((yf + centerY) * width + x0 + centerX) * 4;
pos3 = ((yf + centerY) * width + xf + centerX) * 4;
// Do the bilinear interpolation thing for every component of the pixel
for (k = 0; k < 4; ++k) {
// Interpolate the pixels (x, y) and (x + 1, y)
componentX0 = (srcPixels[pos1 + k] - srcPixels[pos0 + k]) * deltaX + srcPixels[pos0 + k];
// Interpolate the pixels immediately below of (x, y), those are (x, y + 1) and (x + 1, y + 1)
componentX1 = (srcPixels[pos3 + k] - srcPixels[pos2 + k]) * deltaX + srcPixels[pos2 + k];
// Interpolate again the interpolated components
finalPixelComponent = (componentX1 - componentX0) * deltaY + componentX0;
// Set the pixel in the image buffer but first check if it lies between 0 and 255, if not, clamp it to that range
dstPixels[destPosition + k] = finalPixelComponent > 255 ? 255 : (finalPixelComponent < 0 ? 0 : finalPixelComponent);
}
}
}
}
drawPixels(canvasId, destImgData);
setTimeout(function() {
if (c > 15.0) {
growConstant = false;
}
if (c <= 1.0) {
growConstant = true;
}
// Depending on the flag "leftToRight" value, increase/decrease
// the "step" parameter by a small value at a time
animate(c + (growConstant ? 0.1 : -0.1), growConstant);
}, 10);
}
animate(1, true);
}
//(function () {
// "use strict";
grabImageData("canvas_4", liquify);
//})();
</script>
解决方案
200
private void button1_Click(object sender, EventArgs e) {
m_imgSrc = Image.FromFile("./test.png");
Bitmap bmp = (Bitmap)m_imgSrc;
BitmapData bmpData = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
m_byColorsSrc = new byte[bmpData.Height * bmpData.Stride];
Marshal.Copy(bmpData.Scan0, m_byColorsSrc, 0, m_byColorsSrc.Length);
bmp.UnlockBits(bmpData);
centerX = (int)Math.Floor((double)bmp.Width / 2);
centerY = (int)Math.Floor((double)bmp.Height / 2);
//size = width < height ? width : height;
radius = (int)Math.Floor((double)(bmp.Width < bmp.Height ? bmp.Width : bmp.Height) / 2);
radiusSquared = radius * radius;
pictureBox1.Image = this.Animate(1F);
timer1.Interval = 10;
timer1.Start();
}
private int radius;
private int radiusSquared;
private int centerX, centerY;
private byte[] m_byColorsSrc;
//private byte[] by_clrsDst;
private Image m_imgSrc;
private Image Animate(float fC) {
int destPosition;
double r, alpha, newX, newY, deltaX, deltaY, degrees, componentX0, componentX1, finalPixelComponent;
int k, pos0, pos1, pos2, pos3, x0, xf, y0, yf;
double interpolationFactor;
Bitmap bmpRet = new Bitmap(m_imgSrc.Width, m_imgSrc.Height, PixelFormat.Format32bppArgb);
BitmapData bmpData = bmpRet.LockBits(new Rectangle(Point.Empty, bmpRet.Size), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
byte[] byColors = new byte[bmpData.Height * bmpData.Stride];
//Array.Copy(m_byColorsSrc, byColors, byColors.Length);//拷贝原始图像 注释就是圆形
// Iterate over the interest square region
for (int y = -radius; y < radius; ++y) {
for (int x = -radius; x < radius; ++x) {
// Check if the pixel is inside the effect circle
if (x * x + y * y <= radiusSquared) {
// Get the pixel array position
//destPosition = (y + centerY) * width + x + centerX;
//destPosition *= 4;
destPosition = (y + centerY) * bmpData.Stride + (x + centerX) * 4;
// Transform the pixel Cartesian coordinates (x, y) to polar coordinates (r, alpha)
r = Math.Sqrt(x * x + y * y);
alpha = Math.Atan2(y, x);
// Remember that the angle alpha is in radians, transform it to degrees
degrees = (alpha * 180.0) / Math.PI;
// Calculate the interpolation factor
interpolationFactor = r / radius;
// Do the interpolation
r = interpolationFactor * r + (1.0 - interpolationFactor) * fC * Math.Sqrt(r);
// r/rmax * r + (1-r/rmax) *c * √r
// Transform back from polar coordinates to Cartesian
alpha = (degrees * Math.PI) / 180.0;
newY = r * Math.Sin(alpha);
newX = r * Math.Cos(alpha);
// Calculate the (x, y) coordinates of the transformation and keep
// the fractional in the delta variables
x0 = (int)Math.Floor(newX);
xf = x0 + 1;
y0 = (int)Math.Floor(newY);
yf = y0 + 1;
deltaX = newX - x0;
deltaY = newY - y0;
// Calculate the array position for the pixels (x, y), (x + 1, y), (x, y + 1) and (x + 1, y + 1)
//pos0 = ((y0 + centerY) * width + x0 + centerX) * 4;
//pos1 = ((y0 + centerY) * width + xf + centerX) * 4;
//pos2 = ((yf + centerY) * width + x0 + centerX) * 4;
//pos3 = ((yf + centerY) * width + xf + centerX) * 4;
pos0 = (y0 + centerY) * bmpData.Stride + (x0 + centerX) * 4;
pos1 = (y0 + centerY) * bmpData.Stride + (xf + centerX) * 4;
pos2 = (yf + centerY) * bmpData.Stride + (x0 + centerX) * 4;
pos3 = (yf + centerY) * bmpData.Stride + (xf + centerX) * 4;
//这里本人不知道为什么 pos2 会造成下面索引越界 原因是 yf 算出来是 125 centery也是125 图像是250 高度
//对哦 假如当作索引 pos2 最大只能是 249 不能 250 但是 上面 Math.Floor 是向下取整啊
//本人不知道 上面的 x0 + 1 和 yo + 1 是什么意思 莫非js索引是 1 开始的?
if (pos2 > m_byColorsSrc.Length || pos3 > m_byColorsSrc.Length) {
int a = 0;
break;//越界本人就跳出了
//timer1.Stop();
a++;//断掉调试用
}
// Do the bilinear interpolation thing for every component of the pixel
for (k = 0; k < 4; ++k) {
// Interpolate the pixels (x, y) and (x + 1, y)
componentX0 = (m_byColorsSrc[pos1 + k] - m_byColorsSrc[pos0 + k]) * deltaX + m_byColorsSrc[pos0 + k];
// Interpolate the pixels immediately below of (x, y), those are (x, y + 1) and (x + 1, y + 1)
componentX1 = (m_byColorsSrc[pos3 + k] - m_byColorsSrc[pos2 + k]) * deltaX + m_byColorsSrc[pos2 + k];
// Interpolate again the interpolated components
finalPixelComponent = (componentX1 - componentX0) * deltaY + componentX0;
// Set the pixel in the image buffer but first check if it lies between 0 and 255, if not, clamp it to that range
byColors[destPosition + k] = (byte)(finalPixelComponent > 255 ? 255 : (finalPixelComponent < 0 ? 0 : finalPixelComponent));
}
}
}
}
Marshal.Copy(byColors, 0, bmpData.Scan0, byColors.Length);
bmpRet.UnlockBits(bmpData);
return bmpRet;
}
private float m_fC;
private float m_fIncreament = 0.1F;
private void timer1_Tick(object sender, EventArgs e) {
if (m_fC > 15.0) {
m_fIncreament = -0.1F;
}
if (m_fC <= 1.0) {
m_fIncreament = 0.1F;
}
// Depending on the flag "leftToRight" value, increase/decrease
// the "step" parameter by a small value at a time
pictureBox1.Image = this.Animate(m_fC += m_fIncreament);
Console.WriteLine(m_fC);
}

说实在的 翻译那个代码太痛苦了 很多变量没有用到的 而且很多没有必要的变量 看着一大堆的变量 本人也是有点蒙蔽
所以还是建议 把那两个循环理解一下原理 然后本人写比较靠不
还有一点问题 你本人处理吧
你说你转的有问题 估计是 在取 width 的时候
//destPosition = (y + centerY) * width + x + centerX; //destPosition *= 4; destPosition = (y + centerY) * bmpData.Stride + (x + centerX) * 4;
他是用注释的地方地位坐标的
在.NET中用 lockbits 拷贝出来的数组 他的宽度 不是按照 width 来计算 每一行所占用的空间 貌似他会自动补齐 每一行像素所占用的字节数是貌似是4的倍数 所以用Stride来确定每行所占用的像素字节 而不是 y * width * (多少位 / 8) 假如用这种方式 那么数据就错位了