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();
180 var hash = sha1service.ComputeHash(this.Stream.GetBuffer(), 0, (int)this.Stream.Length);
181 return Convert.ToBase64String(hash).GetHashCode();
184 public override bool Equals(object other)
185 => this.Equals(other as MemoryImage);
187 public bool Equals(MemoryImage other)
189 if (object.ReferenceEquals(this, other))
195 // それぞれが保持する MemoryStream の内容が等しいことを検証する
197 var selfLength = this.Stream.Length;
198 var otherLength = other.Stream.Length;
200 if (selfLength != otherLength)
203 var selfBuffer = this.Stream.GetBuffer();
204 var otherBuffer = other.Stream.GetBuffer();
206 for (var pos = 0L; pos < selfLength; pos++)
208 if (selfBuffer[pos] != otherBuffer[pos])
215 object ICloneable.Clone()
218 protected virtual void Dispose(bool disposing)
220 if (this.IsDisposed) return;
224 this.Image.Dispose();
225 this.Stream.Dispose();
228 this.IsDisposed = true;
231 public void Dispose()
235 // 明示的にDisposeが呼ばれた場合はファイナライザを使用しない
236 GC.SuppressFinalize(this);
240 => this.Dispose(false);
243 /// 指定された Stream から MemoryImage を作成します。
246 /// ストリームの内容はメモリ上に展開した後に使用されるため、
247 /// 引数に指定した Stream を MemoryImage より先に破棄しても問題ありません。
249 /// <param name="stream">読み込む対象となる Stream</param>
250 /// <returns>作成された MemoryImage</returns>
251 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
252 public static MemoryImage CopyFromStream(Stream stream)
254 MemoryStream memstream = null;
257 memstream = new MemoryStream();
259 stream.CopyTo(memstream);
261 return new MemoryImage(memstream);
265 memstream?.Dispose();
271 /// 指定された Stream から MemoryImage を非同期に作成します。
274 /// ストリームの内容はメモリ上に展開した後に使用されるため、
275 /// 引数に指定した Stream を MemoryImage より先に破棄しても問題ありません。
277 /// <param name="stream">読み込む対象となる Stream</param>
278 /// <returns>作成された MemoryImage を返すタスク</returns>
279 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
280 public async static Task<MemoryImage> CopyFromStreamAsync(Stream stream)
282 MemoryStream memstream = null;
285 memstream = new MemoryStream();
287 await stream.CopyToAsync(memstream).ConfigureAwait(false);
289 return new MemoryImage(memstream);
293 memstream?.Dispose();
299 /// 指定されたバイト列から MemoryImage を作成します。
301 /// <param name="bytes">読み込む対象となるバイト列</param>
302 /// <returns>作成された MemoryImage</returns>
303 /// <exception cref="InvalidImageException">不正な画像データが入力された場合</exception>
304 public static MemoryImage CopyFromBytes(byte[] bytes)
306 MemoryStream memstream = null;
309 memstream = new MemoryStream(bytes);
310 return new MemoryImage(memstream);
314 memstream?.Dispose();
320 /// Image インスタンスから MemoryImage を作成します
323 /// PNG 画像として描画し直す処理を含むため、極力 Stream や byte[] を渡す他のメソッドを使用すべきです
325 /// <param name="image">対象となる画像</param>
326 /// <returns>作成された MemoryImage</returns>
327 public static MemoryImage CopyFromImage(Image image)
329 MemoryStream memstream = null;
332 memstream = new MemoryStream();
334 image.Save(memstream, ImageFormat.Png);
336 return new MemoryImage(memstream);
340 memstream?.Dispose();
347 /// 不正な画像データに対してスローされる例外
350 public class InvalidImageException : Exception
352 public InvalidImageException() { }
353 public InvalidImageException(string message) : base(message) { }
354 public InvalidImageException(string message, Exception innerException) : base(message, innerException) { }
355 protected InvalidImageException(SerializationInfo info, StreamingContext context) : base(info, context) { }