[runtime] Fix corlib out of date error with disabled COM
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / manifestimages.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal 
5 {
6     using System;
7     using System.IO;
8     using System.Reflection;
9     using System.Windows;
10     using System.Windows.Controls;
11     using System.Windows.Markup;
12     using System.Windows.Media.Imaging;
13     using System.Diagnostics;
14     using System.Runtime;
15     using System.Activities.Presentation;
16
17     // <summary>
18     // Helper class that knows how to load up icons that live in assemblies and follow
19     // our extensibility icon naming convention.
20     // </summary>
21     internal static class ManifestImages 
22     {
23
24         private static readonly string[] SupportedExtensions = new string[] {
25                 ".png", ".xaml", ".bmp", ".gif", ".jpg", ".jpeg"
26             };
27
28         // <summary>
29         // ----s open the assembly to which the specified Type belongs and tries
30         // to find and return an image that follows the naming conventions of:
31         //
32         //     My.Namespace.MyControl.Icon.png
33         //
34         // and matches the desired size most closely, if multiple such images are found.
35         // </summary>
36         // <param name="type">Type to look up</param>
37         // <param name="desiredSize">Desired size (may not be met)</param>
38         // <returns>Null (if no image was found), Image instance (for non-Xaml images),
39         // or object instance (for Xaml-instantiated structures)</returns>
40         // <exception cref="ArgumentNullException">if type is null</exception>
41         public static object GetImage(Type type, Size desiredSize) 
42         {
43             if (type == null)
44             {
45                 throw FxTrace.Exception.ArgumentNull("type");
46             }
47
48             Assembly assembly = type.Assembly;
49             string[] resourceNames = assembly.GetManifestResourceNames();
50
51             if (resourceNames == null || resourceNames.Length == 0)
52             {
53                 return null;
54             }
55
56             string fullTypeName = type.FullName;
57             string typeName = type.Name;
58
59             // Do a full namespace match first
60             ImageInfo bestMatch = FindBestMatch(type, assembly, resourceNames, desiredSize, delegate(string extensionlessResourceName) 
61             {
62                 return fullTypeName.Equals(extensionlessResourceName);
63             });
64
65             // Do a partial name match second, if full name didn't give us anything
66             bestMatch = bestMatch ?? FindBestMatch(type, assembly, resourceNames, desiredSize, delegate(string extensionlessResourceName) 
67             {
68                 return extensionlessResourceName != null && extensionlessResourceName.EndsWith(typeName, StringComparison.Ordinal);
69             });
70
71             if (bestMatch != null)
72             {
73                 return bestMatch.Image;
74             }
75
76             return null;
77         }
78
79         private static ImageInfo FindBestMatch(
80             Type type,
81             Assembly assembly,
82             string[] resourceNames,
83             Size desiredSize,
84             MatchNameDelegate matchName) 
85         {
86
87             Fx.Assert(type != null, "FindBestMatch - type parameter should not be null");
88             Fx.Assert(resourceNames != null && resourceNames.Length > 0, "resourceNames parameter should not be null");
89             Fx.Assert(matchName != null, "matchName parameter should not be null");
90
91             ImageInfo bestMatch = null;
92
93             for (int i = 0; i < resourceNames.Length; i++) 
94             {
95
96                 string extension = Path.GetExtension(resourceNames[i]);
97
98                 if (!IsExtensionSupported(extension))
99                 {
100                     continue;
101                 }
102
103                 if (!matchName(StripIconExtension(resourceNames[i])))
104                 {
105                     continue;
106                 }
107
108                 ImageInfo info = ProcessResource(assembly, resourceNames[i]);
109                 if (info == null)
110                 {
111                     continue;
112                 }
113
114                 // Try to match the found resource to the requested size
115                 float sizeMatch = info.Match(desiredSize);
116
117                 // Check for exact size match
118                 if (sizeMatch < float.Epsilon)
119                 {
120                     return info;
121                 }
122
123                 // Keep the best image found so far
124                 if (bestMatch == null ||
125                     bestMatch.LastMatch > sizeMatch)
126                 {
127                     bestMatch = info;
128                 }
129             }
130
131             return bestMatch;
132         }
133
134         // Tries to load up an image
135         private static ImageInfo ProcessResource(Assembly assembly, string resourceName) 
136         {
137             Stream stream = assembly.GetManifestResourceStream(resourceName);
138             if (stream == null)
139             {
140                 return null;
141             }
142
143             if (IsXamlContent(resourceName))
144             {
145                 return new XamlImageInfo(stream);
146             }
147             else
148             {
149                 return new BitmapImageInfo(stream);
150             }
151         }
152
153         // Checks to see whether the given extension is supported
154         private static bool IsExtensionSupported(string extension) 
155         {
156             for (int i = 0; i < SupportedExtensions.Length; i++)
157             {
158                 if (SupportedExtensions[i].Equals(extension, StringComparison.OrdinalIgnoreCase))
159                 {
160                     return true;
161                 }
162             }
163
164             return false;
165         }
166
167         // Returns true if the passed in resource name ends in ".xaml"
168         private static bool IsXamlContent(string resourceName) 
169         {
170             return ".xaml".Equals(Path.GetExtension(resourceName), StringComparison.OrdinalIgnoreCase);
171         }
172
173         // Strips ".Icon.ext" from a resource name
174         private static string StripIconExtension(string resourceName) 
175         {
176             if (resourceName == null)
177             {
178                 return null;
179             }
180
181             resourceName = Path.GetFileNameWithoutExtension(resourceName);
182             int dotIconIndex = resourceName.LastIndexOf(".Icon", StringComparison.OrdinalIgnoreCase);
183             if (dotIconIndex > 0)
184             {
185                 return resourceName.Substring(0, dotIconIndex);
186             }
187
188             return null;
189         }
190
191         private delegate bool MatchNameDelegate(string extensionlessResourceName);
192
193         // Helper class that has information about an image
194         private abstract class ImageInfo 
195         {
196
197             private float _lastMatch = 1;
198
199             protected ImageInfo() 
200             {
201             }
202
203             public float LastMatch 
204             { get { return _lastMatch; } }
205             public abstract object Image 
206             { get; }
207             protected abstract Size Size 
208             { get; }
209             protected abstract bool HasFixedSize 
210             { get; }
211
212             // gets value range from 0 to 1: 0 == perfect match, 1 == complete opposite
213             public float Match(Size desiredSize) 
214             {
215
216                 if (!this.HasFixedSize) 
217                 {
218                     _lastMatch = 0;
219                 }
220                 else 
221                 {
222                     Size actualSize = this.Size;
223
224                     float desiredAspectRatio = Math.Max(float.Epsilon, GetAspectRatio(desiredSize));
225                     float actualAspectRatio = Math.Max(float.Epsilon, GetAspectRatio(actualSize));
226
227                     float desiredArea = Math.Max(float.Epsilon, GetArea(desiredSize));
228                     float actualArea = Math.Max(float.Epsilon, GetArea(actualSize));
229
230                     // these values range from 0 to 1, 1 being perfect match, 0 being not so perfect match
231                     float ratioDiff = desiredAspectRatio < actualAspectRatio ? desiredAspectRatio / actualAspectRatio : actualAspectRatio / desiredAspectRatio;
232                     float areaDiff = desiredArea < actualArea ? desiredArea / actualArea : actualArea / desiredArea;
233
234                     float diff = ratioDiff * areaDiff;
235
236                     _lastMatch = Math.Min(1f, Math.Max(0f, 1f - diff));
237                 }
238
239                 return _lastMatch;
240             }
241
242             private static float GetAspectRatio(Size size) 
243             {
244                 if (size.Height < float.Epsilon)
245                 {
246                     return 0;
247                 }
248
249                 return (float)(size.Width / size.Height);
250             }
251
252             private static float GetArea(Size size) 
253             {
254                 return (float)(size.Width * size.Height);
255             }
256         }
257
258         // Helper class that knows how to deal with Xaml
259         private class XamlImageInfo : ImageInfo 
260         {
261
262             private object _image;
263
264             public XamlImageInfo(Stream stream) 
265             {
266                 _image = XamlReader.Load(stream);
267             }
268
269             public override object Image 
270             {
271                 get { return _image; }
272             }
273
274             protected override Size Size 
275             {
276                 get { return Size.Empty; }
277             }
278
279             protected override bool HasFixedSize 
280             {
281                 get { return false; }
282             }
283         }
284
285         // Helper class that knows how to deal with bitmaps
286         private class BitmapImageInfo : ImageInfo 
287         {
288
289             private Image _image;
290             private Size _size;
291
292             public BitmapImageInfo(Stream stream) 
293             {
294                 BitmapImage bmp = new BitmapImage();
295                 bmp.BeginInit();
296                 bmp.StreamSource = stream;
297                 bmp.EndInit();
298
299                 _image = new Image();
300                 _image.Source = bmp;
301
302                 _size = new Size(bmp.Width, bmp.Height);
303             }
304
305             public override object Image 
306             {
307                 get { return _image; }
308             }
309
310             protected override Size Size 
311             {
312                 get { return _size; }
313             }
314
315             protected override bool HasFixedSize 
316             {
317                 get { return true; }
318             }
319         }
320     }
321 }