1 // OpenTween - Client of Twitter
2 // Copyright (c) 2013 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
3 // All rights reserved.
5 // This file is part of OpenTween.
7 // This program is free software; you can redistribute it and/or modify it
8 // under the terms of the GNU General Public License as published by the Free
9 // Software Foundation; either version 3 of the License, or (at your option)
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 // You should have received a copy of the GNU General Public License along
18 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
19 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20 // Boston, MA 02110-1301, USA.
23 using System.Collections.Generic;
25 using System.Runtime.InteropServices;
26 using System.Runtime.Serialization;
28 using System.Diagnostics.CodeAnalysis;
31 using System.Threading.Tasks;
32 using System.Drawing.Imaging;
37 /// Image と Stream を対に保持するためのクラス
40 /// Image.FromStream() を使用して Image を生成する場合、
41 /// Image を破棄するまでの間は元となった Stream を破棄できないためその対策として使用する。
43 public class MemoryImage : ICloneable, IDisposable, IEquatable<MemoryImage>
45 private readonly Image image;
47 /// <exception cref="InvalidImageException">
48 /// ストリームから読みだされる画像データが不正な場合にスローされる
50 protected MemoryImage(MemoryStream stream)
54 this.image = Image.FromStream(stream);
56 catch (ArgumentException e)
59 throw new InvalidImageException("Invalid image", e);
61 catch (OutOfMemoryException e)
63 // GDI+ がサポートしない画像形式で OutOfMemoryException がスローされる場合があるらしい
65 throw new InvalidImageException("Invalid image?", e);
67 catch (ExternalException e)
69 // 「GDI+ で汎用エラーが発生しました」という大雑把な例外がスローされる場合があるらしい
71 throw new InvalidImageException("Invalid image?", e);
83 /// MemoryImage が保持している画像
90 throw new ObjectDisposedException("this");
97 /// MemoryImage が保持している画像のストリーム
99 public MemoryStream Stream { get; }
102 /// MemoryImage が破棄されているか否か
104 public bool IsDisposed { get; private set; } = false;
107 /// MemoryImage が保持している画像のフォーマット
109 public ImageFormat ImageFormat
110 => this.Image.RawFormat;
113 /// MemoryImage が保持している画像のフォーマットに相当する拡張子 (ピリオド付き)
115 public string ImageFormatExt
119 var format = this.ImageFormat;
121 // ImageFormat は == で正しく比較できないため Equals を使用する必要がある
122 if (format.Equals(ImageFormat.Bmp))
124 if (format.Equals(ImageFormat.Emf))
126 if (format.Equals(ImageFormat.Gif))
128 if (format.Equals(ImageFormat.Icon))
130 if (format.Equals(ImageFormat.Jpeg))
132 if (format.Equals(ImageFormat.MemoryBmp))
134 if (format.Equals(ImageFormat.Png))
136 if (format.Equals(ImageFormat.Tiff))
138 if (format.Equals(ImageFormat.Wmf))
141 // 対応する形式がなければ空文字列を返す
142 // (上記以外のフォーマットは Image.FromStream を通過できないため、ここが実行されることはまず無い)
148 /// MemoryImage インスタンスを複製します
151 /// メソッド実行中にストリームのシークが行われないよう注意して下さい。
152 /// 特に PictureBox で Gif アニメーションを表示している場合は Enabled に false をセットするなどして更新を止めて下さい。
154 /// <returns>複製された MemoryImage</returns>
155 public MemoryImage Clone()
157 this.Stream.Seek(0, SeekOrigin.Begin);
159 return MemoryImage.CopyFromStream(this.Stream);
163 /// MemoryImage インスタンスを非同期に複製します
166 /// メソッド実行中にストリームのシークが行われないよう注意して下さい。
167 /// 特に PictureBox で Gif アニメーションを表示している場合は Enabled に false をセットするなどして更新を止めて下さい。
169 /// <returns>複製された MemoryImage を返すタスク</returns>
170 public Task<MemoryImage> CloneAsync()
172 this.Stream.Seek(0, SeekOrigin.Begin);
174 return MemoryImage.CopyFromStreamAsync(this.Stream);
177 public override int GetHashCode()
179 using (var sha1service = new System.Security.Cryptography.SHA1CryptoServiceProvider())
181 var hash = sha1service.ComputeHash(this.Stream.GetBuffer(), 0, (int)this.Stream.Length);
182 return Convert.ToBase64String(hash).GetHashCode();
186 public override bool Equals(object other)
187 => this.Equals(other as MemoryImage);
189 public bool Equals(MemoryImage other)
191 if (object.ReferenceEquals(this, other))
197 // それぞれが保持する MemoryStream の内容が等しいことを検証する
199 var selfLength = this.Stream.Length;
200 var otherLength = other.Stream.Length;
202 if (selfLength != otherLength)
205 var selfBuffer = this.Stream.GetBuffer();
206 var otherBuffer = other.Stream.GetBuffer();
208 for (var pos = 0L; pos < selfLength; pos++)
210 if (selfBuffer[pos] != otherBuffer[pos])
217 object ICloneable.Clone()
220 protected virtual void Dispose(bool disposing)
222 if (this.IsDisposed) return;
226 this.Image.Dispose();
227 this.Stream.Dispose();
230 this.IsDisposed = true;
233 public void Dispose()
237 // 明示的にDisposeが呼ばれた場合はファイナライザを使用しない
238 GC.SuppressFinalize(this);
242 => this.Dispose(false);
245 /// 指定された Stream から MemoryImage を作成します。
248 /// ストリームの内容はメモリ上に展開した後に使用されるため、
249 /// 引数に指定した Stream を MemoryImage より先に破棄しても問題ありません。
251 /// <param name="stream">読み込む対象となる Stream</param>
252 /// <returns>作成された MemoryImage</returns>
253 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
254 public static MemoryImage CopyFromStream(Stream stream)
256 MemoryStream memstream = null;
259 memstream = new MemoryStream();
261 stream.CopyTo(memstream);
263 return new MemoryImage(memstream);
267 memstream?.Dispose();
273 /// 指定された Stream から MemoryImage を非同期に作成します。
276 /// ストリームの内容はメモリ上に展開した後に使用されるため、
277 /// 引数に指定した Stream を MemoryImage より先に破棄しても問題ありません。
279 /// <param name="stream">読み込む対象となる Stream</param>
280 /// <returns>作成された MemoryImage を返すタスク</returns>
281 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
282 public async static Task<MemoryImage> CopyFromStreamAsync(Stream stream)
284 MemoryStream memstream = null;
287 memstream = new MemoryStream();
289 await stream.CopyToAsync(memstream).ConfigureAwait(false);
291 return new MemoryImage(memstream);
295 memstream?.Dispose();
301 /// 指定されたバイト列から MemoryImage を作成します。
303 /// <param name="bytes">読み込む対象となるバイト列</param>
304 /// <returns>作成された MemoryImage</returns>
305 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
306 public static MemoryImage CopyFromBytes(byte[] bytes)
308 MemoryStream memstream = null;
311 memstream = new MemoryStream(bytes);
312 return new MemoryImage(memstream);
316 memstream?.Dispose();
322 /// Image インスタンスから MemoryImage を作成します
325 /// PNG 画像として描画し直す処理を含むため、極力 Stream や byte[] を渡す他のメソッドを使用すべきです
327 /// <param name="image">対象となる画像</param>
328 /// <returns>作成された MemoryImage</returns>
329 public static MemoryImage CopyFromImage(Image image)
331 MemoryStream memstream = null;
334 memstream = new MemoryStream();
336 image.Save(memstream, ImageFormat.Png);
338 return new MemoryImage(memstream);
342 memstream?.Dispose();
349 /// 不正な画像データに対してスローされる例外
352 public class InvalidImageException : Exception
354 public InvalidImageException() { }
355 public InvalidImageException(string message) : base(message) { }
356 public InvalidImageException(string message, Exception innerException) : base(message, innerException) { }
357 protected InvalidImageException(SerializationInfo info, StreamingContext context) : base(info, context) { }