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 MemoryStream stream;
46 private readonly Image image;
48 protected bool disposed = false;
50 /// <exception cref="InvalidImageException">
51 /// ストリームから読みだされる画像データが不正な場合にスローされる
53 protected MemoryImage(MemoryStream stream)
57 this.image = Image.FromStream(stream);
59 catch (ArgumentException e)
62 throw new InvalidImageException("Invalid image", e);
64 catch (OutOfMemoryException e)
66 // GDI+ がサポートしない画像形式で OutOfMemoryException がスローされる場合があるらしい
68 throw new InvalidImageException("Invalid image?", e);
70 catch (ExternalException e)
72 // 「GDI+ で汎用エラーが発生しました」という大雑把な例外がスローされる場合があるらしい
74 throw new InvalidImageException("Invalid image?", e);
86 /// MemoryImage が保持している画像
93 throw new ObjectDisposedException("this");
100 /// MemoryImage が保持している画像のストリーム
102 public MemoryStream Stream
104 // MemoryStream は破棄されていても一部のメソッドが使用可能なためここでは例外を投げない
105 get { return this.stream; }
109 /// MemoryImage が破棄されているか否か
111 public bool IsDisposed
113 get { return this.disposed; }
117 /// MemoryImage が保持している画像のフォーマット
119 public ImageFormat ImageFormat
121 get { return this.Image.RawFormat; }
125 /// MemoryImage が保持している画像のフォーマットに相当する拡張子 (ピリオド付き)
127 public string ImageFormatExt
131 var format = this.ImageFormat;
133 // ImageFormat は == で正しく比較できないため Equals を使用する必要がある
134 if (format.Equals(ImageFormat.Bmp))
136 if (format.Equals(ImageFormat.Emf))
138 if (format.Equals(ImageFormat.Gif))
140 if (format.Equals(ImageFormat.Icon))
142 if (format.Equals(ImageFormat.Jpeg))
144 if (format.Equals(ImageFormat.MemoryBmp))
146 if (format.Equals(ImageFormat.Png))
148 if (format.Equals(ImageFormat.Tiff))
150 if (format.Equals(ImageFormat.Wmf))
153 // 対応する形式がなければ空文字列を返す
154 // (上記以外のフォーマットは Image.FromStream を通過できないため、ここが実行されることはまず無い)
160 /// MemoryImage インスタンスを複製します
163 /// メソッド実行中にストリームのシークが行われないよう注意して下さい。
164 /// 特に PictureBox で Gif アニメーションを表示している場合は Enabled に false をセットするなどして更新を止めて下さい。
166 /// <returns>複製された MemoryImage</returns>
167 public MemoryImage Clone()
169 this.Stream.Seek(0, SeekOrigin.Begin);
171 return MemoryImage.CopyFromStream(this.Stream);
175 /// MemoryImage インスタンスを非同期に複製します
178 /// メソッド実行中にストリームのシークが行われないよう注意して下さい。
179 /// 特に PictureBox で Gif アニメーションを表示している場合は Enabled に false をセットするなどして更新を止めて下さい。
181 /// <returns>複製された MemoryImage を返すタスク</returns>
182 public Task<MemoryImage> CloneAsync()
184 this.Stream.Seek(0, SeekOrigin.Begin);
186 return MemoryImage.CopyFromStreamAsync(this.Stream);
189 public override int GetHashCode()
191 using (var sha1service = new System.Security.Cryptography.SHA1CryptoServiceProvider())
193 var hash = sha1service.ComputeHash(this.Stream.GetBuffer(), 0, (int)this.Stream.Length);
194 return Convert.ToBase64String(hash).GetHashCode();
198 public override bool Equals(object other)
200 return this.Equals(other as MemoryImage);
203 public bool Equals(MemoryImage other)
205 if (object.ReferenceEquals(this, other))
211 // それぞれが保持する MemoryStream の内容が等しいことを検証する
213 var selfLength = this.Stream.Length;
214 var otherLength = other.Stream.Length;
216 if (selfLength != otherLength)
219 var selfBuffer = this.Stream.GetBuffer();
220 var otherBuffer = other.Stream.GetBuffer();
222 for (var pos = 0L; pos < selfLength; pos++)
224 if (selfBuffer[pos] != otherBuffer[pos])
231 object ICloneable.Clone()
236 protected virtual void Dispose(bool disposing)
238 if (this.disposed) return;
242 this.Image.Dispose();
243 this.Stream.Dispose();
246 this.disposed = true;
249 public void Dispose()
253 // 明示的にDisposeが呼ばれた場合はファイナライザを使用しない
254 GC.SuppressFinalize(this);
258 => this.Dispose(false);
261 /// 指定された Stream から MemoryImage を作成します。
264 /// ストリームの内容はメモリ上に展開した後に使用されるため、
265 /// 引数に指定した Stream を MemoryImage より先に破棄しても問題ありません。
267 /// <param name="stream">読み込む対象となる Stream</param>
268 /// <returns>作成された MemoryImage</returns>
269 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
270 public static MemoryImage CopyFromStream(Stream stream)
272 MemoryStream memstream = null;
275 memstream = new MemoryStream();
277 stream.CopyTo(memstream);
279 return new MemoryImage(memstream);
283 memstream?.Dispose();
289 /// 指定された Stream から MemoryImage を非同期に作成します。
292 /// ストリームの内容はメモリ上に展開した後に使用されるため、
293 /// 引数に指定した Stream を MemoryImage より先に破棄しても問題ありません。
295 /// <param name="stream">読み込む対象となる Stream</param>
296 /// <returns>作成された MemoryImage を返すタスク</returns>
297 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
298 public async static Task<MemoryImage> CopyFromStreamAsync(Stream stream)
300 MemoryStream memstream = null;
303 memstream = new MemoryStream();
305 await stream.CopyToAsync(memstream).ConfigureAwait(false);
307 return new MemoryImage(memstream);
311 memstream?.Dispose();
317 /// 指定されたバイト列から MemoryImage を作成します。
319 /// <param name="bytes">読み込む対象となるバイト列</param>
320 /// <returns>作成された MemoryImage</returns>
321 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
322 public static MemoryImage CopyFromBytes(byte[] bytes)
324 MemoryStream memstream = null;
327 memstream = new MemoryStream(bytes);
328 return new MemoryImage(memstream);
332 memstream?.Dispose();
338 /// Image インスタンスから MemoryImage を作成します
341 /// PNG 画像として描画し直す処理を含むため、極力 Stream や byte[] を渡す他のメソッドを使用すべきです
343 /// <param name="image">対象となる画像</param>
344 /// <returns>作成された MemoryImage</returns>
345 public static MemoryImage CopyFromImage(Image image)
347 MemoryStream memstream = null;
350 memstream = new MemoryStream();
352 image.Save(memstream, ImageFormat.Png);
354 return new MemoryImage(memstream);
358 memstream?.Dispose();
365 /// 不正な画像データに対してスローされる例外
368 public class InvalidImageException : Exception
370 public InvalidImageException() { }
371 public InvalidImageException(string message) : base(message) { }
372 public InvalidImageException(string message, Exception innerException) : base(message, innerException) { }
373 protected InvalidImageException(SerializationInfo info, StreamingContext context) : base(info, context) { }