2 // System.Drawing.Image.cs
4 // Copyright (C) 2002 Ximian, Inc. http://www.ximian.com
5 // Copyright (C) 2004 Novell, Inc. http://www.novell.com
7 // Author: Christian Meyer (Christian.Meyer@cs.tum.edu)
8 // Alexandre Pigolkine (pigolkine@gmx.de)
9 // Jordi Mas i Hernandez (jordi@ximian.com)
10 // Sanjay Gupta (gsanjay@novell.com)
11 // Ravindra (rkumar@novell.com)
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 using System.Runtime.Remoting;
39 using System.Runtime.Serialization;
40 using System.Runtime.InteropServices;
41 using System.ComponentModel;
42 using System.Drawing.Imaging;
44 using System.Reflection;
46 namespace System.Drawing
50 [Editor ("System.Drawing.Design.ImageEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
51 [TypeConverter (typeof(ImageConverter))]
52 [ImmutableObject (true)]
53 public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISerializable
55 public delegate bool GetThumbnailImageAbort();
58 internal IntPtr nativeObject = IntPtr.Zero;
67 private Image (SerializationInfo info, StreamingContext context)
69 foreach (SerializationEntry serEnum in info) {
70 if (String.Compare(serEnum.Name, "Data", true) == 0) {
71 byte[] bytes = (byte[]) serEnum.Value;
74 InitFromStream(new MemoryStream(bytes));
80 private static bool IsIndexedPixelFormat(PixelFormat pixfmt)
82 return ((pixfmt & PixelFormat.Indexed) != 0);
86 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
88 MemoryStream ms = new MemoryStream ();
89 this.Save (ms, RawFormat);
90 info.AddValue ("Data", ms.ToArray ());
95 public static Image FromFile(string filename)
97 return FromFile (filename, false);
100 public static Image FromFile(string filename, bool useEmbeddedColorManagement)
105 if (!File.Exists (filename))
106 throw new FileNotFoundException (filename);
108 if (useEmbeddedColorManagement)
109 st = GDIPlus.GdipLoadImageFromFileICM (filename, out imagePtr);
111 st = GDIPlus.GdipLoadImageFromFile (filename, out imagePtr);
113 GDIPlus.CheckStatus (st);
114 return new Bitmap (imagePtr);
117 public static Bitmap FromHbitmap(IntPtr hbitmap)
119 return FromHbitmap (hbitmap, IntPtr.Zero);
122 public static Bitmap FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
127 st = GDIPlus.GdipCreateBitmapFromHBITMAP (hbitmap, hpalette, out imagePtr);
129 GDIPlus.CheckStatus (st);
130 return new Bitmap (imagePtr);
133 public static Image FromStream (Stream stream)
135 return new Bitmap (stream);
138 public static Image FromStream (Stream stream, bool useECM)
140 return new Bitmap (stream, useECM);
143 // See http://support.microsoft.com/default.aspx?scid=kb;en-us;831419 for performance discussion
144 public static Image FromStream (Stream stream, bool useECM, bool validateImageData)
146 return new Bitmap (stream, useECM);
149 public static int GetPixelFormatSize(PixelFormat pixfmt)
153 case PixelFormat.Format16bppArgb1555:
154 case PixelFormat.Format16bppGrayScale:
155 case PixelFormat.Format16bppRgb555:
156 case PixelFormat.Format16bppRgb565:
159 case PixelFormat.Format1bppIndexed:
162 case PixelFormat.Format24bppRgb:
165 case PixelFormat.Format32bppArgb:
166 case PixelFormat.Format32bppPArgb:
167 case PixelFormat.Format32bppRgb:
170 case PixelFormat.Format48bppRgb:
173 case PixelFormat.Format4bppIndexed:
176 case PixelFormat.Format64bppArgb:
177 case PixelFormat.Format64bppPArgb:
180 case PixelFormat.Format8bppIndexed:
187 public static bool IsAlphaPixelFormat(PixelFormat pixfmt)
191 case PixelFormat.Format16bppArgb1555:
192 case PixelFormat.Format32bppArgb:
193 case PixelFormat.Format32bppPArgb:
194 case PixelFormat.Format64bppArgb:
195 case PixelFormat.Format64bppPArgb:
198 case PixelFormat.Format16bppGrayScale:
199 case PixelFormat.Format16bppRgb555:
200 case PixelFormat.Format16bppRgb565:
201 case PixelFormat.Format1bppIndexed:
202 case PixelFormat.Format24bppRgb:
203 case PixelFormat.Format32bppRgb:
204 case PixelFormat.Format48bppRgb:
205 case PixelFormat.Format4bppIndexed:
206 case PixelFormat.Format8bppIndexed:
213 public static bool IsCanonicalPixelFormat (PixelFormat pixfmt)
215 return ((pixfmt & PixelFormat.Canonical) != 0);
218 public static bool IsExtendedPixelFormat (PixelFormat pixfmt)
220 return ((pixfmt & PixelFormat.Extended) != 0);
223 internal void InitFromStream (Stream stream)
229 if (!stream.CanSeek) {
230 byte[] buffer = new byte[256];
235 if (buffer.Length < index + 256) {
236 byte[] newBuffer = new byte[buffer.Length * 2];
237 Array.Copy(buffer, newBuffer, buffer.Length);
240 count = stream.Read(buffer, index, 256);
245 stream = new MemoryStream(buffer, 0, index);
248 // check for Unix platforms - see FAQ for more details
249 // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
250 int platform = (int) Environment.OSVersion.Platform;
251 if ((platform == 4) || (platform == 128)) {
252 // Unix, with libgdiplus
253 // We use a custom API for this, because there's no easy way
254 // to get the Stream down to libgdiplus. So, we wrap the stream
255 // with a set of delegates.
256 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream);
258 st = GDIPlus.GdipLoadImageFromDelegate_linux (sh.GetHeaderDelegate, sh.GetBytesDelegate,
259 sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out imagePtr);
262 st = GDIPlus.GdipLoadImageFromStream(new ComIStreamWrapper(stream), out imagePtr);
265 GDIPlus.CheckStatus (st);
266 nativeObject = imagePtr;
270 public RectangleF GetBounds (ref GraphicsUnit pageUnit)
274 Status status = GDIPlus.GdipGetImageBounds (nativeObject, out source, ref pageUnit);
275 GDIPlus.CheckStatus (status);
280 public EncoderParameters GetEncoderParameterList(Guid format)
285 status = GDIPlus.GdipGetEncoderParameterListSize (nativeObject, ref format, out sz);
286 GDIPlus.CheckStatus (status);
288 IntPtr rawEPList = Marshal.AllocHGlobal ((int) sz);
289 EncoderParameters eps;
292 status = GDIPlus.GdipGetEncoderParameterList (nativeObject, ref format, sz, rawEPList);
293 eps = EncoderParameters.FromNativePtr (rawEPList);
294 GDIPlus.CheckStatus (status);
297 Marshal.FreeHGlobal (rawEPList);
303 public int GetFrameCount(FrameDimension dimension)
306 Guid guid = dimension.Guid;
307 Status status = GDIPlus.GdipImageGetFrameCount (nativeObject, ref guid, out count);
309 GDIPlus.CheckStatus (status);
315 public PropertyItem GetPropertyItem(int propid)
319 PropertyItem item = new PropertyItem ();
320 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
323 status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid,
325 GDIPlus.CheckStatus (status);
327 /* Get PropertyItem */
328 property = Marshal.AllocHGlobal (propSize);
330 status = GDIPlus.GdipGetPropertyItem (nativeObject, propid, propSize, property);
331 GDIPlus.CheckStatus (status);
332 gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure (property,
333 typeof (GdipPropertyItem));
334 GdipPropertyItem.MarshalTo (gdipProperty, item);
337 Marshal.FreeHGlobal (property);
342 public Image GetThumbnailImage (int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
344 if ((thumbWidth <= 0) || (thumbHeight <= 0))
345 throw new OutOfMemoryException ("Invalid thumbnail size");
347 Image ThumbNail = new Bitmap (thumbWidth, thumbHeight);
349 using (Graphics g = Graphics.FromImage (ThumbNail)) {
350 Status status = GDIPlus.GdipDrawImageRectRectI (g.nativeObject, nativeObject,
351 0, 0, thumbWidth, thumbHeight,
352 0, 0, this.Width, this.Height,
353 GraphicsUnit.Pixel, IntPtr.Zero, null, IntPtr.Zero);
355 GDIPlus.CheckStatus (status);
362 public void RemovePropertyItem (int propid)
364 Status status = GDIPlus.GdipRemovePropertyItem (nativeObject, propid);
365 GDIPlus.CheckStatus (status);
368 public void RotateFlip (RotateFlipType rotateFlipType)
370 Status status = GDIPlus.GdipImageRotateFlip (nativeObject, rotateFlipType);
371 GDIPlus.CheckStatus (status);
374 internal ImageCodecInfo findEncoderForFormat (ImageFormat format)
376 ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
377 ImageCodecInfo encoder = null;
379 if (format.Guid.Equals (ImageFormat.MemoryBmp.Guid))
380 format = ImageFormat.Png;
382 /* Look for the right encoder for our format*/
383 for (int i = 0; i < encoders.Length; i++) {
384 if (encoders[i].FormatID.Equals (format.Guid)) {
385 encoder = encoders[i];
393 public void Save (string filename)
395 Save (filename, RawFormat);
398 public void Save(string filename, ImageFormat format)
400 ImageCodecInfo encoder = findEncoderForFormat (format);
403 throw new ArgumentException ("No codec available for format:" + format.Guid);
405 Save (filename, encoder, null);
408 public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
411 Guid guid = encoder.Clsid;
413 if (encoderParams == null) {
414 st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, IntPtr.Zero);
416 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
417 st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, nativeEncoderParams);
418 Marshal.FreeHGlobal (nativeEncoderParams);
421 GDIPlus.CheckStatus (st);
424 public void Save (Stream stream, ImageFormat format)
426 ImageCodecInfo encoder = findEncoderForFormat (format);
429 throw new ArgumentException ("No codec available for format:" + format.Guid);
431 Save (stream, encoder, null);
434 public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
437 IntPtr nativeEncoderParams;
438 Guid guid = encoder.Clsid;
440 if (encoderParams == null)
441 nativeEncoderParams = IntPtr.Zero;
443 nativeEncoderParams = encoderParams.ToNativePtr ();
446 // check for Unix platforms - see FAQ for more details
447 // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
448 int platform = (int) Environment.OSVersion.Platform;
449 if ((platform == 4) || (platform == 128)) {
450 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream);
451 st = GDIPlus.GdipSaveImageToDelegate_linux (nativeObject, sh.GetBytesDelegate, sh.PutBytesDelegate,
452 sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, nativeEncoderParams);
455 st = GDIPlus.GdipSaveImageToStream(new HandleRef(this, nativeObject), new ComIStreamWrapper(stream), ref guid, new HandleRef(encoderParams, nativeEncoderParams));
458 if (nativeEncoderParams != IntPtr.Zero)
459 Marshal.FreeHGlobal (nativeEncoderParams);
462 GDIPlus.CheckStatus (st);
465 public void SaveAdd (EncoderParameters encoderParams)
469 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
470 st = GDIPlus.GdipSaveAdd (nativeObject, nativeEncoderParams);
471 Marshal.FreeHGlobal (nativeEncoderParams);
472 GDIPlus.CheckStatus (st);
475 public void SaveAdd (Image image, EncoderParameters encoderParams)
479 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
480 st = GDIPlus.GdipSaveAddImage (nativeObject, image.NativeObject, nativeEncoderParams);
481 Marshal.FreeHGlobal (nativeEncoderParams);
482 GDIPlus.CheckStatus (st);
485 public int SelectActiveFrame(FrameDimension dimension, int frameIndex)
487 Guid guid = dimension.Guid;
488 Status st = GDIPlus.GdipImageSelectActiveFrame (nativeObject, ref guid, frameIndex);
490 GDIPlus.CheckStatus (st);
495 public void SetPropertyItem(PropertyItem propitem)
498 int size = Marshal.SizeOf (typeof(GdipPropertyItem));
499 property = Marshal.AllocHGlobal (size);
501 Marshal.StructureToPtr (propitem, property, true);
502 Status status = GDIPlus.GdipSetPropertyItem (nativeObject, property);
503 GDIPlus.CheckStatus (status);
504 // FIXME: GdipSetPropertyItem isn't implemented in libgdiplus (but returns Ok)
505 // so who's freeing "property" ? GDI+ ?
514 Status status = GDIPlus.GdipGetImageFlags (nativeObject, out flags);
515 GDIPlus.CheckStatus (status);
521 public Guid[] FrameDimensionsList {
524 Status status = GDIPlus.GdipImageGetFrameDimensionsCount (nativeObject, out found);
525 GDIPlus.CheckStatus (status);
526 Guid [] guid = new Guid [found];
527 status = GDIPlus.GdipImageGetFrameDimensionsList (nativeObject, guid, found);
528 GDIPlus.CheckStatus (status);
533 [DefaultValue (false)]
535 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
539 Status status = GDIPlus.GdipGetImageHeight (nativeObject, out height);
540 GDIPlus.CheckStatus (status);
546 public float HorizontalResolution {
550 Status status = GDIPlus.GdipGetImageHorizontalResolution (nativeObject, out resolution);
551 GDIPlus.CheckStatus (status);
558 public ColorPalette Palette {
560 return retrieveGDIPalette();
563 storeGDIPalette(value);
567 internal ColorPalette retrieveGDIPalette()
569 ColorPalette ret = new ColorPalette();
570 if (!IsIndexedPixelFormat (PixelFormat)) {
576 st = GDIPlus.GdipGetImagePaletteSize (nativeObject, out bytes);
577 GDIPlus.CheckStatus (st);
578 IntPtr palette_data = Marshal.AllocHGlobal (bytes);
580 st = GDIPlus.GdipGetImagePalette (nativeObject, palette_data, bytes);
581 GDIPlus.CheckStatus (st);
582 ret.setFromGDIPalette (palette_data);
587 Marshal.FreeHGlobal (palette_data);
591 internal void storeGDIPalette(ColorPalette palette)
593 if (palette == null) {
594 throw new ArgumentNullException("palette");
596 IntPtr palette_data = palette.getGDIPalette();
597 if (palette_data == IntPtr.Zero) {
602 Status st = GDIPlus.GdipSetImagePalette (nativeObject, palette_data);
603 GDIPlus.CheckStatus (st);
607 Marshal.FreeHGlobal(palette_data);
612 public SizeF PhysicalDimension {
615 Status status = GDIPlus.GdipGetImageDimension (nativeObject, out width, out height);
616 GDIPlus.CheckStatus (status);
618 return new SizeF (width, height);
622 public PixelFormat PixelFormat {
624 PixelFormat pixFormat;
625 Status status = GDIPlus.GdipGetImagePixelFormat (nativeObject, out pixFormat);
626 GDIPlus.CheckStatus (status);
633 public int[] PropertyIdList {
637 Status status = GDIPlus.GdipGetPropertyCount (nativeObject,
639 GDIPlus.CheckStatus (status);
641 int [] idList = new int [propNumbers];
642 status = GDIPlus.GdipGetPropertyIdList (nativeObject,
643 propNumbers, idList);
644 GDIPlus.CheckStatus (status);
651 public PropertyItem[] PropertyItems {
653 int propNums, propsSize, propSize;
654 IntPtr properties, propPtr;
655 PropertyItem[] items;
656 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
659 status = GDIPlus.GdipGetPropertySize (nativeObject, out propsSize, out propNums);
660 GDIPlus.CheckStatus (status);
662 items = new PropertyItem [propNums];
667 /* Get PropertyItem list*/
668 properties = Marshal.AllocHGlobal (propsSize * propNums);
670 status = GDIPlus.GdipGetAllPropertyItems (nativeObject, propsSize,
671 propNums, properties);
672 GDIPlus.CheckStatus (status);
674 propSize = Marshal.SizeOf (gdipProperty);
675 propPtr = properties;
677 for (int i = 0; i < propNums; i++, propPtr = new IntPtr (propPtr.ToInt64 () + propSize)) {
678 gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure
679 (propPtr, typeof (GdipPropertyItem));
680 items [i] = new PropertyItem ();
681 GdipPropertyItem.MarshalTo (gdipProperty, items [i]);
685 Marshal.FreeHGlobal (properties);
691 public ImageFormat RawFormat {
694 Status st = GDIPlus.GdipGetImageRawFormat (nativeObject, out guid);
696 GDIPlus.CheckStatus (st);
697 return new ImageFormat (guid);
703 return new Size(Width, Height);
708 [DefaultValue (null)]
709 [LocalizableAttribute(false)]
710 [BindableAttribute(true)]
711 [TypeConverter (typeof (StringConverter))]
717 public float VerticalResolution {
721 Status status = GDIPlus.GdipGetImageVerticalResolution (nativeObject, out resolution);
722 GDIPlus.CheckStatus (status);
728 [DefaultValue (false)]
730 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
734 Status status = GDIPlus.GdipGetImageWidth (nativeObject, out width);
735 GDIPlus.CheckStatus (status);
741 internal IntPtr NativeObject{
746 nativeObject = value;
750 public void Dispose ()
753 System.GC.SuppressFinalize (this);
761 private void DisposeResources ()
763 Status status = GDIPlus.GdipDisposeImage (nativeObject);
764 GDIPlus.CheckStatus (status);
767 protected virtual void Dispose (bool disposing)
769 if (nativeObject != IntPtr.Zero){
771 nativeObject = IntPtr.Zero;
775 public object Clone()
778 IntPtr newimage = IntPtr.Zero;
780 if (!(this is Bitmap))
781 throw new NotImplementedException ();
783 Status status = GDIPlus.GdipCloneImage (NativeObject, out newimage);
784 GDIPlus.CheckStatus (status);
787 return new Bitmap (newimage);
790 throw new NotImplementedException ();