123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- using System;
- using System.Collections.ObjectModel;
- using System.Diagnostics.CodeAnalysis;
- using System.Runtime.InteropServices;
- using System.Security;
- using System.Windows;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using HandyControl.Tools.Interop;
- namespace HandyControl.Tools;
- [SuppressMessage("ReSharper", "ArrangeRedundantParentheses")]
- [SuppressMessage("ReSharper", "IntVariableOverflowInUncheckedContext")]
- internal static class IconHelper
- {
- private static Size SmallIconSize;
- private static Size IconSize;
- private static int SystemBitDepth;
- [SecurityCritical, SecuritySafeCritical]
- public static void GetIconHandlesFromImageSource(ImageSource image, out IconHandle largeIconHandle, out IconHandle smallIconHandle)
- {
- EnsureSystemMetrics();
- largeIconHandle = CreateIconHandleFromImageSource(image, IconSize);
- smallIconHandle = CreateIconHandleFromImageSource(image, SmallIconSize);
- }
- [SecurityCritical]
- public static IconHandle CreateIconHandleFromImageSource(ImageSource image, Size size)
- {
- EnsureSystemMetrics();
- var asGoodAsItGets = false;
- var bf = image as BitmapFrame;
- if (bf?.Decoder?.Frames != null)
- {
- bf = GetBestMatch(bf.Decoder.Frames, size);
- asGoodAsItGets = bf.Decoder is IconBitmapDecoder || bf.PixelWidth == (int) size.Width && bf.PixelHeight == (int) size.Height;
- image = bf;
- }
- if (!asGoodAsItGets)
- {
- bf = BitmapFrame.Create(GenerateBitmapSource(image, size));
- }
- return CreateIconHandleFromBitmapFrame(bf);
- }
- [SecurityCritical]
- private static IconHandle CreateIconHandleFromBitmapFrame(BitmapFrame sourceBitmapFrame)
- {
- BitmapSource bitmapSource = sourceBitmapFrame;
- if (bitmapSource.Format != PixelFormats.Bgra32 && bitmapSource.Format != PixelFormats.Pbgra32)
- {
- bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgra32, null, 0.0);
- }
- var w = bitmapSource.PixelWidth;
- var h = bitmapSource.PixelHeight;
- var bpp = bitmapSource.Format.BitsPerPixel;
- var stride = (bpp * w + 31) / 32 * 4;
- var sizeCopyPixels = stride * h;
- var xor = new byte[sizeCopyPixels];
- bitmapSource.CopyPixels(xor, stride, 0);
- return CreateIconCursor(xor, w, h, 0, 0, true);
- }
- [SecurityCritical]
- internal static IconHandle CreateIconCursor(byte[] colorArray, int width, int height, int xHotspot, int yHotspot, bool isIcon)
- {
- BitmapHandle colorBitmap = null;
- BitmapHandle maskBitmap = null;
- try
- {
- var bi = new InteropValues.BITMAPINFO(width, -height, 32)
- {
- biCompression = InteropValues.BI_RGB
- };
- var bits = IntPtr.Zero;
- colorBitmap = InteropMethods.CreateDIBSection(new HandleRef(null, IntPtr.Zero), ref bi, InteropValues.DIB_RGB_COLORS, ref bits, null, 0);
- if (colorBitmap.IsInvalid || bits == IntPtr.Zero)
- {
- return IconHandle.GetInvalidIcon();
- }
- Marshal.Copy(colorArray, 0, bits, colorArray.Length);
- var maskArray = GenerateMaskArray(width, height, colorArray);
- maskBitmap = InteropMethods.CreateBitmap(width, height, 1, 1, maskArray);
- if (maskBitmap.IsInvalid)
- {
- return IconHandle.GetInvalidIcon();
- }
- var iconInfo = new InteropValues.ICONINFO
- {
- fIcon = isIcon,
- xHotspot = xHotspot,
- yHotspot = yHotspot,
- hbmMask = maskBitmap,
- hbmColor = colorBitmap
- };
- return InteropMethods.CreateIconIndirect(iconInfo);
- }
- finally
- {
- colorBitmap?.Dispose();
- maskBitmap?.Dispose();
- }
- }
- private static byte[] GenerateMaskArray(int width, int height, byte[] colorArray)
- {
- var nCount = width * height;
- var bytesPerScanLine = AlignToBytes(width, 2) / 8;
- var bitsMask = new byte[bytesPerScanLine * height];
- for (var i = 0; i < nCount; i++)
- {
- var hPos = i % width;
- var vPos = i / width;
- var byteIndex = hPos / 8;
- var offsetBit = (byte) (0x80 >> (hPos % 8));
- if (colorArray[i * 4 + 3] == 0x00)
- {
- bitsMask[byteIndex + bytesPerScanLine * vPos] |= offsetBit;
- }
- else
- {
- bitsMask[byteIndex + bytesPerScanLine * vPos] &= (byte) ~offsetBit;
- }
- if (hPos == width - 1 && width == 8)
- {
- bitsMask[1 + bytesPerScanLine * vPos] = 0xff;
- }
- }
- return bitsMask;
- }
- internal static int AlignToBytes(double original, int nBytesCount)
- {
- var nBitsCount = 8 << (nBytesCount - 1);
- return ((int) Math.Ceiling(original) + (nBitsCount - 1)) / nBitsCount * nBitsCount;
- }
- private static BitmapSource GenerateBitmapSource(ImageSource img, Size renderSize)
- {
- var drawingDimensions = new Rect(0, 0, renderSize.Width, renderSize.Height);
- var renderRatio = renderSize.Width / renderSize.Height;
- var aspectRatio = img.Width / img.Height;
- if (img.Width <= renderSize.Width && img.Height <= renderSize.Height)
- {
- drawingDimensions = new Rect((renderSize.Width - img.Width) / 2, (renderSize.Height - img.Height) / 2, img.Width, img.Height);
- }
- else if (renderRatio > aspectRatio)
- {
- var scaledRenderWidth = (img.Width / img.Height) * renderSize.Width;
- drawingDimensions = new Rect((renderSize.Width - scaledRenderWidth) / 2, 0, scaledRenderWidth, renderSize.Height);
- }
- else if (renderRatio < aspectRatio)
- {
- var scaledRenderHeight = img.Height / img.Width * renderSize.Height;
- drawingDimensions = new Rect(0, (renderSize.Height - scaledRenderHeight) / 2, renderSize.Width, scaledRenderHeight);
- }
- var dv = new DrawingVisual();
- var dc = dv.RenderOpen();
- dc.DrawImage(img, drawingDimensions);
- dc.Close();
- var bmp = new RenderTargetBitmap((int) renderSize.Width, (int) renderSize.Height, 96, 96, PixelFormats.Pbgra32);
- bmp.Render(dv);
- return bmp;
- }
- private static BitmapFrame GetBestMatch(ReadOnlyCollection<BitmapFrame> frames, Size size)
- {
- var bestScore = int.MaxValue;
- var bestBpp = 0;
- var bestIndex = 0;
- var isBitmapIconDecoder = frames[0].Decoder is IconBitmapDecoder;
- for (var i = 0; i < frames.Count && bestScore != 0; ++i)
- {
- var currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel;
- if (currentIconBitDepth == 0)
- {
- currentIconBitDepth = 8;
- }
- var score = MatchImage(frames[i], size, currentIconBitDepth);
- if (score < bestScore)
- {
- bestIndex = i;
- bestBpp = currentIconBitDepth;
- bestScore = score;
- }
- else if (score == bestScore)
- {
- if (bestBpp < currentIconBitDepth)
- {
- bestIndex = i;
- bestBpp = currentIconBitDepth;
- }
- }
- }
- return frames[bestIndex];
- }
- private static int MatchImage(BitmapFrame frame, Size size, int bpp)
- {
- var score = 2 * MyAbs(bpp, SystemBitDepth, false) +
- MyAbs(frame.PixelWidth, (int) size.Width, true) +
- MyAbs(frame.PixelHeight, (int) size.Height, true);
- return score;
- }
- private static int MyAbs(int valueHave, int valueWant, bool fPunish)
- {
- var diff = (valueHave - valueWant);
- if (diff < 0)
- {
- diff = (fPunish ? -2 : -1) * diff;
- }
- return diff;
- }
- [SecurityCritical, SecuritySafeCritical]
- private static void EnsureSystemMetrics()
- {
- if (SystemBitDepth == 0)
- {
- var hdcDesktop = new HandleRef(null, InteropMethods.GetDC(new HandleRef()));
- try
- {
- var sysBitDepth = InteropMethods.GetDeviceCaps(hdcDesktop, InteropValues.BITSPIXEL);
- sysBitDepth *= InteropMethods.GetDeviceCaps(hdcDesktop, InteropValues.PLANES);
- if (sysBitDepth == 8)
- {
- sysBitDepth = 4;
- }
- var cxSmallIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CXSMICON);
- var cySmallIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CYSMICON);
- var cxIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CXICON);
- var cyIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CYICON);
- SmallIconSize = new Size(cxSmallIcon, cySmallIcon);
- IconSize = new Size(cxIcon, cyIcon);
- SystemBitDepth = sysBitDepth;
- }
- finally
- {
- InteropMethods.ReleaseDC(new HandleRef(), hdcDesktop);
- }
- }
- }
- [SecurityCritical, SecuritySafeCritical]
- public static void GetDefaultIconHandles(out IconHandle largeIconHandle, out IconHandle smallIconHandle)
- {
- largeIconHandle = null;
- smallIconHandle = null;
- SecurityHelper.DemandUIWindowPermission();
- var iconModuleFile = InteropMethods.GetModuleFileName(new HandleRef());
- InteropMethods.ExtractIconEx(iconModuleFile, 0, out largeIconHandle, out smallIconHandle, 1);
- }
- }
|