// TODO: check image not ignored (presently this method doesnt seem to be called unless width/height // refer to image) private void SaveBestSingleIcon(BinaryWriter writer, int width, int height) { writer.Write(iconDir.idReserved); writer.Write(iconDir.idType); writer.Write((ushort)1); // find best entry and save it int best = 0; int bitCount = 0; for (int i = 0; i < iconDir.idCount; i++) { IconDirEntry ide = iconDir.idEntries [i]; if ((width == ide.width) && (height == ide.height)) { if (ide.bitCount >= bitCount) { bitCount = ide.bitCount; best = i; } } } SaveIconDirEntry(writer, iconDir.idEntries [best], 22); SaveIconImage(writer, (IconImage)imageData [best]); }
public void Save(Stream stream) { _frames.SortAscending(); stream.Position = 0; var writer = new BinaryWriter(stream, System.Text.Encoding.UTF32); var framesCount = Convert.ToUInt16(_frames.Count); const ushort fileHeaderLength = 6; const ushort frameHeaderLength = 16; var fileHeader = new IconDir(framesCount); writer.Write(fileHeader.IdReserved); writer.Write(fileHeader.IdType); writer.Write(fileHeader.IdCount); var data = new byte[framesCount][]; foreach (var frame in _frames) { var frameIndex = _frames.IndexOf(frame); data[frameIndex] = GetPngData(frame); //if (frame.PixelWidth == 256) //{ // data[frameIndex] = GetPngData(frame); //} //else //{ // data[frameIndex] = GetBmpData(frame); //} } uint frameDataOffset = fileHeaderLength; frameDataOffset += (uint)(frameHeaderLength * framesCount); foreach (var frame in _frames) { var frameIndex = _frames.IndexOf(frame); if (frameIndex > 0) { frameDataOffset += Convert.ToUInt32(data[frameIndex - 1].Length); } var frameHeader = new IconDirEntry((ushort)frame.PixelWidth, (ushort)frame.PixelHeight, Convert.ToUInt16(frame.Format.BitsPerPixel), Convert.ToUInt32(data[frameIndex].Length), frameDataOffset); writer.Write(frameHeader.BWidth); writer.Write(frameHeader.BHeight); writer.Write(frameHeader.BColorCount); writer.Write(frameHeader.BReserved); writer.Write(frameHeader.WPlanes); writer.Write(frameHeader.WBitCount); writer.Write(frameHeader.DwBytesInRes); writer.Write(frameHeader.DwImageOffset); } foreach (var frameData in data) { writer.Write(frameData); } }
public void Save(Stream outputStream) { if (iconDir.idEntries != null) { BinaryWriter bw = new BinaryWriter(outputStream); //write icondir bw.Write(iconDir.idReserved); bw.Write(iconDir.idType); ushort count = iconDir.idCount; bw.Write(count); //now write iconDirEntries for (int i = 0; i < (int)count; i++) { IconDirEntry ide = iconDir.idEntries[i]; bw.Write(ide.width); bw.Write(ide.height); bw.Write(ide.colorCount); bw.Write(ide.reserved); bw.Write(ide.planes); bw.Write(ide.bitCount); bw.Write(ide.bytesInRes); bw.Write(ide.imageOffset); } //now write iconImage data for (int i = 0; i < (int)count; i++) { BitmapInfoHeader bih = imageData[i].iconHeader; bw.Write(bih.biSize); bw.Write(bih.biWidth); bw.Write(bih.biHeight); bw.Write(bih.biPlanes); bw.Write(bih.biBitCount); bw.Write(bih.biCompression); bw.Write(bih.biSizeImage); bw.Write(bih.biXPelsPerMeter); bw.Write(bih.biYPelsPerMeter); bw.Write(bih.biClrUsed); bw.Write(bih.biClrImportant); //now write color table int colCount = imageData[i].iconColors.Length; for (int j = 0; j < colCount; j++) { bw.Write(imageData[i].iconColors[j]); } //now write XOR Mask bw.Write(imageData[i].iconXOR); //now write AND Mask bw.Write(imageData[i].iconAND); } bw.Flush(); } }
private void SaveIconDirEntry(BinaryWriter writer, IconDirEntry ide, uint offset) { writer.Write(ide.width); writer.Write(ide.height); writer.Write(ide.colorCount); writer.Write(ide.reserved); writer.Write(ide.planes); writer.Write(ide.bitCount); writer.Write(ide.bytesInRes); writer.Write((offset == uint.MaxValue) ? ide.imageOffset : offset); }
private static void SaveIconDirEntry(BinaryWriter writer, IconDirEntry iconDirEntry) { writer.Write(iconDirEntry.width); writer.Write(iconDirEntry.height); writer.Write(iconDirEntry.colorCount); writer.Write(iconDirEntry.reserved); writer.Write(iconDirEntry.planes); writer.Write(iconDirEntry.bitCount); writer.Write(iconDirEntry.bytesInRes); writer.Write(iconDirEntry.imageOffset); }
public Icon(Icon original, Size size) { this.iconSize = size; this.winHandle = original.winHandle; this.iconDir = original.iconDir; this.imageData = original.imageData; int count = iconDir.idCount; bool sizeObtained = false; for (int i = 0; i < count; i++) { IconDirEntry ide = iconDir.idEntries[i]; if (!sizeObtained) { if (ide.height == size.Height && ide.width == size.Width) { this.id = (ushort)i; sizeObtained = true; this.iconSize.Height = ide.height; this.iconSize.Width = ide.width; break; } } } if (!sizeObtained) { uint largestSize = 0; for (int j = 0; j < count; j++) { if (iconDir.idEntries[j].bytesInRes >= largestSize) { largestSize = iconDir.idEntries[j].bytesInRes; this.id = (ushort)j; this.iconSize.Height = iconDir.idEntries[j].height; this.iconSize.Width = iconDir.idEntries[j].width; } } } }
public Icon(Icon original, Size size) { if (original == null) { throw new ArgumentNullException(nameof(original)); } iconSize = size; iconDir = original.iconDir; int count = iconDir.idCount; if (count > 0) { imageData = original.imageData; id = ushort.MaxValue; for (ushort i = 0; i < count; i++) { IconDirEntry ide = iconDir.idEntries[i]; if (((ide.height == size.Height) || (ide.width == size.Width)) && !ide.png) { id = i; break; } } // if a perfect match isn't found we look for the biggest icon *smaller* than specified if (id == ushort.MaxValue) { int requested = Math.Min(size.Height, size.Width); // previously best set to 1st image, as this might not be smallest changed loop to check all IconDirEntry?best = null; for (ushort i = 0; i < count; i++) { IconDirEntry ide = iconDir.idEntries[i]; if (((ide.height < requested) || (ide.width < requested)) && !ide.png) { if (best == null) { best = ide; id = i; } else if ((ide.height > best.Value.height) || (ide.width > best.Value.width)) { best = ide; id = i; } } } } // last one, if nothing better can be found if (id == ushort.MaxValue) { int i = count; while (id == ushort.MaxValue && i > 0) { i--; if (!iconDir.idEntries[i].png) { id = (ushort)i; } } } if (id == ushort.MaxValue) { throw new ArgumentException(SR.NoValidIconImageFound, nameof(original)); } iconSize.Height = iconDir.idEntries[id].height; iconSize.Width = iconDir.idEntries[id].width; } else { iconSize.Height = size.Height; iconSize.Width = size.Width; } if (original.bitmap != null) { bitmap = (Bitmap)original.bitmap.Clone(); } }
private void SaveBitmapAsIcon(BinaryWriter writer) { writer.Write((ushort)0); // idReserved must be 0 writer.Write((ushort)1); // idType must be 1 writer.Write((ushort)1); // only one icon // when transformed into a bitmap only a single image exists IconDirEntry ide = new IconDirEntry(); ide.width = (byte)bitmap.Width; ide.height = (byte)bitmap.Height; ide.colorCount = 0; // 32 bbp == 0, for palette size ide.reserved = 0; // always 0 ide.planes = 0; ide.bitCount = 32; ide.imageOffset = 22; // 22 is the first icon position (for single icon files) BitmapInfoHeader bih = new BitmapInfoHeader(); bih.biSize = (uint)Marshal.SizeOf(typeof(BitmapInfoHeader)); bih.biWidth = bitmap.Width; bih.biHeight = 2 * bitmap.Height; // include both XOR and AND images bih.biPlanes = 1; bih.biBitCount = 32; bih.biCompression = 0; bih.biSizeImage = 0; bih.biXPelsPerMeter = 0; bih.biYPelsPerMeter = 0; bih.biClrUsed = 0; bih.biClrImportant = 0; IconImage ii = new IconImage(); ii.iconHeader = bih; ii.iconColors = new uint [0]; // no palette int xor_size = (((bih.biBitCount * bitmap.Width + 31) & ~31) >> 3) * bitmap.Height; ii.iconXOR = new byte [xor_size]; int p = 0; for (int y = bitmap.Height - 1; y >= 0; y--) { for (int x = 0; x < bitmap.Width; x++) { Color c = bitmap.GetPixel(x, y); ii.iconXOR [p++] = c.B; ii.iconXOR [p++] = c.G; ii.iconXOR [p++] = c.R; ii.iconXOR [p++] = c.A; } } int and_line_size = (((Width + 31) & ~31) >> 3); // must be a multiple of 4 bytes int and_size = and_line_size * bitmap.Height; ii.iconAND = new byte [and_size]; ide.bytesInRes = (uint)(bih.biSize + xor_size + and_size); SaveIconDirEntry(writer, ide, UInt32.MaxValue); SaveIconImage(writer, ii); }
private void InitFromStreamWithSize(Stream stream, int width, int height) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (stream.Length == 0) { throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(stream))); } bool sizeObtained = false; ushort dirEntryCount; // Read the icon header using (var reader = new BinaryReader(stream)) { iconDir.idReserved = reader.ReadUInt16(); if (iconDir.idReserved != 0) //must be 0 { throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(stream))); } iconDir.idType = reader.ReadUInt16(); if (iconDir.idType != 1) //must be 1 { throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(stream))); } dirEntryCount = reader.ReadUInt16(); imageData = new ImageData[dirEntryCount]; iconDir.idCount = dirEntryCount; iconDir.idEntries = new IconDirEntry[dirEntryCount]; // Now read in the IconDirEntry structures for (int i = 0; i < dirEntryCount; i++) { var ide = new IconDirEntry { width = reader.ReadByte(), height = reader.ReadByte(), colorCount = reader.ReadByte(), reserved = reader.ReadByte(), planes = reader.ReadUInt16(), bitCount = reader.ReadUInt16(), bytesInRes = reader.ReadUInt32(), imageOffset = reader.ReadUInt32() }; // Vista 256x256 icons points directly to a PNG bitmap // 256x256 icons are decoded as 0x0 (width and height are encoded as BYTE) // We mark them as png and later on we just store the raw bytes to be able to save to a file. ide.png = (ide.width == 0) && (ide.height == 0); iconDir.idEntries[i] = ide; if (!sizeObtained) { if (((ide.height == height) || (ide.width == width)) && !ide.png) { this.id = (ushort)i; sizeObtained = true; this.iconSize.Height = ide.height; this.iconSize.Width = ide.width; } } } // If we havent found the best match, return the one with the largest size. if (!sizeObtained) { uint largestSize = 0; for (int j = 0; j < dirEntryCount; j++) { if (iconDir.idEntries[j].bytesInRes >= largestSize && !iconDir.idEntries[j].png) { largestSize = iconDir.idEntries[j].bytesInRes; this.id = (ushort)j; this.iconSize.Height = iconDir.idEntries[j].height; this.iconSize.Width = iconDir.idEntries[j].width; } } } // Now read in the icon data bool valid = false; for (int j = 0; j < dirEntryCount; j++) { stream.Seek(iconDir.idEntries[j].imageOffset, SeekOrigin.Begin); byte[] buffer = new byte[iconDir.idEntries[j].bytesInRes]; stream.Read(buffer, 0, buffer.Length); using (var bihReader = new BinaryReader(new MemoryStream(buffer))) { uint headerSize = bihReader.ReadUInt32(); int headerWidth = bihReader.ReadInt32(); // Process PNG images into IconDump if (iconDir.idEntries[j].png || (headerSize == PNGSignature1 && headerWidth == (int)PNGSignature2)) { IconDump id = new IconDump(); id.data = buffer; imageData[j] = id; iconDir.idEntries[j].png = true; continue; } // We found a valid icon BMP entry. valid = true; var bih = new BitmapInfoHeader { biSize = headerSize, biWidth = headerWidth, biHeight = bihReader.ReadInt32(), biPlanes = bihReader.ReadUInt16(), biBitCount = bihReader.ReadUInt16(), biCompression = bihReader.ReadUInt32(), biSizeImage = bihReader.ReadUInt32(), biXPelsPerMeter = bihReader.ReadInt32(), biYPelsPerMeter = bihReader.ReadInt32(), biClrUsed = bihReader.ReadUInt32(), biClrImportant = bihReader.ReadUInt32() }; var iidata = new IconImage { iconHeader = bih }; // Read the number of colors used and corresponding memory occupied by // color table. Fill this memory chunk into rgbquad[] int numColors; switch (bih.biBitCount) { case 1: numColors = 2; break; case 4: numColors = 16; break; case 8: numColors = 256; break; default: numColors = 0; break; } iidata.iconColors = new uint[numColors]; for (int i = 0; i < numColors; i++) { iidata.iconColors[i] = bihReader.ReadUInt32(); } //XOR mask is immediately after ColorTable and its size is //icon height* no. of bytes per line //icon height is half of BITMAPINFOHEADER.biHeight, since it contains //both XOR as well as AND mask bytes int iconHeight = bih.biHeight / 2; //bytes per line should be uint aligned int numBytesPerLine = checked ((((bih.biWidth * bih.biPlanes * bih.biBitCount) + 31) >> 5) << 2); //Determine the XOR array Size int xorSize = checked (numBytesPerLine * iconHeight); iidata.iconXOR = new byte[xorSize]; int nread = bihReader.Read(iidata.iconXOR, 0, xorSize); if (nread != xorSize) { throw new ArgumentException(SR.Format(SR.IconInvalidMaskLength, "XOR", xorSize, nread), nameof(stream)); } //Determine the AND array size numBytesPerLine = checked ((((bih.biWidth) + 31) & ~31) >> 3); int andSize = checked (numBytesPerLine * iconHeight); iidata.iconAND = new byte[andSize]; nread = bihReader.Read(iidata.iconAND, 0, andSize); if (nread != andSize) { throw new ArgumentException(SR.Format(SR.IconInvalidMaskLength, "AND", andSize, nread), nameof(stream)); } imageData[j] = iidata; } } // Throw error if no valid entries found if (!valid) { throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER, SR.Format(SR.InvalidPictureType, "picture", nameof(stream))); } } }
public Icon(Icon original, Size size) { if (original == null) { throw new ArgumentException("original"); } iconSize = size; iconDir = original.iconDir; int count = iconDir.idCount; if (count > 0) { imageData = original.imageData; id = UInt16.MaxValue; for (ushort i = 0; i < count; i++) { IconDirEntry ide = iconDir.idEntries [i]; if ((ide.height == size.Height) || (ide.width == size.Width)) { id = i; break; } } // if a perfect match isn't found we look for the biggest icon *smaller* than specified if (id == UInt16.MaxValue) { int requested = Math.Min(size.Height, size.Width); IconDirEntry best = iconDir.idEntries [0]; for (ushort i = 1; i < count; i++) { IconDirEntry ide = iconDir.idEntries [i]; if ((ide.height < requested) || (ide.width < requested)) { if ((ide.height > best.height) || (ide.width > best.width)) { id = i; } } } } // last one, if nothing better can be found if (id == UInt16.MaxValue) { id = (ushort)(count - 1); } iconSize.Height = iconDir.idEntries [id].height; iconSize.Width = iconDir.idEntries [id].width; } else { iconSize.Height = size.Height; iconSize.Width = size.Width; } if (original.bitmap != null) { bitmap = (Bitmap)original.bitmap.Clone(); } }
private void SaveBitmapAsIcon (BinaryWriter writer) { writer.Write ((ushort)0); // idReserved must be 0 writer.Write ((ushort)1); // idType must be 1 writer.Write ((ushort)1); // only one icon // when transformed into a bitmap only a single image exists IconDirEntry ide = new IconDirEntry (); ide.width = (byte) bitmap.Width; ide.height = (byte) bitmap.Height; ide.colorCount = 0; // 32 bbp == 0, for palette size ide.reserved = 0; // always 0 ide.planes = 0; ide.bitCount = 32; ide.imageOffset = 22; // 22 is the first icon position (for single icon files) BitmapInfoHeader bih = new BitmapInfoHeader (); bih.biSize = (uint) Marshal.SizeOf (typeof (BitmapInfoHeader)); bih.biWidth = bitmap.Width; bih.biHeight = 2 * bitmap.Height; // include both XOR and AND images bih.biPlanes = 1; bih.biBitCount = 32; bih.biCompression = 0; bih.biSizeImage = 0; bih.biXPelsPerMeter = 0; bih.biYPelsPerMeter = 0; bih.biClrUsed = 0; bih.biClrImportant = 0; IconImage ii = new IconImage (); ii.iconHeader = bih; ii.iconColors = new uint [0]; // no palette int xor_size = (((bih.biBitCount * bitmap.Width + 31) & ~31) >> 3) * bitmap.Height; ii.iconXOR = new byte [xor_size]; int p = 0; for (int y = bitmap.Height - 1; y >=0; y--) { for (int x = 0; x < bitmap.Width; x++) { Color c = bitmap.GetPixel (x, y); ii.iconXOR [p++] = c.B; ii.iconXOR [p++] = c.G; ii.iconXOR [p++] = c.R; ii.iconXOR [p++] = c.A; } } int and_line_size = (((Width + 31) & ~31) >> 3); // must be a multiple of 4 bytes int and_size = and_line_size * bitmap.Height; ii.iconAND = new byte [and_size]; ide.bytesInRes = (uint) (bih.biSize + xor_size + and_size); SaveIconDirEntry (writer, ide, UInt32.MaxValue); SaveIconImage (writer, ii); }
private void SaveIconDirEntry (BinaryWriter writer, IconDirEntry ide, uint offset) { writer.Write (ide.width); writer.Write (ide.height); writer.Write (ide.colorCount); writer.Write (ide.reserved); writer.Write (ide.planes); writer.Write (ide.bitCount); writer.Write (ide.bytesInRes); writer.Write ((offset == UInt32.MaxValue) ? ide.imageOffset : offset); }
/// <summary> /// Converts this <see cref="Bitmap"/> to an <see cref="Icon"/>. /// </summary> /// <param name="bitmap">The bitmap to convert.</param> public static Icon ToIcon(this Bitmap bitmap) { var bih = new BitmapInfoHeader { biSize = (uint)Marshal.SizeOf(typeof(BitmapInfoHeader)), biWidth = bitmap.Width, biHeight = 2 * bitmap.Height, // include both XOR and AND images biPlanes = 1, biBitCount = 32, biCompression = 0, biSizeImage = 0, biXPelsPerMeter = 0, biYPelsPerMeter = 0, biClrUsed = 0, biClrImportant = 0 }; var xorSize = (((bih.biBitCount * bitmap.Width + 31) & ~31) >> 3) * bitmap.Height; var andLineSize = (((bitmap.Width + 31) & ~31) >> 3); // must be a multiple of 4 bytes var andSize = andLineSize * bitmap.Height; var iconDirEntry = new IconDirEntry { width = (byte)bitmap.Width, height = (byte)bitmap.Height, colorCount = 0, // 32 bbp == 0, for palette size reserved = 0, planes = 0, bitCount = 32, imageOffset = 22, // 22 is the first icon position (for single icon files) bytesInRes = (uint)(bih.biSize + xorSize + andSize) }; var iconImage = new IconImage { iconHeader = bih, iconColors = new uint [0], // no palette iconXOR = new byte [xorSize], iconAND = new byte [andSize] }; var pixel = 0; for (var y = bitmap.Height - 1; y >= 0; y--) { for (var x = 0; x < bitmap.Width; x++) { var color = bitmap.GetPixel(x, y); iconImage.iconXOR [pixel++] = color.B; iconImage.iconXOR [pixel++] = color.G; iconImage.iconXOR [pixel++] = color.R; iconImage.iconXOR [pixel++] = color.A; } } using (var outputStream = new MemoryStream()) using (var streamWriter = new BinaryWriter(outputStream)) { streamWriter.Write((ushort)0); // idReserved must be 0 streamWriter.Write((ushort)1); // idType must be 1 streamWriter.Write((ushort)1); // only one icon SaveIconDirEntry(streamWriter, iconDirEntry); SaveIconImage(streamWriter, iconImage); outputStream.Seek(0, SeekOrigin.Begin); return(new Icon(outputStream)); } }
} // Save /// <summary> /// Saves the supplied images in .ico file format /// </summary> /// <param name="stream"></param> public void Save(Stream stream) { // although not strictly necessary, it's customary to // write the images sorted by size and bpp var icons = from entry in _images orderby entry.Key.Size ascending, entry.Key.BitsPerPixel ascending select new { Image = entry.Value, entry.Key.SaveAs }; // create icon directory header var iconDirHeader = new IconDirHeader(_images.Count); // create icon directory entries var entries = new IconDirEntry[_images.Count]; var imageData = new byte[_images.Count][]; var index = 0; foreach (var icon in icons) { imageData[index] = GetIconImageData(icon.Image, icon.SaveAs); var entry = new IconDirEntry() { Width = (icon.Image.Width < 256) ? (byte)icon.Image.Width : (byte)0, Height = (icon.Image.Height < 256) ? (byte)icon.Image.Height : (byte)0, ColorCount = 0, // more than 256 colors Reserved = 0, // must be zero Planes = 1, BitCount = GetBitsPerPixel(icon.Image.PixelFormat), BytesInRes = (uint)imageData[index].Length, ImageOffset = 0 // to be set later }; entries[index++] = entry; } // foreach // set offsets var sizeOfIconDirEntry = Marshal.SizeOf <IconDirEntry>(); var offset = Marshal.SizeOf <IconDirHeader>() + (sizeOfIconDirEntry * _images.Count); for (index = 0; index < entries.Length; index++) { entries[index].ImageOffset = (uint)offset; offset += imageData[index].Length; } // for index // write data // 1. IconDirHeader WriteStruct(stream, iconDirHeader); // 2. IconDirEntries (one per icon image) foreach (var entry in entries) { WriteStruct(stream, entry); } // foreach entry // 3. Icon image data foreach (var data in imageData) { stream.Write(data, 0, data.Length); } // foreach } // Save