/// <summary> /// Converts <see cref="Image"/> to <see cref="Bitmap"/>. /// </summary> /// <param name="src"><see cref="Image"/> to be converted.</param> /// <returns>A new <see cref="Bitmap"/>.</returns> public static Bitmap ToBitmap(this Image src) { if (src == null) { throw new ArgumentNullException(nameof(src)); } // Ensure image is converted to sRGB if (src.Bands >= 3) { src = src.Colourspace(Enums.Interpretation.Srgb); } PixelFormat pf; switch (src.Bands) { case 1: // when src.Interpretation == Enums.Interpretation.Multiband || // src.Interpretation == Enums.Interpretation.Bw || // src.Interpretation == Enums.Interpretation.Matrix pf = PixelFormat.Format8bppIndexed; // Ensure image is casted to uint8 (unsigned char) src = src.Cast(Enums.BandFormat.Uchar); break; case 2 when src.Interpretation == Enums.Interpretation.Grey16: // Convert to sRGB, since Format16bppGrayScale appears to be unsupported by GDI+. // See: https://stackoverflow.com/a/19706842/10952119 src = src.Colourspace(Enums.Interpretation.Srgb); goto case 4; case 2: // when src.Interpretation == Enums.Interpretation.Multiband || // src.Interpretation == Enums.Interpretation.Bw // Add an additional band src = src.Bandjoin(255); goto case 3; case 3: pf = src.Format == Enums.BandFormat.Ushort ? PixelFormat.Format48bppRgb : PixelFormat.Format24bppRgb; { // Switch from RGB to BGR var rgb = src.Bandsplit(); using var r = rgb[0]; using var g = rgb[1]; using var b = rgb[2]; using (src) { src = b.Bandjoin(g, r); } } break; case 4: pf = src.Format == Enums.BandFormat.Ushort ? PixelFormat.Format64bppArgb : PixelFormat.Format32bppArgb; { // Switch from RGBA to BGRA var rgba = src.Bandsplit(); using var r = rgba[0]; using var g = rgba[1]; using var b = rgba[2]; using var a = rgba[3]; using (src) { src = b.Bandjoin(g, r, a); } } break; default: throw new NotImplementedException( $"Number of bands must be 1 or in the in the range of 3 to 4. Got: {src.Bands}"); } if (src.Format != Enums.BandFormat.Uchar || src.Format != Enums.BandFormat.Ushort) { // Pixel formats other than uchar and ushort needs to be casted to uint8 (unsigned char) using (src) { src = src.Cast(Enums.BandFormat.Uchar); } } var dst = new Bitmap(src.Width, src.Height, pf); // We need to generate a greyscale palette for 8bpp images if (pf == PixelFormat.Format8bppIndexed) { var plt = dst.Palette; for (var x = 0; x < 256; x++) { plt.Entries[x] = Color.FromArgb(x, x, x); } dst.Palette = plt; } var w = src.Width; var h = src.Height; var bands = src.Bands; var rect = new Rectangle(0, 0, w, h); BitmapData bd = null; var memory = IntPtr.Zero; try { bd = dst.LockBits(rect, ImageLockMode.WriteOnly, pf); var dstSize = (ulong)(bd.Stride * h); ulong srcSize; using (src) { memory = src.WriteToMemory(out srcSize); } // bd.Stride is aligned to a multiple of 4 if (dstSize == srcSize) { unsafe { Buffer.MemoryCopy(memory.ToPointer(), bd.Scan0.ToPointer(), srcSize, srcSize); } } else { var offset = w * bands; // Copy the bytes from src to dst for each scanline for (var y = 0; y < h; y++) { var pSrc = memory + y * offset; var pDst = bd.Scan0 + y * bd.Stride; unsafe { Buffer.MemoryCopy(pSrc.ToPointer(), pDst.ToPointer(), offset, offset); } } } } finally { if (bd != null) { dst.UnlockBits(bd); } if (memory != IntPtr.Zero) { NetVips.Free(memory); } } return(dst); }
/// <summary> /// Converts <see cref="Image"/> to <see cref="Bitmap"/>. /// </summary> /// <param name="src"><see cref="Image"/> to be converted.</param> /// <returns>A new <see cref="Bitmap"/>.</returns> public static Bitmap ToBitmap(this Image src) { if (src == null) { throw new ArgumentNullException(nameof(src)); } // Ensure image is casted to uint8 (unsigned char) if (src.Bands < 3 || src.Format != Enums.BandFormat.Ushort) { src = src.Cast(Enums.BandFormat.Uchar); } PixelFormat pf; switch (src.Bands) { case 1: pf = PixelFormat.Format8bppIndexed; break; case 2: // Note: Format16bppGrayScale appears to be unsupported by GDI+. // See: https://stackoverflow.com/a/19706842/10952119 pf = PixelFormat.Format16bppGrayScale; // pf = PixelFormat.Format8bppIndexed; // src = src[0]; break; case 3: pf = src.Format == Enums.BandFormat.Ushort ? PixelFormat.Format48bppRgb : PixelFormat.Format24bppRgb; // Switch from RGB to BGR var rgb = src.Bandsplit(); src = rgb[2].Bandjoin(rgb[1], rgb[0]); break; case 4: pf = src.Format == Enums.BandFormat.Ushort ? PixelFormat.Format64bppArgb : PixelFormat.Format32bppArgb; // Switch from RGBA to BGRA var rgba = src.Bandsplit(); src = rgba[2].Bandjoin(rgba[1], rgba[0], rgba[3]); break; default: throw new NotImplementedException( $"Number of bands must be in the range of 1 to 4. Got: {src.Bands}"); } var dst = new Bitmap(src.Width, src.Height, pf); // We need to generate a greyscale palette for 8bpp images if (pf == PixelFormat.Format8bppIndexed) { var plt = dst.Palette; for (var x = 0; x < 256; x++) { plt.Entries[x] = Color.FromArgb(x, x, x); } dst.Palette = plt; } var w = src.Width; var h = src.Height; var rect = new Rectangle(0, 0, w, h); BitmapData bd = null; try { bd = dst.LockBits(rect, ImageLockMode.WriteOnly, pf); var dstSize = (ulong)(bd.Stride * h); var memory = src.WriteToMemory(out var srcSize); // bd.Stride is aligned to a multiple of 4 if (dstSize == srcSize) { unsafe { Buffer.MemoryCopy(memory.ToPointer(), bd.Scan0.ToPointer(), srcSize, srcSize); } } else { var offset = w * src.Bands; // Copy the bytes from src to dst for each scanline for (var y = 0; y < h; y++) { var pSrc = memory + y * offset; var pDst = bd.Scan0 + y * bd.Stride; unsafe { Buffer.MemoryCopy(pSrc.ToPointer(), pDst.ToPointer(), offset, offset); } } } NetVips.Free(memory); } finally { if (bd != null) { dst.UnlockBits(bd); } } return(dst); }