/// <summary> /// Rotates the bitmap in 90?steps clockwise and returns a new rotated WriteableBitmap. /// </summary> /// <param name="bmp">The WriteableBitmap.</param> /// <param name="angle">The angle in degrees the bitmap should be rotated in 90?steps clockwise.</param> /// <returns>A new WriteableBitmap that is a rotated version of the input.</returns> public static WriteableBitmap Rotate(this WriteableBitmap bmp, int angle) { using (var context = bmp.GetBitmapContext(ReadWriteMode.ReadOnly)) { // Use refs for faster access (really important!) speeds up a lot! var w = context.Width; var h = context.Height; var p = context.Pixels; var i = 0; WriteableBitmap result = null; angle %= 360; if (angle > 0 && angle <= 90) { result = BitmapFactory.New(h, w); using (var destContext = result.GetBitmapContext()) { var rp = destContext.Pixels; for (var x = 0; x < w; x++) { for (var y = h - 1; y >= 0; y--) { var srcInd = y * w + x; rp[i] = p[srcInd]; i++; } } } } else if (angle > 90 && angle <= 180) { result = BitmapFactory.New(w, h); using (var destContext = result.GetBitmapContext()) { var rp = destContext.Pixels; for (var y = h - 1; y >= 0; y--) { for (var x = w - 1; x >= 0; x--) { var srcInd = y * w + x; rp[i] = p[srcInd]; i++; } } } } else if (angle > 180 && angle <= 270) { result = BitmapFactory.New(h, w); using (var destContext = result.GetBitmapContext()) { var rp = destContext.Pixels; for (var x = w - 1; x >= 0; x--) { for (var y = 0; y < h; y++) { var srcInd = y * w + x; rp[i] = p[srcInd]; i++; } } } } else { result = bmp.Clone(); } return(result); } }
/// <summary> /// Rotates the bitmap in any degree returns a new rotated WriteableBitmap. /// </summary> /// <param name="bmp">The WriteableBitmap.</param> /// <param name="angle">Arbitrary angle in 360 Degrees (positive = clockwise).</param> /// <param name="crop">if true: keep the size, false: adjust canvas to new size</param> /// <returns>A new WriteableBitmap that is a rotated version of the input.</returns> public static WriteableBitmap RotateFree(this WriteableBitmap bmp, double angle, bool crop = true) { // rotating clockwise, so it's negative relative to Cartesian quadrants double cnAngle = -1.0 * (Math.PI / 180) * angle; // general iterators int i, j; // calculated indices in Cartesian coordinates int x, y; double fDistance, fPolarAngle; // for use in neighboring indices in Cartesian coordinates int iFloorX, iCeilingX, iFloorY, iCeilingY; // calculated indices in Cartesian coordinates with trailing decimals double fTrueX, fTrueY; // for interpolation double fDeltaX, fDeltaY; // interpolated "top" pixels double fTopRed, fTopGreen, fTopBlue, fTopAlpha; // interpolated "bottom" pixels double fBottomRed, fBottomGreen, fBottomBlue, fBottomAlpha; // final interpolated color components int iRed, iGreen, iBlue, iAlpha; int iCentreX, iCentreY; int iDestCentreX, iDestCentreY; int iWidth, iHeight, newWidth, newHeight; using (var bmpContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly)) { iWidth = bmpContext.Width; iHeight = bmpContext.Height; if (crop) { newWidth = iWidth; newHeight = iHeight; } else { var rad = angle / (180 / Math.PI); newWidth = (int)Math.Ceiling(Math.Abs(Math.Sin(rad) * iHeight) + Math.Abs(Math.Cos(rad) * iWidth)); newHeight = (int)Math.Ceiling(Math.Abs(Math.Sin(rad) * iWidth) + Math.Abs(Math.Cos(rad) * iHeight)); } iCentreX = iWidth / 2; iCentreY = iHeight / 2; iDestCentreX = newWidth / 2; iDestCentreY = newHeight / 2; var bmBilinearInterpolation = BitmapFactory.New(newWidth, newHeight); using (var bilinearContext = bmBilinearInterpolation.GetBitmapContext()) { var newp = bilinearContext.Pixels; var oldp = bmpContext.Pixels; var oldw = bmpContext.Width; // assigning pixels of destination image from source image // with bilinear interpolation for (i = 0; i < newHeight; ++i) { for (j = 0; j < newWidth; ++j) { // convert raster to Cartesian x = j - iDestCentreX; y = iDestCentreY - i; // convert Cartesian to polar fDistance = Math.Sqrt(x * x + y * y); if (x == 0) { if (y == 0) { // center of image, no rotation needed newp[i * newWidth + j] = oldp[iCentreY * oldw + iCentreX]; continue; } if (y < 0) { fPolarAngle = 1.5 * Math.PI; } else { fPolarAngle = 0.5 * Math.PI; } } else { fPolarAngle = Math.Atan2(y, x); } // the crucial rotation part // "reverse" rotate, so minus instead of plus fPolarAngle -= cnAngle; // convert polar to Cartesian fTrueX = fDistance * Math.Cos(fPolarAngle); fTrueY = fDistance * Math.Sin(fPolarAngle); // convert Cartesian to raster fTrueX = fTrueX + iCentreX; fTrueY = iCentreY - fTrueY; iFloorX = (int)(Math.Floor(fTrueX)); iFloorY = (int)(Math.Floor(fTrueY)); iCeilingX = (int)(Math.Ceiling(fTrueX)); iCeilingY = (int)(Math.Ceiling(fTrueY)); // check bounds if (iFloorX < 0 || iCeilingX < 0 || iFloorX >= iWidth || iCeilingX >= iWidth || iFloorY < 0 || iCeilingY < 0 || iFloorY >= iHeight || iCeilingY >= iHeight) { continue; } fDeltaX = fTrueX - iFloorX; fDeltaY = fTrueY - iFloorY; var clrTopLeft = oldp[iFloorY * oldw + iFloorX]; var clrTopRight = oldp[iFloorY * oldw + iCeilingX]; var clrBottomLeft = oldp[iCeilingY * oldw + iFloorX]; var clrBottomRight = oldp[iCeilingY * oldw + iCeilingX]; fTopAlpha = (1 - fDeltaX) * ((clrTopLeft >> 24) & 0xFF) + fDeltaX * ((clrTopRight >> 24) & 0xFF); fTopRed = (1 - fDeltaX) * ((clrTopLeft >> 16) & 0xFF) + fDeltaX * ((clrTopRight >> 16) & 0xFF); fTopGreen = (1 - fDeltaX) * ((clrTopLeft >> 8) & 0xFF) + fDeltaX * ((clrTopRight >> 8) & 0xFF); fTopBlue = (1 - fDeltaX) * (clrTopLeft & 0xFF) + fDeltaX * (clrTopRight & 0xFF); // linearly interpolate horizontally between bottom neighbors fBottomAlpha = (1 - fDeltaX) * ((clrBottomLeft >> 24) & 0xFF) + fDeltaX * ((clrBottomRight >> 24) & 0xFF); fBottomRed = (1 - fDeltaX) * ((clrBottomLeft >> 16) & 0xFF) + fDeltaX * ((clrBottomRight >> 16) & 0xFF); fBottomGreen = (1 - fDeltaX) * ((clrBottomLeft >> 8) & 0xFF) + fDeltaX * ((clrBottomRight >> 8) & 0xFF); fBottomBlue = (1 - fDeltaX) * (clrBottomLeft & 0xFF) + fDeltaX * (clrBottomRight & 0xFF); // linearly interpolate vertically between top and bottom interpolated results iRed = (int)(Math.Round((1 - fDeltaY) * fTopRed + fDeltaY * fBottomRed)); iGreen = (int)(Math.Round((1 - fDeltaY) * fTopGreen + fDeltaY * fBottomGreen)); iBlue = (int)(Math.Round((1 - fDeltaY) * fTopBlue + fDeltaY * fBottomBlue)); iAlpha = (int)(Math.Round((1 - fDeltaY) * fTopAlpha + fDeltaY * fBottomAlpha)); // make sure color values are valid if (iRed < 0) { iRed = 0; } if (iRed > 255) { iRed = 255; } if (iGreen < 0) { iGreen = 0; } if (iGreen > 255) { iGreen = 255; } if (iBlue < 0) { iBlue = 0; } if (iBlue > 255) { iBlue = 255; } if (iAlpha < 0) { iAlpha = 0; } if (iAlpha > 255) { iAlpha = 255; } var a = iAlpha + 1; newp[i * newWidth + j] = (iAlpha << 24) | ((byte)((iRed * a) >> 8) << 16) | ((byte)((iGreen * a) >> 8) << 8) | ((byte)((iBlue * a) >> 8)); } } return(bmBilinearInterpolation); } } }
public static WriteableBitmap FromStream(this WriteableBitmap bmp, Stream stream) { return(BitmapFactory.FromStream(stream)); }
/// <summary> /// Creates a new filtered WriteableBitmap. /// </summary> /// <param name="bmp">The WriteableBitmap.</param> /// <param name="kernel">The kernel used for convolution.</param> /// <param name="kernelFactorSum">The factor used for the kernel summing.</param> /// <param name="kernelOffsetSum">The offset used for the kernel summing.</param> /// <returns>A new WriteableBitmap that is a filtered version of the input.</returns> public static WriteableBitmap Convolute(this WriteableBitmap bmp, int[,] kernel, int kernelFactorSum, int kernelOffsetSum) { var kh = kernel.GetUpperBound(0) + 1; var kw = kernel.GetUpperBound(1) + 1; if ((kw & 1) == 0) { throw new InvalidOperationException("Kernel width must be odd!"); } if ((kh & 1) == 0) { throw new InvalidOperationException("Kernel height must be odd!"); } using (var srcContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly)) { var w = srcContext.Width; var h = srcContext.Height; var result = BitmapFactory.New(w, h); using (var resultContext = result.GetBitmapContext()) { var pixels = srcContext.Pixels; var resultPixels = resultContext.Pixels; var index = 0; var kwh = kw >> 1; var khh = kh >> 1; for (var y = 0; y < h; y++) { for (var x = 0; x < w; x++) { var a = 0; var r = 0; var g = 0; var b = 0; for (var kx = -kwh; kx <= kwh; kx++) { var px = kx + x; // Repeat pixels at borders if (px < 0) { px = 0; } else if (px >= w) { px = w - 1; } for (var ky = -khh; ky <= khh; ky++) { var py = ky + y; // Repeat pixels at borders if (py < 0) { py = 0; } else if (py >= h) { py = h - 1; } var col = pixels[py * w + px]; var k = kernel[ky + kwh, kx + khh]; a += ((col >> 24) & 0x000000FF) * k; r += ((col >> 16) & 0x000000FF) * k; g += ((col >> 8) & 0x000000FF) * k; b += ((col) & 0x000000FF) * k; } } var ta = ((a / kernelFactorSum) + kernelOffsetSum); var tr = ((r / kernelFactorSum) + kernelOffsetSum); var tg = ((g / kernelFactorSum) + kernelOffsetSum); var tb = ((b / kernelFactorSum) + kernelOffsetSum); // Clamp to byte boundaries var ba = (byte)((ta > 255) ? 255 : ((ta < 0) ? 0 : ta)); var br = (byte)((tr > 255) ? 255 : ((tr < 0) ? 0 : tr)); var bg = (byte)((tg > 255) ? 255 : ((tg < 0) ? 0 : tg)); var bb = (byte)((tb > 255) ? 255 : ((tb < 0) ? 0 : tb)); resultPixels[index++] = (ba << 24) | (br << 16) | (bg << 8) | (bb); } } return(result); } } }
public static WriteableBitmap FromContent(this WriteableBitmap bmp, string relativePath) { return(BitmapFactory.FromContent(relativePath)); }
public static WriteableBitmap FromResource(this WriteableBitmap bmp, string relativePath) { return(BitmapFactory.FromResource(relativePath)); }
public static Task <WriteableBitmap> FromPixelBuffer(this WriteableBitmap bmp, IBuffer pixelBuffer, int width, int height) { return(BitmapFactory.FromPixelBuffer(pixelBuffer, width, height)); }
public static Task <WriteableBitmap> FromStream(this WriteableBitmap bmp, IRandomAccessStream stream, BitmapPixelFormat pixelFormat = BitmapPixelFormat.Unknown) { return(BitmapFactory.FromStream(stream, pixelFormat)); }
public static Task <WriteableBitmap> FromContent(this WriteableBitmap bmp, Uri uri, BitmapPixelFormat pixelFormat = BitmapPixelFormat.Unknown) { return(BitmapFactory.FromContent(uri, pixelFormat)); }