public void Serialize() { MemoryStream stream = new MemoryStream(); ImageFormat format = null; switch (this.Type) { case ICOImageType.BMP: format = ImageFormat.Bmp; break; case ICOImageType.PNG: format = ImageFormat.Png; break; default: throw new FormatException("Invalid image format type"); } this.Image.Save(stream, format); serialized = stream.ToArray(); // For BMP images, strip BITMAPFILEHEADER + add AND mask if (Type == ICOImageType.BMP) { // Generate AND mask: false for transparent, true for opaque bool[] ANDmask = new bool[this.Height * this.Width]; for (int i = 0; i < ANDmask.Length; i++) { ANDmask[i] = ((this.Image.GetPixel(i % this.Width, i / this.Width).A == 0) ? false : true); } serialized = BMP.ToICO(serialized, ANDmask); } }
private static int GetRowSize(int width, int bpp) { if (bpp < 8 || bpp % 8 != 0) { throw new FormatException(string.Format("Invalid BMP bpp: {0}", bpp)); } if (width < 0) { throw new FormatException(string.Format("Invalid BMP width: {0}", width)); } return(width * bpp / 8 + BMP.GetRowAlignmentBytes(width, bpp)); }
public static byte[] ToICO(byte[] BMPData, bool[] ANDmask) { byte[] result = Bytes.Subset(BMPData, BITMAPFILEHEADER_SIZE, BMPData.Length - BITMAPFILEHEADER_SIZE); /* * wikipedia.org/wiki/ICO_(file_format) * * Images with less than 32 bits of color depth follow a particular format: the image is encoded as * a single image consisting of a color mask (the "XOR mask") together with an opacity mask (the "AND mask"). * The XOR mask must precede the AND mask inside the bitmap data; * if the image is stored in bottom-up order (which it most likely is), the XOR mask would be drawn below the AND mask. * The AND mask is 1 bit per pixel, regardless of the color depth specified by the BMP header, and specifies * which pixels are fully transparent and which are fully opaque. * The XOR mask conforms to the bit depth specified in the BMP header * and specifies the numerical color or palette value for each pixel. * Together, the AND mask and XOR mask make for a non-transparent image representing an image with 1-bit transparency; * they also allow for inversion of the background. * * The height for the image in the ICONDIRENTRY structure of the ICO/CUR file takes on that of the intended image dimensions * (after the masks are composited), whereas the height in the BMP header takes on that of the two mask images combined * (before they are composited). Therefore, the masks must each be of the same dimensions, and the height * specified in the BMP header must be exactly twice the height specified in the ICONDIRENTRY structure. */ int bpp = BMP.GetBitsPerPixel(result); int height = Bytes.FromBytes(result, BITMAPINFOHEADER_HEIGHT_OFFSET, 4); byte[] heightBytes = Bytes.FromInt(height * 2, 4); Bytes.Replace(result, heightBytes, BITMAPINFOHEADER_HEIGHT_OFFSET); if (bpp < 32) { // Create and append AND mask after the bitmap data /* * int width = Bytes.FromBytes(result, BITMAPINFOHEADER_WIDTH_OFFSET, 4); * int BytesPerPixel = bpp / 8; * int RowAlignment = BMP.GetRowAlignmentBytes(width, bpp); * byte[] ANDbytes = new byte[ANDmask.Length * BytesPerPixel + height * RowAlignment]; * for (int i = 0; i < ANDmask.Length; i++) * for (int j = 0; j < BytesPerPixel; j++) * ANDbytes[i * BytesPerPixel + j] = ANDmask[i] ? (byte)0xFF : (byte)0x00; * result = Bytes.Merge(result, ANDbytes); */ } return(result); }
internal ICOImage(ICOType type, ICONDIRENTRY icondirentry, byte[] icoData) { byte[] imageData = Bytes.Subset(icoData, icondirentry.Image.Offset, icondirentry.Image.Size); if (BMP.isStrippedBMP(imageData)) { imageData = BMP.FromICO(icondirentry, imageData); this.Type = ICOImageType.BMP; } else { this.Type = ICOImageType.PNG; } MemoryStream stream = new MemoryStream(imageData); this.Image = new Bitmap(System.Drawing.Image.FromStream(stream)); this.HotspotX = icondirentry.Image.HotspotX; this.HotspotY = icondirentry.Image.HotspotY; }
public static byte[] Get24bppBMP(Bitmap image) { int bpp = 24; int headersSize = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE; int fileSize = BMP.GetSize(image.Width, image.Height, bpp); ByteStream stream = new ByteStream(fileSize); // BMP file header stream.Write16(0x4D42); stream.Write32(fileSize); stream.Write16(0); stream.Write16(0); stream.Write32(headersSize); // BMP info header stream.Write32(BITMAPINFOHEADER_SIZE); stream.Write32(image.Width); stream.Write32(image.Height); stream.Write16(1); stream.Write16(bpp); stream.Write32(0); stream.Write32(fileSize - headersSize); stream.Write32(0); stream.Write32(0); stream.Write32(0); stream.Write32(0); // BMP image data int alignmentBytes = BMP.GetRowAlignmentBytes(image.Width, bpp); byte[] rowAlignment = new byte[alignmentBytes]; for (int y = image.Height - 1; y >= 0; y--) { for (int x = 0; x < image.Width; x++) { Color color = image.GetPixel(x, y); byte[] pixel = { color.R, color.G, color.B }; stream.Write(pixel); } stream.Write(rowAlignment); } return(stream.Buffer); }