// Warp an image and return a new Bitmap32 holding the result. public Bitmap32 Warp(WarpOperations warp_op, bool lock_result) { // Make a copy of this Bitmap32. Bitmap32 result = this.Clone(); // Lock both bitmaps. bool was_locked = this.IsLocked; this.LockBitmap(); result.LockBitmap(); // Warp the image. WarpImage(this, result, warp_op); // Unlock the bitmaps. if (!lock_result) { result.UnlockBitmap(); } if (!was_locked) { this.UnlockBitmap(); } // Return the result. return(result); }
private static void WarpImage(Warping bm_src, Warping bm_dest, WarpOperations warp_op) { double xmid = bm_dest.Width / 2.0; double ymid = bm_dest.Height / 2.0; double rmax = bm_dest.Width * 0.75; int ix_max = bm_src.Width - 2; int iy_max = bm_src.Height - 2; double x0, y0; for (int y1 = 0; y1 < bm_dest.Height; y1++) { for (int x1 = 0; x1 < bm_dest.Width; x1++) { MapPixel(warp_op, xmid, ymid, rmax, x1, y1, out x0, out y0); int ix0 = (int)x0; int iy0 = (int)y0; if ((ix0 < 0) || (ix0 > ix_max) || (iy0 < 0) || (iy0 > iy_max)) { bm_dest.SetPixel(x1, y1, 255, 255, 255, 255); } else { double dx0 = x0 - ix0; double dy0 = y0 - iy0; double dx1 = 1 - dx0; double dy1 = 1 - dy0; byte r00, g00, b00, a00, r01, g01, b01, a01, r10, g10, b10, a10, r11, g11, b11, a11; bm_src.GetPixel(ix0, iy0, out r00, out g00, out b00, out a00); bm_src.GetPixel(ix0, iy0 + 1, out r01, out g01, out b01, out a01); bm_src.GetPixel(ix0 + 1, iy0, out r10, out g10, out b10, out a10); bm_src.GetPixel(ix0 + 1, iy0 + 1, out r11, out g11, out b11, out a11); int r = (int)( r00 * dx1 * dy1 + r01 * dx1 * dy0 + r10 * dx0 * dy1 + r11 * dx0 * dy0); int g = (int)( g00 * dx1 * dy1 + g01 * dx1 * dy0 + g10 * dx0 * dy1 + g11 * dx0 * dy0); int b = (int)( b00 * dx1 * dy1 + b01 * dx1 * dy0 + b10 * dx0 * dy1 + b11 * dx0 * dy0); int a = (int)( a00 * dx1 * dy1 + a01 * dx1 * dy0 + a10 * dx0 * dy1 + a11 * dx0 * dy0); bm_dest.SetPixel(x1, y1, (byte)r, (byte)g, (byte)b, (byte)a); } } } }
public Warping Warp(WarpOperations warp_op, bool lock_result) { Warping result = this.Clone(); bool was_locked = this.IsLocked; this.LockBitmap(); result.LockBitmap(); WarpImage(this, result, warp_op); if (!lock_result) { result.UnlockBitmap(); } if (!was_locked) { this.UnlockBitmap(); } return(result); }
// Map the output pixel (x1, y1) back to the input pixel (x0, y0). private static void MapPixel(WarpOperations warp_op, double xmid, double ymid, double rmax, int x1, int y1, out double x0, out double y0) { const double pi_over_2 = Math.PI / 2.0; const double K = 100.0; const double offset = -pi_over_2; double dx, dy, r1, r2; switch (warp_op) { case WarpOperations.Identity: x0 = x1; y0 = y1; break; case WarpOperations.FishEye: dx = x1 - xmid; dy = y1 - ymid; r1 = Math.Sqrt(dx * dx + dy * dy); if (r1 == 0) { x0 = xmid; y0 = ymid; } else { r2 = rmax / 2 * (1 / (1 - r1 / rmax) - 1); x0 = dx * r2 / r1 + xmid; y0 = dy * r2 / r1 + ymid; } break; case WarpOperations.Twist: dx = x1 - xmid; dy = y1 - ymid; r1 = Math.Sqrt(dx * dx + dy * dy); if (r1 == 0) { x0 = 0; y0 = 0; } else { double theta = Math.Atan2(dx, dy) - r1 / K - offset; x0 = r1 * Math.Cos(theta); y0 = r1 * Math.Sin(theta); } x0 = x0 + xmid; y0 = y0 + ymid; break; case WarpOperations.Wave: x0 = x1; y0 = y1 - 10 * (Math.Sin(x1 / 50.0 * Math.PI) + 1) + 5; break; case WarpOperations.SmallTop: dx = xmid - x1; dx = dx * ymid * 2 / (y1 + 1); x0 = xmid - dx; y0 = y1; break; case WarpOperations.Wiggles: dx = xmid - x1; dx = dx * (Math.Sin(y1 / 50.0 * Math.PI) / 2 + 1.5); x0 = xmid - dx; y0 = y1; break; case WarpOperations.DoubleWave: x0 = x1 - 10 * (Math.Sin(y1 / 50.0 * Math.PI) + 1) + 5; y0 = y1 - 10 * (Math.Sin(x1 / 50.0 * Math.PI) + 1) + 5; break; default: // Flip vertically and horizontally. x0 = 2 * xmid - x1; y0 = 2 * ymid - y1; break; } }
// Transform the image. private static void WarpImage(Bitmap32 bm_src, Bitmap32 bm_dest, WarpOperations warp_op) { // Calculate some image information. double xmid = bm_dest.Width / 2.0; double ymid = bm_dest.Height / 2.0; double rmax = bm_dest.Width * 0.75; int ix_max = bm_src.Width - 2; int iy_max = bm_src.Height - 2; // Generate a result for each output pixel. double x0, y0; for (int y1 = 0; y1 < bm_dest.Height; y1++) { for (int x1 = 0; x1 < bm_dest.Width; x1++) { // Map back to the source image. MapPixel(warp_op, xmid, ymid, rmax, x1, y1, out x0, out y0); // Interpolate to get the result pixel's value. // Find the next smaller integral position. int ix0 = (int)x0; int iy0 = (int)y0; // See if this is out of bounds. if ((ix0 < 0) || (ix0 > ix_max) || (iy0 < 0) || (iy0 > iy_max)) { // The point is outside the image. Use white. bm_dest.SetPixel(x1, y1, 255, 255, 255, 255); } else { // The point lies within the image. // Calculate its value. double dx0 = x0 - ix0; double dy0 = y0 - iy0; double dx1 = 1 - dx0; double dy1 = 1 - dy0; // Get the colors of the surrounding pixels. byte r00, g00, b00, a00, r01, g01, b01, a01, r10, g10, b10, a10, r11, g11, b11, a11; bm_src.GetPixel(ix0, iy0, out r00, out g00, out b00, out a00); bm_src.GetPixel(ix0, iy0 + 1, out r01, out g01, out b01, out a01); bm_src.GetPixel(ix0 + 1, iy0, out r10, out g10, out b10, out a10); bm_src.GetPixel(ix0 + 1, iy0 + 1, out r11, out g11, out b11, out a11); // Compute the weighted average. int r = (int)( r00 * dx1 * dy1 + r01 * dx1 * dy0 + r10 * dx0 * dy1 + r11 * dx0 * dy0); int g = (int)( g00 * dx1 * dy1 + g01 * dx1 * dy0 + g10 * dx0 * dy1 + g11 * dx0 * dy0); int b = (int)( b00 * dx1 * dy1 + b01 * dx1 * dy0 + b10 * dx0 * dy1 + b11 * dx0 * dy0); int a = (int)( a00 * dx1 * dy1 + a01 * dx1 * dy0 + a10 * dx0 * dy1 + a11 * dx0 * dy0); bm_dest.SetPixel(x1, y1, (byte)r, (byte)g, (byte)b, (byte)a); } } } }
private static void MapPixel(WarpOperations warp_op, double xmid, double ymid, double rmax, int x1, int y1, out double x0, out double y0) { const double pi_over_2 = Math.PI / 2.0; const double K = 100.0; const double offset = -pi_over_2; double dx, dy, r1, r2; switch (warp_op) { case WarpOperations.Identity: x0 = x1; y0 = y1; break; case WarpOperations.FishEye: dx = x1 - xmid; dy = y1 - ymid; r1 = Math.Sqrt(dx * dx + dy * dy); if (r1 == 0) { x0 = xmid; y0 = ymid; } else { r2 = rmax / 2 * (1 / (1 - r1 / rmax) - 1); x0 = dx * r2 / r1 + xmid; y0 = dy * r2 / r1 + ymid; } break; case WarpOperations.Twist: dx = x1 - xmid; dy = y1 - ymid; r1 = Math.Sqrt(dx * dx + dy * dy); if (r1 == 0) { x0 = 0; y0 = 0; } else { double theta = Math.Atan2(dx, dy) - r1 / K - offset; x0 = r1 * Math.Cos(theta); y0 = r1 * Math.Sin(theta); } x0 = x0 + xmid; y0 = y0 + ymid; break; case WarpOperations.Wave: x0 = x1; y0 = y1 - 10 * (Math.Sin(x1 / 50.0 * Math.PI) + 1) + 5; break; case WarpOperations.SmallTop: dx = xmid - x1; dx = dx * ymid * 2 / (y1 + 1); x0 = xmid - dx; y0 = y1; break; case WarpOperations.Wiggles: dx = xmid - x1; dx = dx * (Math.Sin(y1 / 50.0 * Math.PI) / 2 + 1.5); x0 = xmid - dx; y0 = y1; break; case WarpOperations.DoubleWave: x0 = x1 - 10 * (Math.Sin(y1 / 50.0 * Math.PI) + 1) + 5; y0 = y1 - 10 * (Math.Sin(x1 / 50.0 * Math.PI) + 1) + 5; break; default: x0 = 2 * xmid - x1; y0 = 2 * ymid - y1; break; } }
public Warping Warp(WarpOperations warp_op, bool lock_result) { Warping result = this.Clone(); bool was_locked = this.IsLocked; this.LockBitmap(); result.LockBitmap(); WarpImage(this, result, warp_op); if (!lock_result) result.UnlockBitmap(); if (!was_locked) this.UnlockBitmap(); return result; }