OSDN Git Service

自動プロパティを使用する (IDE0032)
[opentween/open-tween.git] / OpenTween / MediaItem.cs
1 // OpenTween - Client of Twitter
2 // Copyright (c) 2015 spx (@5px)
3 // All rights reserved.
4 // 
5 // This file is part of OpenTween.
6 // 
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)
10 // any later version.
11 // 
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
15 // for more details. 
16 // 
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.
21
22 using System;
23 using System.Drawing;
24 using System.IO;
25 using System.Linq;
26 using System.Threading;
27
28 namespace OpenTween
29 {
30     public interface IMediaItem
31     {
32         /// <summary>
33         /// メディアへの絶対パス
34         /// </summary>
35         string Path { get; }
36
37         /// <summary>
38         /// メディア名
39         /// </summary>
40         string Name { get; }
41
42         /// <summary>
43         /// メディアの拡張子
44         /// </summary>
45         string Extension { get; }
46
47         /// <summary>
48         /// メディアが存在するかどうかを示す真偽値
49         /// </summary>
50         bool Exists { get; }
51
52         /// <summary>
53         /// メディアのサイズ(バイト単位)
54         /// </summary>
55         long Size { get; }
56
57         /// <summary>
58         /// メディアが画像であるかどうかを示す真偽値
59         /// </summary>
60         bool IsImage { get; }
61
62         /// <summary>
63         /// 代替テキスト (アップロード先が対応している必要がある)
64         /// </summary>
65         string AltText { get; set; }
66
67         /// <summary>
68         /// 表示用の MemoryImage を作成する
69         /// </summary>
70         /// <remarks>
71         /// 呼び出し側にて破棄すること
72         /// </remarks>
73         MemoryImage CreateImage();
74
75         /// <summary>
76         /// メディアの内容を読み込むための Stream を開く
77         /// </summary>
78         /// <remarks>
79         /// 呼び出し側にて閉じること
80         /// </remarks>
81         Stream OpenRead();
82
83         /// <summary>
84         /// メディアの内容を Stream へ書き込む
85         /// </summary>
86         void CopyTo(Stream stream);
87     }
88
89     /// <summary>
90     /// ファイル用の MediaItem クラス
91     /// </summary>
92     public class FileMediaItem : IMediaItem
93     {
94         public FileInfo FileInfo { get; }
95         public string AltText { get; set; }
96
97         public FileMediaItem(string path)
98         {
99             this.FileInfo = new FileInfo(path);
100         }
101
102         public FileMediaItem(FileInfo fileInfo)
103             : this(fileInfo.FullName)
104         {
105         }
106
107         public string Path
108         {
109             get { return this.FileInfo.FullName; }
110         }
111
112         public string Name
113         {
114             get { return this.FileInfo.Name; }
115         }
116
117         public string Extension
118         {
119             get { return this.FileInfo.Extension; }
120         }
121
122         public bool Exists
123         {
124             get { return this.FileInfo.Exists; }
125         }
126
127         public long Size
128         {
129             get { return this.FileInfo.Length; }
130         }
131
132         public bool IsImage
133         {
134             get
135             {
136                 if (this.isImage == null)
137                 {
138                     try
139                     {
140                         // MemoryImage が生成できるかを検証する
141                         using (var image = this.CreateImage()) { }
142
143                         this.isImage = true;
144                     }
145                     catch (InvalidImageException)
146                     {
147                         this.isImage = false;
148                     }
149                 }
150
151                 return this.isImage.Value;
152             }
153         }
154
155         /// <summary>IsImage の検証結果をキャッシュする。未検証なら null</summary>
156         private bool? isImage = null;
157
158         public MemoryImage CreateImage()
159         {
160             using (var fs = this.FileInfo.OpenRead())
161             {
162                 return MemoryImage.CopyFromStream(fs);
163             }
164         }
165
166         public Stream OpenRead()
167         {
168             return this.FileInfo.OpenRead();
169         }
170
171         public void CopyTo(Stream stream)
172         {
173             using (var fs = this.FileInfo.OpenRead())
174             {
175                 fs.CopyTo(stream);
176             }
177         }
178     }
179
180     /// <summary>
181     /// MemoryImage 用の MediaItem クラス
182     /// </summary>
183     /// <remarks>
184     /// 用途の関係上、メモリ使用量が大きくなるため、不要になればできるだけ破棄すること
185     /// </remarks>
186     public class MemoryImageMediaItem : IMediaItem, IDisposable
187     {
188         public const string PathPrefix = "<>MemoryImage://";
189         private static int _fileNumber = 0;
190         private readonly MemoryImage _image;
191
192         public bool IsDisposed { get; private set; } = false;
193
194         public MemoryImageMediaItem(MemoryImage image)
195         {
196             this._image = image ?? throw new ArgumentNullException(nameof(image));
197
198             var num = Interlocked.Increment(ref _fileNumber);
199             this.Path = PathPrefix + num + this._image.ImageFormatExt;
200         }
201
202         public string Path { get; }
203         public string AltText { get; set; }
204
205         public string Name
206         {
207             get { return this.Path.Substring(PathPrefix.Length); }
208         }
209
210         public string Extension
211         {
212             get { return this._image.ImageFormatExt; }
213         }
214
215         public bool Exists
216         {
217             get { return this._image != null; }
218         }
219
220         public long Size
221         {
222             get { return this._image.Stream.Length; }
223         }
224
225         public bool IsImage
226         {
227             get { return true; }
228         }
229
230         public MemoryImage CreateImage()
231         {
232             return this._image.Clone();
233         }
234
235         public Stream OpenRead()
236         {
237             MemoryStream memstream = null;
238             try
239             {
240                 // コピーを作成する
241                 memstream = new MemoryStream();
242
243                 this._image.Stream.WriteTo(memstream);
244                 memstream.Seek(0, SeekOrigin.Begin);
245
246                 return memstream;
247             }
248             catch
249             {
250                 memstream?.Dispose();
251                 throw;
252             }
253         }
254
255         public void CopyTo(Stream stream)
256         {
257             this._image.Stream.WriteTo(stream);
258         }
259
260         protected virtual void Dispose(bool disposing)
261         {
262             if (this.IsDisposed) return;
263
264             if (disposing)
265             {
266                 this._image.Dispose();
267             }
268
269             this.IsDisposed = true;
270         }
271
272         public void Dispose()
273         {
274             this.Dispose(true);
275
276             // 明示的にDisposeが呼ばれた場合はファイナライザを使用しない
277             GC.SuppressFinalize(this);
278         }
279
280         ~MemoryImageMediaItem()
281             => this.Dispose(false);
282     }
283 }