public static void ToReplacedColor(BitmapHolder bmp, int r, int g, int b, int a) { var nWidth = bmp.Width; var nHeight = bmp.Height; var len = bmp.PixelCount; for (var i = 0; i < len; i++) { var c = bmp.GetPixelAsInt(i); var currentAlpha = (c >> 24) & 0x000000FF; var aNew = (int)(currentAlpha * (a / currentAlpha)); var rNew = r; var gNew = g; var bNew = b; if (rNew > 255) rNew = 255; if (gNew > 255) gNew = 255; if (bNew > 255) bNew = 255; bmp.SetPixel(i, (aNew << 24) | (rNew << 16) | (gNew << 8) | bNew); } }
public static void ToSepia(BitmapHolder bmp) { var nWidth = bmp.Width; var nHeight = bmp.Height; var len = bmp.PixelCount; for (var i = 0; i < len; i++) { var c = bmp.GetPixelAsInt(i); var a = (c >> 24) & 0x000000FF; var r = (c >> 16) & 0x000000FF; var g = (c >> 8) & 0x000000FF; var b = (c) & 0x000000FF; var rNew = (int)Math.Min((.393 * r) + (.769 * g) + (.189 * (b)), 255.0); var gNew = (int)Math.Min((.349 * r) + (.686 * g) + (.168 * (b)), 255.0); var bNew = (int)Math.Min((.272 * r) + (.534 * g) + (.131 * (b)), 255.0); if (rNew > 255) rNew = 255; if (gNew > 255) gNew = 255; if (bNew > 255) bNew = 255; bmp.SetPixel(i, (a << 24) | (rNew << 16) | (gNew << 8) | bNew); } }
public static void ToCropped(BitmapHolder source, int x, int y, int width, int height) { var srcWidth = source.Width; var srcHeight = source.Height; // If the rectangle is completely out of the bitmap if (x > srcWidth || y > srcHeight) { return; } // Clamp to boundaries if (x < 0) x = 0; if (x + width > srcWidth) width = srcWidth - x; if (y < 0) y = 0; if (y + height > srcHeight) height = srcHeight - y; // Copy the pixels line by line using fast BlockCopy var result = new int[width * height]; for (var line = 0; line < height; line++) { var srcOff = ((y + line) * srcWidth + x) * Helpers.SizeOfArgb; var dstOff = line * width * Helpers.SizeOfArgb; Helpers.BlockCopy(source.Pixels, srcOff, result, dstOff, width * Helpers.SizeOfArgb); } source.SetPixels(result, width, height); }
public static BitmapHolder ToTransformedCorners(BitmapHolder source, double topLeftCornerSize, double topRightCornerSize, double bottomLeftCornerSize, double bottomRightCornerSize, CornerTransformType cornersTransformType, double cropWidthRatio, double cropHeightRatio) { double sourceWidth = source.Width; double sourceHeight = source.Height; double desiredWidth = sourceWidth; double desiredHeight = sourceHeight; double desiredRatio = cropWidthRatio / cropHeightRatio; double currentRatio = sourceWidth / sourceHeight; if (currentRatio > desiredRatio) desiredWidth = (cropWidthRatio * sourceHeight / cropHeightRatio); else if (currentRatio < desiredRatio) desiredHeight = (cropHeightRatio * sourceWidth / cropWidthRatio); topLeftCornerSize = topLeftCornerSize * (desiredWidth + desiredHeight) / 2 / 100; topRightCornerSize = topRightCornerSize * (desiredWidth + desiredHeight) / 2 / 100; bottomLeftCornerSize = bottomLeftCornerSize * (desiredWidth + desiredHeight) / 2 / 100; bottomRightCornerSize = bottomRightCornerSize * (desiredWidth + desiredHeight) / 2 / 100; float cropX = (float)((sourceWidth - desiredWidth) / 2); float cropY = (float)((sourceHeight - desiredHeight) / 2); //TODO not implemented return source; }
protected override BitmapHolder Transform(BitmapHolder source) { var transformed = ToFlipped(source, _flipType); source = null; // Free resource as we return a new WriteableBitmap return transformed; }
protected override BitmapHolder Transform(BitmapHolder source) { if (EnableSolidColor) { ToReplacedColor(source, R, G, B, A); return source; } RGBAWMatrix = FFColorMatrix.ColorToTintMatrix(R, G, B, A); return base.Transform(source); }
public static BitmapHolder ToCropped(BitmapHolder source, double zoomFactor, double xOffset, double yOffset, double cropWidthRatio, double cropHeightRatio) { double sourceWidth = source.Width; double sourceHeight = source.Height; double desiredWidth = sourceWidth; double desiredHeight = sourceHeight; double desiredRatio = cropWidthRatio / cropHeightRatio; double currentRatio = sourceWidth / sourceHeight; if (currentRatio > desiredRatio) desiredWidth = (cropWidthRatio * sourceHeight / cropHeightRatio); else if (currentRatio < desiredRatio) desiredHeight = (cropHeightRatio * sourceWidth / cropWidthRatio); xOffset = xOffset * desiredWidth; yOffset = yOffset * desiredHeight; desiredWidth = desiredWidth / zoomFactor; desiredHeight = desiredHeight / zoomFactor; float cropX = (float)(((sourceWidth - desiredWidth) / 2) + xOffset); float cropY = (float)(((sourceHeight - desiredHeight) / 2) + yOffset); if (cropX < 0) cropX = 0; if (cropY < 0) cropY = 0; if (cropX + desiredWidth > sourceWidth) cropX = (float)(sourceWidth - desiredWidth); if (cropY + desiredHeight > sourceHeight) cropY = (float)(sourceHeight - desiredHeight); int width = (int)desiredWidth; int height = (int)desiredHeight; // Copy the pixels line by line using fast BlockCopy var result = new int[width * height]; for (var line = 0; line < height; line++) { var srcOff = (((int)cropY + line) * source.Width + (int)cropX) * ColorExtensions.SizeOfArgb; var dstOff = line * width * ColorExtensions.SizeOfArgb; Helpers.BlockCopy(source.Pixels, srcOff, result, dstOff, width * ColorExtensions.SizeOfArgb); } return new BitmapHolder(result, width, height); }
public static void ToGrayscale(BitmapHolder bmp) { var nWidth = bmp.Width; var nHeight = bmp.Height; var px = bmp.Pixels; var len = bmp.Pixels.Length; for (var i = 0; i < len; i++) { var c = px[i]; var a = (c >> 24) & 0x000000FF; var r = (c >> 16) & 0x000000FF; var g = (c >> 8) & 0x000000FF; var b = (c) & 0x000000FF; // Convert to gray with constant factors 0.2126, 0.7152, 0.0722 var gray = (r * 6966 + g * 23436 + b * 2366) >> 15; r = g = b = gray; px[i] = (a << 24) | (r << 16) | (g << 8) | b; } }
public static void ToGrayscale(BitmapHolder bmp) { var nWidth = bmp.Width; var nHeight = bmp.Height; var len = bmp.PixelCount; for (var i = 0; i < len; i++) { var c = bmp.GetPixelAsInt(i); var a = (c >> 24) & 0x000000FF; var r = (c >> 16) & 0x000000FF; var g = (c >> 8) & 0x000000FF; var b = (c) & 0x000000FF; // Convert to gray with constant factors 0.2126, 0.7152, 0.0722 var gray = (r * 6966 + g * 23436 + b * 2366) >> 15; r = g = b = gray; bmp.SetPixel(i, (a << 24) | (r << 16) | (g << 8) | b); } }
public static BitmapHolder ToFlipped(BitmapHolder bmp, FlipType flipMode) { // Use refs for faster access (really important!) speeds up a lot! var w = bmp.Width; var h = bmp.Height; var p = bmp.Pixels; var i = 0; BitmapHolder result = new BitmapHolder(new int[bmp.Pixels.Length], w, h); if (flipMode == FlipType.Vertical) { var rp = result.Pixels; for (var y = h - 1; y >= 0; y--) { for (var x = 0; x < w; x++) { var srcInd = y * w + x; rp[i] = p[srcInd]; i++; } } } else { var rp = result.Pixels; for (var y = 0; y < h; y++) { for (var x = w - 1; x >= 0; x--) { var srcInd = y * w + x; rp[i] = p[srcInd]; i++; } } } return result; }
protected override BitmapHolder Transform(BitmapHolder source) { RoundedTransformation.ToRounded(source, 0, 1f, 1f); return source; }
protected override BitmapHolder Transform(BitmapHolder source) { return ToRotated(source, _degrees, _ccw, _resize); }
public static BitmapHolder ToRotated(BitmapHolder source, double degrees, bool ccw, bool resize) { if (degrees == 0 || degrees % 360 == 0) return source; if (ccw) degrees = 360d - degrees; // rotating clockwise, so it's negative relative to Cartesian quadrants double cnAngle = -1.0 * (Math.PI / 180) * degrees; // 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; iWidth = source.Width; iHeight = source.Height; if (!resize || (degrees % 180 == 0)) { newWidth = iWidth; newHeight = iHeight; } else { var rad = degrees / (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 newp = new int[newWidth * newHeight]; var oldp = source.Pixels; var oldw = source.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 new BitmapHolder(newp, newWidth, newHeight); }
public static BitmapHolder ToTransformedCorners(BitmapHolder source, double topLeftCornerSize, double topRightCornerSize, double bottomLeftCornerSize, double bottomRightCornerSize, CornerTransformType cornersTransformType, double cropWidthRatio, double cropHeightRatio) { double sourceWidth = source.Width; double sourceHeight = source.Height; double desiredWidth = sourceWidth; double desiredHeight = sourceHeight; double desiredRatio = cropWidthRatio / cropHeightRatio; double currentRatio = sourceWidth / sourceHeight; if (currentRatio > desiredRatio) desiredWidth = (cropWidthRatio * sourceHeight / cropHeightRatio); else if (currentRatio < desiredRatio) desiredHeight = (cropHeightRatio * sourceWidth / cropWidthRatio); double cropX = ((sourceWidth - desiredWidth) / 2); double cropY = ((sourceHeight - desiredHeight) / 2); BitmapHolder bitmap = null; if (cropX != 0 || cropY != 0) { bitmap = CropTransformation.ToCropped(source, (int)cropX, (int)cropY, (int)(desiredWidth), (int)(desiredHeight)); } else { bitmap = new BitmapHolder(source.Pixels, source.Width, source.Height); } topLeftCornerSize = topLeftCornerSize * (desiredWidth + desiredHeight) / 2 / 100; topRightCornerSize = topRightCornerSize * (desiredWidth + desiredHeight) / 2 / 100; bottomLeftCornerSize = bottomLeftCornerSize * (desiredWidth + desiredHeight) / 2 / 100; bottomRightCornerSize = bottomRightCornerSize * (desiredWidth + desiredHeight) / 2 / 100; int topLeftSize = (int)topLeftCornerSize; int topRightSize = (int)topRightCornerSize; int bottomLeftSize = (int)bottomLeftCornerSize; int bottomRightSize = (int)bottomRightCornerSize; int w = bitmap.Width; int h = bitmap.Height; int transparentColor = Colors.Transparent.ToInt(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if (x <= topLeftSize && y <= topLeftSize) { //top left corner if (!CheckCorner(topLeftSize, topLeftSize, topLeftSize, cornersTransformType, Corner.TopLeftCorner, x, y)) bitmap.Pixels[y * w + x] = transparentColor; } else if (x >= w - topRightSize && y <= topRightSize && topRightSize > 0) { // top right corner if (!CheckCorner(w - topRightSize, topRightSize, topRightSize, cornersTransformType, Corner.TopRightCorner, x, y)) bitmap.Pixels[y * w + x] = transparentColor; } else if (x >= w - bottomRightSize && y >= h - bottomRightSize && bottomRightSize > 0) { // bottom right corner if (!CheckCorner(w - bottomRightSize, h - bottomRightSize, bottomRightSize, cornersTransformType, Corner.BottomRightCorner, x, y)) bitmap.Pixels[y * w + x] = transparentColor; } else if (x <= bottomLeftSize && y >= h - bottomLeftSize && bottomLeftSize > 0) { // bottom left corner if (!CheckCorner(bottomLeftSize, h - bottomLeftSize, bottomLeftSize, cornersTransformType, Corner.BottomLeftCorner, x, y)) bitmap.Pixels[y * w + x] = transparentColor; } } } return bitmap; }
protected override BitmapHolder Transform(BitmapHolder source) { return ToTransformedCorners(source, TopLeftCornerSize, TopRightCornerSize, BottomLeftCornerSize, BottomRightCornerSize, CornersTransformType, CropWidthRatio, CropHeightRatio); }
protected override BitmapHolder Transform(BitmapHolder source) { ToLegacyBlurred(source, (int)_radius); return source; }
// Source: http://incubator.quasimondo.com/processing/superfast_blur.php public static void ToLegacyBlurred(BitmapHolder source, int radius) { int w = source.Width; int h = source.Height; int wm = w - 1; int hm = h - 1; int wh = w * h; int div = radius + radius + 1; int[] r = new int[wh]; int[] g = new int[wh]; int[] b = new int[wh]; int rsum, gsum, bsum, x, y, i, p, p1, p2, yp, yi, yw; int[] vmin = new int[Math.Max(w, h)]; int[] vmax = new int[Math.Max(w, h)]; int[] dv = new int[256 * div]; for (i = 0; i < 256 * div; i++) { dv[i] = (i / div); } yw = yi = 0; for (y = 0; y < h; y++) { rsum = gsum = bsum = 0; for (i = -radius; i <= radius; i++) { p = source.Pixels[yi + Math.Min(wm, Math.Max(i, 0))]; rsum += (p & 0xff0000) >> 16; gsum += (p & 0x00ff00) >> 8; bsum += p & 0x0000ff; } for (x = 0; x < w; x++) { r[yi] = dv[rsum]; g[yi] = dv[gsum]; b[yi] = dv[bsum]; if (y == 0) { vmin[x] = Math.Min(x + radius + 1, wm); vmax[x] = Math.Max(x - radius, 0); } p1 = source.Pixels[yw + vmin[x]]; p2 = source.Pixels[yw + vmax[x]]; rsum += ((p1 & 0xff0000) - (p2 & 0xff0000)) >> 16; gsum += ((p1 & 0x00ff00) - (p2 & 0x00ff00)) >> 8; bsum += (p1 & 0x0000ff) - (p2 & 0x0000ff); yi++; } yw += w; } for (x = 0; x < w; x++) { rsum = gsum = bsum = 0; yp = -radius * w; for (i = -radius; i <= radius; i++) { yi = Math.Max(0, yp) + x; rsum += r[yi]; gsum += g[yi]; bsum += b[yi]; yp += w; } yi = x; for (y = 0; y < h; y++) { // Preserve alpha channel: ( 0xff000000 & pix[yi] ) source.Pixels[yi] = (int)((0xff000000 & source.Pixels[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]); if (x == 0) { vmin[y] = Math.Min(y + radius + 1, hm) * w; vmax[y] = Math.Max(y - radius, 0) * w; } p1 = x + vmin[y]; p2 = x + vmax[y]; rsum += r[p1] - r[p2]; gsum += g[p1] - g[p2]; bsum += b[p1] - b[p2]; yi += w; } } }
protected override BitmapHolder Transform(BitmapHolder source) { return ToCropped(source, ZoomFactor, XOffset, YOffset, CropWidthRatio, CropHeightRatio); }
protected async override Task <WriteableBitmap> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { BitmapHolder imageIn = null; if (imageData == null) { throw new ArgumentNullException(nameof(imageData)); } ThrowIfCancellationRequested(); try { bool allowUpscale = Parameters.AllowUpscale ?? Configuration.AllowUpscale; if (source != ImageSource.Stream && imageInformation.Type == ImageInformation.ImageType.WEBP) { throw new NotImplementedException("Webp is not implemented on Windows"); } else if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { imageIn = await imageData.ToBitmapHolderAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, allowUpscale, imageInformation).ConfigureAwait(false); } else { return(await imageData.ToBitmapImageAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, allowUpscale, imageInformation).ConfigureAwait(false)); } } finally { imageData.TryDispose(); } ThrowIfCancellationRequested(); if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { var transformations = Parameters.Transformations.ToList(); await _decodingLock.WaitAsync(CancellationTokenSource.Token).ConfigureAwait(false); // Applying transformations is both CPU and memory intensive ThrowIfCancellationRequested(); try { foreach (var transformation in transformations) { ThrowIfCancellationRequested(); var old = imageIn; try { IBitmap bitmapHolder = transformation.Transform(imageIn, path, source, isPlaceholder, Key); imageIn = bitmapHolder.ToNative(); } catch (Exception ex) { Logger.Error(string.Format("Transformation failed: {0}", transformation.Key), ex); throw; } finally { if (old != null && old != imageIn && old.PixelData != imageIn.PixelData) { old.FreePixels(); old = null; } } } } finally { _decodingLock.Release(); } } try { return(await imageIn.ToBitmapImageAsync()); } finally { imageIn.FreePixels(); imageIn = null; } }
protected override BitmapHolder Transform(BitmapHolder source) { ToColorSpace(source, _rgbawMatrix); return source; }
protected override BitmapHolder Transform(BitmapHolder source) { return ToFlipped(source, FlipType); }
protected override BitmapHolder Transform(BitmapHolder source) { return RoundedTransformation.ToRounded(source, 0, 1f, 1f, _borderSize, _borderHexColor); }
protected override BitmapHolder Transform(BitmapHolder source) { return ToRotated(source, Degrees, CCW, Resize); }
protected override BitmapHolder Transform(BitmapHolder source) { return ToCropped(source, _zoomFactor, _xOffset, _yOffset, _cropWidthRatio, _cropHeightRatio); }
protected async override Task <WriteableBitmap> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { BitmapHolder imageIn = null; if (imageData == null) { throw new ArgumentNullException(nameof(imageData)); } ThrowIfCancellationRequested(); try { // Special case to handle WebP decoding on Windows string ext = null; if (!string.IsNullOrWhiteSpace(path)) { if (source == ImageSource.Url && Uri.IsWellFormedUriString(path, UriKind.RelativeOrAbsolute)) { ext = Path.GetExtension(new Uri(path).LocalPath).ToLowerInvariant(); } else { ext = Path.GetExtension(path).ToLowerInvariant(); } } bool allowUpscale = Parameters.AllowUpscale ?? Configuration.AllowUpscale; if (source != ImageSource.Stream && ext == ".webp") { throw new NotImplementedException("Webp is not implemented on Windows"); } else if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { imageIn = await imageData.ToBitmapHolderAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, allowUpscale, imageInformation).ConfigureAwait(false); } else { return(await imageData.ToBitmapImageAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, allowUpscale, imageInformation).ConfigureAwait(false)); } } finally { imageData?.Dispose(); } ThrowIfCancellationRequested(); if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { var transformations = Parameters.Transformations.ToList(); await _decodingLock.WaitAsync().ConfigureAwait(false); // Applying transformations is both CPU and memory intensive try { foreach (var transformation in transformations) { ThrowIfCancellationRequested(); var old = imageIn; try { IBitmap bitmapHolder = transformation.Transform(imageIn, path, source, isPlaceholder, Key); imageIn = bitmapHolder.ToNative(); } catch (Exception ex) { Logger.Error(string.Format("Transformation failed: {0}", transformation.Key), ex); throw; } finally { if (old != null && old != imageIn && old.PixelData != imageIn.PixelData) { old.FreePixels(); old = null; } } } } finally { _decodingLock.Release(); } } try { return(await imageIn.ToBitmapImageAsync()); } finally { imageIn.FreePixels(); imageIn = null; } }
protected abstract BitmapHolder Transform(BitmapHolder source);
public static BitmapHolder ToCropped(BitmapHolder source, int x, int y, int width, int height) { var srcWidth = source.Width; var srcHeight = source.Height; // Clamp to boundaries if (x < 0) x = 0; if (x + width > srcWidth) width = srcWidth - x; if (y < 0) y = 0; if (y + height > srcHeight) height = srcHeight - y; // Copy the pixels line by line using fast BlockCopy var result = new int[width * height]; for (var line = 0; line < height; line++) { var srcOff = ((y + line) * srcWidth + x) * ColorExtensions.SizeOfArgb; var dstOff = line * width * ColorExtensions.SizeOfArgb; Helpers.BlockCopy(source.Pixels, srcOff, result, dstOff, width * ColorExtensions.SizeOfArgb); } return new BitmapHolder(result, width, height); }
protected override BitmapHolder Transform(BitmapHolder source) { return ToRounded(source, (int)Radius, CropWidthRatio, CropHeightRatio, BorderSize, BorderHexColor); }
// helper function, draws pixel and mirrors it static void SetPixel4(BitmapHolder bitmap, int centerX, int centerY, int deltaX, int deltaY, int color) { bitmap.SetPixel(centerX + deltaX, centerY + deltaY, color); bitmap.SetPixel(centerX - deltaX, centerY + deltaY, color); bitmap.SetPixel(centerX + deltaX, centerY - deltaY, color); bitmap.SetPixel(centerX - deltaX, centerY - deltaY, color); }
public static BitmapHolder ToRounded(BitmapHolder source, int rad, double cropWidthRatio, double cropHeightRatio, double borderSize, string borderHexColor) { double sourceWidth = source.Width; double sourceHeight = source.Height; double desiredWidth = sourceWidth; double desiredHeight = sourceHeight; double desiredRatio = cropWidthRatio / cropHeightRatio; double currentRatio = sourceWidth / sourceHeight; if (currentRatio > desiredRatio) desiredWidth = (cropWidthRatio * sourceHeight / cropHeightRatio); else if (currentRatio < desiredRatio) desiredHeight = (cropHeightRatio * sourceWidth / cropWidthRatio); double cropX = ((sourceWidth - desiredWidth) / 2); double cropY = ((sourceHeight - desiredHeight) / 2); BitmapHolder bitmap = null; if (cropX != 0 || cropY != 0) { bitmap = CropTransformation.ToCropped(source, (int)cropX, (int)cropY, (int)(desiredWidth), (int)(desiredHeight)); } else { bitmap = new BitmapHolder(source.Pixels, source.Width, source.Height); } if (rad == 0) rad = (int)(Math.Min(desiredWidth, desiredHeight) / 2); else rad = (int)(rad * (desiredWidth + desiredHeight) / 2 / 500); int w = (int)desiredWidth; int h = (int)desiredHeight; int transparentColor = Colors.Transparent.ToInt(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { if (x <= rad && y <= rad) { //top left corner if (!CheckRoundedCorner(rad, rad, rad, Corner.TopLeftCorner, x, y)) bitmap.Pixels[y * w + x] = transparentColor; } else if (x >= w - rad && y <= rad) { // top right corner if (!CheckRoundedCorner(w - rad, rad, rad, Corner.TopRightCorner, x, y)) bitmap.Pixels[y * w + x] = transparentColor; } else if (x >= w - rad && y >= h - rad) { // bottom right corner if (!CheckRoundedCorner(w - rad, h - rad, rad, Corner.BottomRightCorner, x, y)) bitmap.Pixels[y * w + x] = transparentColor; } else if (x <= rad && y >= h - rad) { // bottom left corner if (!CheckRoundedCorner(rad, h - rad, rad, Corner.BottomLeftCorner, x, y)) bitmap.Pixels[y * w + x] = transparentColor; } } } //TODO draws a border - we should optimize that and add some anti-aliasing if (borderSize > 0d) { borderSize = (borderSize * (desiredWidth + desiredHeight) / 2d / 500d); int borderColor = Colors.Transparent.ToInt(); try { if (!borderHexColor.StartsWith("#", StringComparison.Ordinal)) borderHexColor.Insert(0, "#"); borderColor = borderHexColor.ToColorFromHex().ToInt(); } catch (Exception) { } int intBorderSize = (int)Math.Ceiling(borderSize); for (int i = 2; i < intBorderSize; i++) { CircleAA(bitmap, i, borderColor); } } return bitmap; }
public static void ToColorSpace(BitmapHolder bmp, float[][] rgbawMatrix) { var r0 = rgbawMatrix[0][0]; var r1 = rgbawMatrix[0][1]; var r2 = rgbawMatrix[0][2]; var r3 = rgbawMatrix[0][3]; var g0 = rgbawMatrix[1][0]; var g1 = rgbawMatrix[1][1]; var g2 = rgbawMatrix[1][2]; var g3 = rgbawMatrix[1][3]; var b0 = rgbawMatrix[2][0]; var b1 = rgbawMatrix[2][1]; var b2 = rgbawMatrix[2][2]; var b3 = rgbawMatrix[2][3]; var a0 = rgbawMatrix[3][0]; var a1 = rgbawMatrix[3][1]; var a2 = rgbawMatrix[3][2]; var a3 = rgbawMatrix[3][3]; var rOffset = rgbawMatrix[4][0]; var gOffset = rgbawMatrix[4][1]; var bOffset = rgbawMatrix[4][2]; var aOffset = rgbawMatrix[4][3]; var nWidth = bmp.Width; var nHeight = bmp.Height; var len = bmp.PixelCount; for (var i = 0; i < len; i++) { var c = bmp.GetPixelAsInt(i); var a = (c >> 24) & 0x000000FF; var r = (c >> 16) & 0x000000FF; var g = (c >> 8) & 0x000000FF; var b = (c) & 0x000000FF; var rNew = (int)(r * r0 + g * g0 + b * b0 + a * a0 + rOffset); var gNew = (int)(r * r1 + g * g1 + b * b1 + a * a1 + gOffset); var bNew = (int)(r * r2 + g * g2 + b * b2 + a * a2 + bOffset); var aNew = (int)(r * r3 + g * g3 + b * b3 + a * a3 + aOffset); if (rNew > 255) rNew = 255; if (gNew > 255) gNew = 255; if (bNew > 255) bNew = 255; if (aNew > 255) aNew = 255; if (rNew < 0) rNew = 0; if (gNew < 0) gNew = 0; if (bNew < 0) bNew = 0; if (aNew < 0) aNew = 0; bmp.SetPixel(i, (aNew << 24) | (rNew << 16) | (gNew << 8) | bNew); } }
static void CircleAA(BitmapHolder bitmap, int size, int color) { if (size % 2 != 0) size++; int centerX = bitmap.Width / 2; double radiusX = (bitmap.Width - size) / 2; int centerY = bitmap.Height / 2; double radiusY = (bitmap.Height - size) / 2; const int maxTransparency = 127; // default: 127 double radiusX2 = radiusX * radiusX; double radiusY2 = radiusY * radiusY; // upper and lower halves int quarter = (int)Math.Round(radiusX2 / Math.Sqrt(radiusX2 + radiusY2)); for (int x = 0; x <= quarter; x++) { double y = Math.Floor(radiusY * Math.Sqrt(1 - x * x / radiusX2)); double error = y - Math.Floor(y); int transparency = (int)Math.Round(error * maxTransparency); int alpha = color | (transparency << 24); int alpha2 = color | ((maxTransparency - transparency) << 24); SetPixel4(bitmap, centerX, centerY, x, (int)Math.Floor(y), alpha); SetPixel4(bitmap, centerX, centerY, x, (int)Math.Floor(y) + 1, alpha2); } // right and left halves quarter = (int)Math.Round(radiusY2 / Math.Sqrt(radiusX2 + radiusY2)); for (int y = 0; y <= quarter; y++) { double x = Math.Floor(radiusX * Math.Sqrt(1 - y * y / radiusY2)); double error = x - Math.Floor(x); int transparency = (int)Math.Round(error * maxTransparency); int alpha = color | (transparency << 24); int alpha2 = color | ((maxTransparency - transparency) << 24); SetPixel4(bitmap, centerX, centerY, (int)Math.Floor(x), y, alpha); SetPixel4(bitmap, centerX, centerY, (int)Math.Floor(x) + 1, y, alpha2); } }