// OpenTween - Client of Twitter
// Copyright (c) 2015 spx (@5px)
// All rights reserved.
//
// This file is part of OpenTween.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see , or write to
// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
// Boston, MA 02110-1301, USA.
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
namespace OpenTween
{
public interface IMediaItem
{
///
/// メディアへの絶対パス
///
string Path { get; }
///
/// メディア名
///
string Name { get; }
///
/// メディアの拡張子
///
string Extension { get; }
///
/// メディアが存在するかどうかを示す真偽値
///
bool Exists { get; }
///
/// メディアのサイズ(バイト単位)
///
long Size { get; }
///
/// メディアが画像であるかどうかを示す真偽値
///
bool IsImage { get; }
///
/// 代替テキスト (アップロード先が対応している必要がある)
///
string AltText { get; set; }
///
/// 表示用の MemoryImage を作成する
///
///
/// 呼び出し側にて破棄すること
///
MemoryImage CreateImage();
///
/// メディアの内容を読み込むための Stream を開く
///
///
/// 呼び出し側にて閉じること
///
Stream OpenRead();
///
/// メディアの内容を Stream へ書き込む
///
void CopyTo(Stream stream);
}
///
/// ファイル用の MediaItem クラス
///
public class FileMediaItem : IMediaItem
{
public FileInfo FileInfo { get; }
public string AltText { get; set; }
public FileMediaItem(string path)
=> this.FileInfo = new FileInfo(path);
public FileMediaItem(FileInfo fileInfo)
: this(fileInfo.FullName)
{
}
public string Path
=> this.FileInfo.FullName;
public string Name
=> this.FileInfo.Name;
public string Extension
=> this.FileInfo.Extension;
public bool Exists
=> this.FileInfo.Exists;
public long Size
=> this.FileInfo.Length;
public bool IsImage
{
get
{
if (this.isImage == null)
{
try
{
// MemoryImage が生成できるかを検証する
using (var image = this.CreateImage()) { }
this.isImage = true;
}
catch (InvalidImageException)
{
this.isImage = false;
}
}
return this.isImage.Value;
}
}
/// IsImage の検証結果をキャッシュする。未検証なら null
private bool? isImage = null;
public MemoryImage CreateImage()
{
using (var fs = this.FileInfo.OpenRead())
{
return MemoryImage.CopyFromStream(fs);
}
}
public Stream OpenRead()
=> this.FileInfo.OpenRead();
public void CopyTo(Stream stream)
{
using (var fs = this.FileInfo.OpenRead())
{
fs.CopyTo(stream);
}
}
}
///
/// MemoryImage 用の MediaItem クラス
///
///
/// 用途の関係上、メモリ使用量が大きくなるため、不要になればできるだけ破棄すること
///
public class MemoryImageMediaItem : IMediaItem, IDisposable
{
public const string PathPrefix = "<>MemoryImage://";
private static int _fileNumber = 0;
private readonly MemoryImage _image;
public bool IsDisposed { get; private set; } = false;
public MemoryImageMediaItem(MemoryImage image)
{
this._image = image ?? throw new ArgumentNullException(nameof(image));
var num = Interlocked.Increment(ref _fileNumber);
this.Path = PathPrefix + num + this._image.ImageFormatExt;
}
public string Path { get; }
public string AltText { get; set; }
public string Name
=> this.Path.Substring(PathPrefix.Length);
public string Extension
=> this._image.ImageFormatExt;
public bool Exists
=> this._image != null;
public long Size
=> this._image.Stream.Length;
public bool IsImage
=> true;
public MemoryImage CreateImage()
=> this._image.Clone();
public Stream OpenRead()
{
MemoryStream memstream = null;
try
{
// コピーを作成する
memstream = new MemoryStream();
this._image.Stream.WriteTo(memstream);
memstream.Seek(0, SeekOrigin.Begin);
return memstream;
}
catch
{
memstream?.Dispose();
throw;
}
}
public void CopyTo(Stream stream)
=> this._image.Stream.WriteTo(stream);
protected virtual void Dispose(bool disposing)
{
if (this.IsDisposed) return;
if (disposing)
{
this._image.Dispose();
}
this.IsDisposed = true;
}
public void Dispose()
{
this.Dispose(true);
// 明示的にDisposeが呼ばれた場合はファイナライザを使用しない
GC.SuppressFinalize(this);
}
~MemoryImageMediaItem()
=> this.Dispose(false);
}
}