有这样一个 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) 假如用这种方式 那么数据就错位了