/// <summary> /// Вырезает указанную зону изображения. /// </summary> /// <param name="sourceImage">Исходное изображение.</param> /// <param name="rectangle">Участок изображения.</param> /// <returns></returns> public static Bitmap Crop(this Bitmap sourceImage, Rectangle rectangle) { if (sourceImage.Width == rectangle.Width && sourceImage.Height == rectangle.Height) { return(sourceImage); } BitmapData sourceImageData = sourceImage.LockBits(new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), ImageLockMode.ReadOnly, sourceImage.PixelFormat); Bitmap croppedImage = new Bitmap(rectangle.Width, rectangle.Height, sourceImage.PixelFormat); BitmapData croppedImageData = croppedImage.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height), ImageLockMode.WriteOnly, croppedImage.PixelFormat); int pixelSize = Image.GetPixelFormatSize(croppedImageData.PixelFormat) / 8; byte * src = (byte *)(void *)sourceImageData.Scan0 + rectangle.Y * sourceImageData.Stride + rectangle.X * pixelSize; byte * dest = (byte *)(void *)croppedImageData.Scan0; for (int y = 0; y < rectangle.Height; y++) { int srcIndex = y * sourceImageData.Stride; int croppedIndex = y * croppedImageData.Stride; UnmanagedBitmap.CopyUnmanagedMemory(dest + croppedIndex, src + srcIndex, croppedImageData.Stride); } croppedImage.UnlockBits(croppedImageData); sourceImage.UnlockBits(sourceImageData); return(croppedImage); }
/// <summary> /// Копирует буфер текущего изображения в другой экземпляр <see cref="UnmanagedBitmap"/>. /// </summary> /// <param name="destImage">Экземпляр <see cref="UnmanagedBitmap"/>, куда следует скопировать буфер текущего.</param> public void Copy(UnmanagedBitmap destImage) { if (Width != destImage.Width || Height != destImage.Height || PixelFormat != destImage.PixelFormat) { throw new Exception("Destination image has different size or pixel format."); } if (Stride == destImage.Stride) { CopyUnmanagedMemory(destImage.ImageData, ImageData, Stride * Height); } else { int dstStride = destImage.Stride; int copyLength = (Stride < dstStride) ? Stride : dstStride; byte *src = (byte *)(void *)ImageData; byte *dst = (byte *)(void *)destImage.ImageData; for (int i = 0; i < Height; i++) { CopyUnmanagedMemory(dst, src, copyLength); dst += dstStride; src += Stride; } } }
/// <summary> /// Усреднённая выборка. /// </summary> /// <param name="sourceData">Исходное изображение.</param> /// <param name="destinationData">Изображение назначения.</param> /// <param name="newWidth">Новая ширина.</param> /// <param name="newHeight">новая высота.</param> private static void NearestNeighbor(UnmanagedBitmap sourceData, UnmanagedBitmap destinationData, int newWidth, int newHeight) { int pixelSize = Image.GetPixelFormatSize(sourceData.PixelFormat) / 8; int srcStride = sourceData.Stride; int dstStride = destinationData.Stride; double xFactor = (double)sourceData.Width / newWidth; double yFactor = (double)sourceData.Height / newHeight; byte * baseSrc = (byte *)(void *)sourceData.ImageData; byte * baseDst = (byte *)(void *)destinationData.ImageData; for (int y = 0; y < newHeight; y++) { byte *dst = baseDst + dstStride * y; byte *src = baseSrc + srcStride * ((int)(y * yFactor)); byte *p; for (int x = 0; x < newWidth; x++) { p = src + pixelSize * ((int)(x * xFactor)); for (int i = 0; i < pixelSize; i++, dst++, p++) { *dst = *p; } } } }
/// <summary> /// Билинейная фильтрация. /// </summary> /// <param name="sourceData">Исходное изображение.</param> /// <param name="destinationData">Изображение назначения.</param> /// <param name="newWidth">Новая ширина.</param> /// <param name="newHeight">новая высота.</param> private static void Bilinear(UnmanagedBitmap sourceData, UnmanagedBitmap destinationData, int newWidth, int newHeight) { int pixelSize = Image.GetPixelFormatSize(sourceData.PixelFormat) / 8; int srcStride = sourceData.Stride; int dstOffset = destinationData.Stride - pixelSize * newWidth; double xFactor = (double)sourceData.Width / newWidth; double yFactor = (double)sourceData.Height / newHeight; byte * src = (byte *)(void *)sourceData.ImageData; byte * dst = (byte *)(void *)destinationData.ImageData; double ox, oy, dx1, dy1, dx2, dy2; int ox1, oy1, ox2, oy2; int ymax = sourceData.Height - 1; int xmax = sourceData.Width - 1; byte *tp1, tp2; byte *p1, p2, p3, p4; for (int y = 0; y < newHeight; y++) { oy = y * yFactor; oy1 = (int)oy; oy2 = (oy1 == ymax) ? oy1 : oy1 + 1; dy1 = oy - oy1; dy2 = 1.0 - dy1; tp1 = src + oy1 * srcStride; tp2 = src + oy2 * srcStride; for (int x = 0; x < newWidth; x++) { ox = x * xFactor; ox1 = (int)ox; ox2 = (ox1 == xmax) ? ox1 : ox1 + 1; dx1 = ox - ox1; dx2 = 1.0 - dx1; p1 = tp1 + ox1 * pixelSize; p2 = tp1 + ox2 * pixelSize; p3 = tp2 + ox1 * pixelSize; p4 = tp2 + ox2 * pixelSize; for (int i = 0; i < pixelSize; i++, dst++, p1++, p2++, p3++, p4++) { *dst = (byte)(dy2 * (dx2 * (*p1) + dx1 * (*p2)) + dy1 * (dx2 * (*p3) + dx1 * (*p4))); } } dst += dstOffset; } }
/// <summary> /// Возвращает полную копию экземпляра <see cref="UnmanagedBitmap"/>. /// </summary> /// <returns></returns> public UnmanagedBitmap Clone() { IntPtr newImageData = Marshal.AllocHGlobal(Stride * Height); GC.AddMemoryPressure(Stride * Height); UnmanagedBitmap newImage = new UnmanagedBitmap(newImageData, Width, Height, Stride, PixelFormat) { disposed = true }; CopyUnmanagedMemory(newImageData, ImageData, Stride * Height); return(newImage); }
/// <summary> /// Получает неуправляемый экземпляр <see cref="UnmanagedBitmap"/> из управляемого <see cref="Bitmap"/>. /// </summary> /// <param name="imageData">Исходный экземпляр <see cref="Bitmap"/>.</param> /// <returns></returns> public static UnmanagedBitmap FromManagedImage(Bitmap image) { UnmanagedBitmap dstImage = null; BitmapData sourceData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat); try { dstImage = FromManagedImage(sourceData); } finally { image.UnlockBits(sourceData); } return(dstImage); }
/// <summary> /// Обрабатывает изображение указанным алгоритмом. /// </summary> /// <param name="sourceData">Исходное изображение.</param> /// <param name="destinationData">Изображение назначения.</param> /// <param name="newWidth">Новая ширина.</param> /// <param name="newHeight">новая высота.</param> /// <param name="algorithm">Алгоритм изменения размеров.</param> public static void Process(UnmanagedBitmap sourceData, UnmanagedBitmap destinationData, int newWidth, int newHeight, AntialiasingMethod algorithm) { switch (algorithm) { default: case AntialiasingMethod.NearestNeighbor: NearestNeighbor(sourceData, destinationData, newWidth, newHeight); break; case AntialiasingMethod.Bilinear: Bilinear(sourceData, destinationData, newWidth, newHeight); break; case AntialiasingMethod.Bicubic: Bicubic(sourceData, destinationData, newWidth, newHeight); break; } }
/// <summary> /// Получает неуправляемый экземпляр <see cref="UnmanagedBitmap"/> из <see cref="BitmapData"/>. /// </summary> /// <param name="imageData">Исходный экземпляр <see cref="BitmapData"/>.</param> /// <returns></returns> public static UnmanagedBitmap FromManagedImage(BitmapData imageData) { PixelFormat pixelFormat = imageData.PixelFormat; if (pixelFormat != PixelFormat.Format8bppIndexed && pixelFormat != PixelFormat.Format16bppGrayScale && pixelFormat != PixelFormat.Format24bppRgb && pixelFormat != PixelFormat.Format32bppRgb && pixelFormat != PixelFormat.Format32bppArgb && pixelFormat != PixelFormat.Format32bppPArgb && pixelFormat != PixelFormat.Format48bppRgb && pixelFormat != PixelFormat.Format64bppArgb && pixelFormat != PixelFormat.Format64bppPArgb) { throw new Exception("Unsupported pixel format of the source image."); } IntPtr dstImageData = Marshal.AllocHGlobal(imageData.Stride * imageData.Height); GC.AddMemoryPressure(imageData.Stride * imageData.Height); UnmanagedBitmap image = new UnmanagedBitmap(dstImageData, imageData.Width, imageData.Height, imageData.Stride, pixelFormat); CopyUnmanagedMemory(dstImageData, imageData.Scan0, imageData.Stride * imageData.Height); image.disposed = true; return(image); }
/// <summary> /// Изменяет размеры исходного изображения без потерь качества. /// </summary> /// <param name="sourceData">Исходное изображение.</param> /// <param name="width">Новая ширина.</param> /// <param name="height">Новая высота.</param> /// <param name="algorithm">Алгоритм изменения размера.</param> /// <returns></returns> public static Bitmap Resize(this BitmapData sourceData, int width, int height, AntialiasingMethod algorithm) { Bitmap destinationImage = sourceData.PixelFormat == PixelFormat.Format8bppIndexed ? UnmanagedBitmap.CreateGrayscaleImage(width, height) : new Bitmap(width, height, sourceData.PixelFormat); BitmapData destinationData = destinationImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, sourceData.PixelFormat); try { using (UnmanagedBitmap unmSource = new UnmanagedBitmap(sourceData)) using (UnmanagedBitmap unmDestination = new UnmanagedBitmap(destinationData)) AntialiasingFilter.Process(unmSource, unmDestination, width, height, algorithm); } finally { destinationImage.UnlockBits(destinationData); } destinationImage.SetResolution(300, 300); return(destinationImage); }
/// <summary> /// Бикубическая фильтрация. /// </summary> /// <param name="sourceData">Исходное изображение.</param> /// <param name="destinationData">Изображение назначения.</param> /// <param name="newWidth">Новая ширина.</param> /// <param name="newHeight">новая высота.</param> private static void Bicubic(UnmanagedBitmap sourceData, UnmanagedBitmap destinationData, int newWidth, int newHeight) { int pixelSize = sourceData.PixelFormat == PixelFormat.Format8bppIndexed ? 1 : 3; int srcStride = sourceData.Stride; int dstOffset = destinationData.Stride - pixelSize * newWidth; double xFactor = (double)sourceData.Width / newWidth; double yFactor = (double)sourceData.Height / newHeight; byte * src = (byte *)(void *)sourceData.ImageData; byte * dst = (byte *)(void *)destinationData.ImageData; double ox, oy, dx, dy, k1, k2; int ox1, oy1, ox2, oy2; double r, g, b; int ymax = sourceData.Height - 1; int xmax = sourceData.Width - 1; byte *p; if (destinationData.PixelFormat == PixelFormat.Format8bppIndexed) { for (int y = 0; y < newHeight; y++) { oy = y * yFactor - 0.5; oy1 = (int)oy; dy = oy - oy1; for (int x = 0; x < newWidth; x++, dst++) { ox = x * xFactor - 0.5f; ox1 = (int)ox; dx = ox - ox1; g = 0; for (int n = -1; n < 3; n++) { k1 = BicubicKernel(dy - n); oy2 = oy1 + n; if (oy2 < 0) { oy2 = 0; } if (oy2 > ymax) { oy2 = ymax; } for (int m = -1; m < 3; m++) { k2 = k1 * BicubicKernel(m - dx); ox2 = ox1 + m; if (ox2 < 0) { ox2 = 0; } if (ox2 > xmax) { ox2 = xmax; } g += k2 * src[oy2 * srcStride + ox2]; } } *dst = (byte)Math.Max(0, Math.Min(255, g)); } dst += dstOffset; } } else { for (int y = 0; y < newHeight; y++) { oy = y * yFactor - 0.5; oy1 = (int)oy; dy = oy - oy1; for (int x = 0; x < newWidth; x++, dst += 3) { ox = x * xFactor - 0.5; ox1 = (int)ox; dx = ox - ox1; r = g = b = 0; for (int n = -1; n < 3; n++) { k1 = BicubicKernel(dy - n); oy2 = oy1 + n; if (oy2 < 0) { oy2 = 0; } if (oy2 > ymax) { oy2 = ymax; } for (int m = -1; m < 3; m++) { k2 = k1 * BicubicKernel(m - dx); ox2 = ox1 + m; if (ox2 < 0) { ox2 = 0; } if (ox2 > xmax) { ox2 = xmax; } p = src + oy2 * srcStride + ox2 * 3; r += k2 * p[RGB.R]; g += k2 * p[RGB.G]; b += k2 * p[RGB.B]; } } dst[RGB.R] = (byte)Math.Max(0, Math.Min(255, r)); dst[RGB.G] = (byte)Math.Max(0, Math.Min(255, g)); dst[RGB.B] = (byte)Math.Max(0, Math.Min(255, b)); } dst += dstOffset; } } }
/// <summary> /// Создаёт новый экземпляр <see cref="UnmanagedBitmap"/>. /// </summary> /// <param name="width">Ширина изображения.</param> /// <param name="height">Высота изображения.</param> /// <param name="pixelFormat">Формат битовой упаковки пикселей.</param> /// <returns></returns> public static UnmanagedBitmap Create(int width, int height, PixelFormat pixelFormat) { int bytesPerPixel = Image.GetPixelFormatSize(pixelFormat) / 8; //switch (pixelFormat) //{ // case PixelFormat.Format8bppIndexed: // bytesPerPixel = 1; // break; // case PixelFormat.Format16bppGrayScale: // bytesPerPixel = 2; // break; // case PixelFormat.Format24bppRgb: // bytesPerPixel = 3; // break; // case PixelFormat.Format32bppRgb: // case PixelFormat.Format32bppArgb: // case PixelFormat.Format32bppPArgb: // bytesPerPixel = 4; // break; // case PixelFormat.Format48bppRgb: // bytesPerPixel = 6; // break; // case PixelFormat.Format64bppArgb: // case PixelFormat.Format64bppPArgb: // bytesPerPixel = 8; // break; // default: // throw new Exception("Can not create image with specified pixel format."); //} if (width <= 0 || height <= 0) { throw new Exception("Invalid image size specified."); } int stride = width * bytesPerPixel; if (stride % 4 != 0) { stride += (4 - (stride % 4)); } IntPtr imageData = Marshal.AllocHGlobal(stride * height); SetUnmanagedMemory(imageData, 0, stride * height); GC.AddMemoryPressure(stride * height); UnmanagedBitmap image = new UnmanagedBitmap(imageData, width, height, stride, pixelFormat) { disposed = true }; return(image); }