/*
	© 2013-2017, 2019-2020 FrankHB.

	This file is part of the YSLib project, and may only be used,
	modified, and distributed under the terms of the YSLib project
	license, LICENSE.TXT.  By continuing to use, modify, or distribute
	this file you indicate that you have read the license and
	understand and accept it fully.
*/

/*!	\file Image.h
\ingroup Adaptor
\brief 平台中立的图像输入和输出。
\version r1526
\author FrankHB <frankhb1989@gmail.com>
\since build 402
\par 创建时间:
	2013-05-05 12:34:03 +0800
\par 修改时间:
	2020-01-25 23:02 +0800
\par 文本编码:
	UTF-8
\par 模块名称:
	YSLib::Adaptor::Image
*/


#ifndef YSL_INC_Adaptor_Image_h_
#define YSL_INC_Adaptor_Image_h_ 1

#include "../Core/YModules.h"
#include YFM_YSLib_Core_YGraphics // for ystdex::unsupported,
//	ystdex::copy_and_swap;
#include YFM_YSLib_Core_YClock // for TimeSpan;
//#include <FreeImage.h>
#include <ystdex/type_op.hpp> // for ystdex::exclude_self_params_t;

//! \since build 402
struct FIBITMAP;
//! \since build 556
struct FIMETADATA;
//! \since build 556
struct FITAG;
//! \since build 417
struct FIMEMORY;

namespace YSLib
{

namespace Drawing
{

//! \since build 418
class CompactPixmap;
//! \since build 470
class HBitmap;

//! \since build 402
using BitPerPixel = std::uint8_t;


/*!
\brief 图像格式。
\note 数值对应 FreeImage 实现的 \c ::FREE_IMAGE_FORMAT 。
\see FreeImage 宏 FI_ENUM 。
\since build 457
*/
enum class ImageFormat : int
{
	Unknown = -1,
	BMP = 0,
	ICO = 1,
	JPEG = 2,
	//! \since build 470
	PNG = 13,
	GIF = 25
};


/*!
\brief 图像解码器标识。
\note 数值对应 FreeImage 实现。
\since build 457
*/
enum class ImageDecoderFlags : int
{
	Default = 0,
	GIF_Load256 = 1,
	GIF_Playback = 2,
	ICO_MakeAlpha = 1,
	JPEG_Fast = 0x0001,
	JPEG_Accurate = 0x0002,
	JPEG_CMYK = 0x0004,
	JPEG_EXIFRotate = 0x0008,
	JPEG_GreyScale = 0x0010,
	JPEG_QualitySuperb = 0x80,
	JPEG_QualityGood = 0x0100,
	JPEG_QualityNormal = 0x0200,
	JPEG_QualityAverage = 0x0400,
	JPEG_QualityBad = 0x0800,
	JPEG_Progressive = 0x2000,
	JPEG_Subsampling_411 = 0x1000,
	JPEG_Subsampling_420 = 0x4000,
	JPEG_Subsampling_422 = 0x8000,
	JPEG_Subsampling_444 = 0x10000,
	JPEG_Optimize = 0x20000,
	JPEG_Baseline = 0x40000
};

/*!
\relates ImageDecoderFlags
\since build 457
*/
DefBitmaskEnum(ImageDecoderFlags)


/*!
\brief 采样过滤算法。
\note 数值对应 FreeImage 实现的 \c ::FREE_IMAGE_FILTER 。
\see FreeImage 宏 FI_ENUM 。
\since build 430
*/
enum class SamplingFilter
{
	Box = 0,
	Bicubic = 1,
	Bilinear = 2,
	BSpline = 3,
	CatmullRom = 4,
	Lanczos3 = 5
};


/*!
\brief 图像元数据模型标识。
\note 数值对应 FreeImage 实现的 \c ::FREE_IMAGE_MDMODEL 。
\since build 557
*/
enum class ImageMetadataModel
{
	NoData = -1,
	Comments = 0,
	EXIF_Main = 1,
	EXIF_EXIF = 2,
	EXIF_GPS = 3,
	EXIF_MakerNote = 4,
	EXIF_Interop = 5,
	IPTC = 6,
	XMP = 7,
	GeoTIFF = 8,
	Animation = 9,
	Custom = 10,
	EXIF_RAW = 11
};


/*!
\brief 图像资源分配失败异常：表示存储等资源不足导致无法创建图像。
\since build 402
*/
class YF_API BadImageAlloc : public std::bad_alloc
{
public:
	//! \since build 586
	//@{
	DefDeCtor(BadImageAlloc)
	DefDeCopyCtor(BadImageAlloc)
	//! \brief 虚析构：类定义外默认实现。
	~BadImageAlloc() override;
	//@}
};


/*!
\brief 未被支持的图像格式异常：表示请求的操作涉及的图像格式不受库的支持。
\since build 471
*/
class YF_API UnsupportedImageFormat
	: public GeneralEvent, public ystdex::unsupported
{
public:
	//! \since build 566
	using GeneralEvent::GeneralEvent;

	//! \since build 586
	DefDeCopyCtor(UnsupportedImageFormat)
	/*!
	\brief 虚析构：类定义外默认实现。
	\since build 586
	*/
	~UnsupportedImageFormat() override;
};


//! \since build 417
//@{
/*!
\brief 未知图像格式异常：表示请求的操作涉及的图像格式因为不明确而不受库的支持。
*/
class YF_API UnknownImageFormat : public UnsupportedImageFormat
{
public:
	//! \since build 566
	using UnsupportedImageFormat::UnsupportedImageFormat;

	//! \since build 586
	DefDeCopyCtor(UnknownImageFormat)
	/*!
	\brief 虚析构：类定义外默认实现。
	\since build 586
	*/
	~UnknownImageFormat() override;
};


/*!
\brief 图像内存对象；保存图像内容的数据缓冲对象。
\note 仅当新建缓冲区时可访问缓冲区数据。
*/
class YF_API ImageMemory final
{
public:
	using NativeHandle = ::FIMEMORY*;
	//! \since build 470
	using Buffer = vector<octet>;

private:
	//! \since build 713
	class YF_API Deleter
	{
	public:
		using pointer = NativeHandle;

		void
		operator()(pointer) const ynothrow;
	};

	Buffer buffer;
	ImageFormat format;
	//! \since build 713
	unique_ptr_from<Deleter> p_memory;

public:
	/*!
	\exception GeneralEvent 打开内存缓冲区失败。
	\note 需要校验图像格式正确。
	\since build 470
	*/
	//@{
	/*!
	\brief 构造：从现有图像打开。
	\post \c GetBuffer().empty() 。
	\throw BadImageAlloc 创建内存数据结构失败。
	\throw GeneralEvent 图像为空。
	\throw GeneralEvent 图像保存到缓冲区失败。
	\throw UnknownImageFormat 未知图像格式。
	\warning 内部缓冲区大小截断至 <tt>unsigned</tt> 。
	*/
	explicit
	ImageMemory(const HBitmap&, ImageFormat = ImageFormat::BMP,
		ImageDecoderFlags = ImageDecoderFlags::Default);
	/*!
	\post <tt>!GetBuffer().empty()</tt> 。
	\exception GeneralEvent 缓冲区大小等于 0 。
	\exception GeneralEvent 打开内存缓冲区失败。
	*/
	//@{
	//! \brief 构造：打开指定的内存缓冲区。
	ImageMemory(Buffer);
	//! \brief 构造：以指定格式打开指定的内存缓冲区。
	ImageMemory(Buffer, ImageFormat);
	//! \brief 构造：新建并打开内存缓冲区。
	template<typename _func>
	ImageMemory(_func f)
		: ImageMemory(f())
	{}
	//! \brief 构造：以指定格式新建并打开内存缓冲区。
	template<typename _func>
	ImageMemory(_func f, ImageFormat fmt)
		: ImageMemory(f(), fmt)
	{}
	//@}
	DefDelMoveCtor(ImageMemory)
	//@}
	//! \since build 461
	DefDeDtor(ImageMemory)

	//! \since build 470
	DefGetter(const ynothrow, const Buffer&, Buffer, buffer)
	DefGetter(const ynothrow, ImageFormat, Format, format)
	DefGetter(const ynothrow, NativeHandle, NativeHandle, p_memory.get())
};
//@}


/*!
\brief 位图句柄：指向位图数据。
\note 内部数据的平凡容器：深复制且可转移。
\since build 418
*/
class YF_API HBitmap final
{
public:
	using DataPtr = observer_ptr<::FIBITMAP>;
	//! \since build 671
	class YF_API Deleter
	{
	public:
		using pointer = ::FIBITMAP*;

		void
		operator()(pointer) const ynothrow;
	};

private:
	//! \since build 671
	unique_ptr_from<Deleter> p_bitmap;

public:
	/*!
	\post \c !*this 。
	\since build 556
	*/
	DefDeCtor(HBitmap)
	/*!
	\brief 构造：使用现有数据指针。
	\note 取得所有权。
	\since build 556
	*/
	HBitmap(DataPtr ptr) ynothrow
		: p_bitmap(ptr.get())
	{}
	//! \exception LoggedEvent 异常中立：输入的大小分量过大。
	//@{
	//! \throw BadImageAlloc 存储分配失败。
	HBitmap(const Size&, BitPerPixel = 0);
	/*!
	\brief 构造：从矩形像素图缓冲区按指定大小和扫描线跨距增量复制并转换图像数据。
	\pre 间接断言：指针参数非空。
	\throw GeneralEvent 转换失败。
	\exception LoggedEvent 异常中立：由跨距计算的偏移值范围检查失败。
	\note 扫描线跨距的单位为字节，
		等于图像的宽乘以每像素字节数与输入的扫描线跨距增量之和。
	\since build 471
	*/
	explicit YB_NONNULL(2)
	HBitmap(BitmapPtr, const Size&, size_t = 0);
	//@}
	/*!
	\brief 构造：从标准矩形像素图缓冲区复制并转换图像数据。
	\exception GeneralEvent 转换失败。
	\since build 471
	*/
	HBitmap(const CompactPixmap&);
	/*!
	\pre 间接断言：路径参数非空。
	\throw std::invalid_argument 文件打开失败。
	\throw GeneralEvent 读取失败。
	\since build 556
	*/
	//@{
	/*!
	\brief 构造：使用指定 UTF-8 文件名和解码器标识。
	\throw UnknownImageFormat 未知图像格式。
	*/
	YB_NONNULL(2)
	HBitmap(const char*, ImageDecoderFlags = ImageDecoderFlags::Default);
	//! \brief 构造：使用指定 UTF-8 文件名、指定格式和解码器标识。
	YB_NONNULL(2)
	HBitmap(const char*, ImageFormat,
		ImageDecoderFlags = ImageDecoderFlags::Default);
	/*!
	\brief 构造：使用指定 UCS-2 文件名和解码器标识。
	\throw UnknownImageFormat 未知图像格式。
	*/
	YB_NONNULL(2)
	HBitmap(const char16_t*, ImageDecoderFlags = ImageDecoderFlags::Default);
	//! \brief 构造：使用指定 UCS-2 文件名、指定格式和解码器标识。
	YB_NONNULL(2)
	HBitmap(const char16_t*, ImageFormat,
		ImageDecoderFlags = ImageDecoderFlags::Default);
	/*!
	\brief 构造：使用指定字符串文件名和解码器标识。
	\throw UnknownImageFormat 未知图像格式。
	\since build 483
	*/
	template<class _tString,
		yimpl(typename = ystdex::enable_for_string_class_t<_tString>)>
	HBitmap(const _tString& filename,
		ImageDecoderFlags = ImageDecoderFlags::Default)
		: HBitmap(&filename[0])
	{}
	/*!
	\brief 构造：使用指定字符串文件名和解码器标识。
	\since build 483
	*/
	template<class _tString,
		yimpl(typename = ystdex::enable_for_string_class_t<_tString>)>
	HBitmap(const _tString& filename, ImageFormat fmt,
		ImageDecoderFlags = ImageDecoderFlags::Default)
		: HBitmap(&filename[0], fmt)
	{}
	//@}
	/*!
	\throw GeneralEvent 读取失败。
	\since build 457
	*/
	HBitmap(const ImageMemory&, ImageDecoderFlags = ImageDecoderFlags::Default);
	/*!
	\brief 构造指定图像转换为指定色深的基于 RGB 像素格式的位图副本。
	\throw UnsupportedImageFormat 指定的色深对指定图形不被支持。
	\throw GeneralEvent 转换失败（包括色深被支持但具体格式不被实现支持的情形）。
	\note 对 16 位位图使用 RGB555 。对 32 位位图使用 RGBA8888 。
	\since build 471
	*/
	HBitmap(const HBitmap&, BitPerPixel);
	//! \since build 417
	//@{
	/*!
	\brief 构造指定图像缩放至指定大小的副本。
	\throw GeneralEvent 缩放失败。
	\since build 430
	*/
	HBitmap(const HBitmap&, const Size&, SamplingFilter);
	//! \throw BadImageAlloc 存储分配失败。
	HBitmap(const HBitmap&);
	DefDeMoveCtor(HBitmap)
	//! \since build 461
	DefDeDtor(HBitmap)
	//@}

	//! \since build 768
	//@{
	//! \brief 复制赋值：使用复制和交换。
	PDefHOp(HBitmap&, =, const HBitmap& pixmap)
		ImplRet(ystdex::copy_and_swap(*this, pixmap))
	DefDeMoveAssignment(HBitmap)
	//@}

	//! \since build 430
	//@{
	DefBoolNeg(explicit, bool(p_bitmap))

	/*!
	\brief 取扫描线数据。
	\pre 断言： \c bool(*this) 。
	\pre 断言： 参数值小于高。
	\return 扫描线数据的起始指针。
	\note 扫描线宽为跨距。
	\since build 566
	*/
	YB_ATTR_returns_nonnull YB_PURE byte*
	operator[](size_t) const ynothrowv;
	//@}

	/*!
	\brief 转换为标准矩形像素图缓冲区。
	\since build 471
	*/
	YB_ATTR_nodiscard YB_PURE
	operator CompactPixmap() const;

	YB_ATTR_nodiscard YB_PURE BitPerPixel
	GetBPP() const ynothrow;
	//! \since build 417
	DefGetter(const ynothrow, DataPtr, DataPtr, make_observer(p_bitmap.get()))
	YB_ATTR_nodiscard YB_PURE SDst
	GetHeight() const ynothrow;
	//! \since build 417
	DefGetter(const ynothrow, Size, Size, {GetWidth(), GetHeight()})
	//! \since build 417
	YB_ATTR_nodiscard YB_PURE SDst
	GetPitch() const ynothrow;
	/*!
	\brief 取像素数据。
	\return 若数据指针为空则为空指针，否则为像素数据起始非空指针。
	\note 像素数据由连续的扫面线数据构成，数量等于高度值。
	\since build 471
	*/
	YB_ATTR_nodiscard YB_PURE byte*
	GetPixels() const ynothrow;
	/*!
	\brief 取扫描线数据。
	\pre 间接断言：参数值小于高。
	\return 若数据指针为空则为空指针，否则为扫描线数据起始非空指针。
	\note 使用 \c operator[] 实现。
	\sa operator[]
	\since build 471
	*/
	YB_ATTR_nodiscard YB_PURE
		PDefH(byte*, GetScanLine, size_t idx) const ynothrow
		ImplRet(p_bitmap ? (*this)[idx] : nullptr)
	YB_ATTR_nodiscard YB_PURE SDst
	GetWidth() const ynothrow;

	/*!
	\brief 释放所有权。
	\post <tt>!*this</tt>
	\since build 671
	*/
	YB_ATTR_nodiscard Deleter::pointer
	Release() ynothrow;

	/*!
	\brief 缩放为指定大小。
	\since build 430
	*/
	void
	Rescale(const Size&, SamplingFilter = SamplingFilter::Box);

	/*!
	\throw std::invalid_argument 不存在可被保存的位图数据。
	\throw GeneralEvent 保存失败。
	\since build 556
	*/
	//@{
	//!\ brief 保存：使用指定 UTF-8 文件名、格式和解码器标识。
	void
	SaveTo(const char*, ImageFormat = ImageFormat::BMP,
		ImageDecoderFlags = ImageDecoderFlags::Default) const;
	//!\ brief 保存：使用指定 UTF-16 文件名、格式和解码器标识。
	void
	SaveTo(const char16_t*, ImageFormat = ImageFormat::BMP,
		ImageDecoderFlags = ImageDecoderFlags::Default) const;
	/*!
	\brief 保存：使用指定字符串文件名、格式和解码器标识。
	\since build 483
	*/
	template<class _tString,
		yimpl(typename = ystdex::enable_for_string_class_t<_tString>)>
	void
	SaveTo(const _tString& filename, ImageFormat fmt = ImageFormat::BMP,
		ImageDecoderFlags flags = ImageDecoderFlags::Default) const
	{
		SaveTo(&filename[0], fmt, flags);
	}
	//@}

	/*!
	\brief 交换。
	\since build 710
	*/
	friend PDefH(void, swap, HBitmap& x, HBitmap& y) ynothrow
		ImplExpr(std::swap(x.p_bitmap, y.p_bitmap))
};

//! \relates HBitmap
//@{
/*!
\brief 取图像延时。
\return CheckNonnegative 检查后的元数据指定的被显示为帧的图像的时间间隔。
\exception GeneralEvent 指定数据不存在。
\exception LoggedEvent 数据小于 0 。
\since build 584
*/
YB_ATTR_nodiscard YF_API YB_PURE Timers::TimeSpan
GetFrameTimeOf(const HBitmap&);

/*!
\brief 取图像逻辑大小。
\return CheckPositive 检查后的元数据指定分量的大小，保证分量大于 0 。
\exception GenerlError 元数据读取失败或不完整。
\exception LoggedEvent 大小分量溢出。
\note 当前只支持 ImageMetadataModel::Animation 模型。
\since build 557
*/
YB_ATTR_nodiscard YF_API YB_PURE Size
GetLogicalSizeOf(const HBitmap&);
//@}


//! \since build 456
//@{
/*!
\brief 多页面位图数据。
\note 非公开实现。
*/
class MultiBitmapData;

/*!
\brief 多页面位图句柄：指向多页面位图数据。
\note 共享复制且可转移。
\warning 非虚析构。
\todo 增加使用 ImageMemory 的构造函数。
*/
class YF_API HMultiBitmap final
{
public:
	using DataPtr = shared_ptr<MultiBitmapData>;
	//! \since build 556
	using iterator = ystdex::subscriptive_iterator<const HMultiBitmap, HBitmap,
		ptrdiff_t, const HBitmap*, HBitmap>;
	//! \since build 460
	using const_iterator = iterator;

private:
	DataPtr pages{};

public:
	/*!
	\brief 无参数构造：默认实现。
	\post \c *!this 。
	\since build 556
	*/
	DefDeCtor(HMultiBitmap)
	/*!
	\post \c bool(*this) 。
	\throw BadImageAlloc 存储分配失败。
	\throw std::invalid_argument 文件打开失败。
	\since build 457
	*/
	//@{
	/*!
	\brief 构造：使用指定 UTF-8 文件名和解码器标识。
	\throw UnknownImageFormat 未知图像格式。
	*/
	HMultiBitmap(const char*, ImageDecoderFlags = ImageDecoderFlags::Default);
	//! \brief 构造：使用指定 UTF-8 文件名、指定格式和解码器标识。
	HMultiBitmap(const char*, ImageFormat,
		ImageDecoderFlags = ImageDecoderFlags::Default);
	/*!
	\brief 构造：使用指定 UCS-2 文件名和解码器标识。
	\throw UnknownImageFormat 未知图像格式。
	*/
	HMultiBitmap(const char16_t*,
		ImageDecoderFlags = ImageDecoderFlags::Default);
	//! \brief 构造：使用指定 UCS-2 文件名、指定格式和解码器标识。
	HMultiBitmap(const char16_t*, ImageFormat,
		ImageDecoderFlags = ImageDecoderFlags::Default);
	//! \since build 483
	//@{
	/*!
	\brief 构造：使用指定字符串文件名和解码器标识。
	\throw UnknownImageFormat 未知图像格式。
	*/
	template<class _tString,
		yimpl(typename = ystdex::enable_for_string_class_t<_tString>)>
	HMultiBitmap(const _tString& filename,
		ImageDecoderFlags flags = ImageDecoderFlags::Default)
		: HMultiBitmap(&filename[0], flags)
	{}
	//! \brief 构造：使用指定字符串文件名和解码器标识。
	template<class _tString,
		yimpl(typename = ystdex::enable_for_string_class_t<_tString>)>
	HMultiBitmap(const _tString& filename, ImageFormat fmt,
		ImageDecoderFlags flags = ImageDecoderFlags::Default)
		: HMultiBitmap(&filename[0], fmt, flags)
	{}
	//@}
	//@}
	DefDeCopyMoveCtorAssignment(HMultiBitmap)

	DefBoolNeg(explicit, bool(pages))

	//! \since build 556
	PDefHOp(HBitmap, [], size_t idx) const ynothrowv
		ImplRet(Lock(idx))

	YB_ATTR_nodiscard YB_PURE size_t
	GetPageCount() const ynothrow;

	YB_ATTR_nodiscard YB_PURE HBitmap
	Lock(size_t = 0) const ynothrowv;

	/*!
	\brief 交换。
	\since build 710
	*/
	friend PDefH(void, swap, HMultiBitmap& x, HMultiBitmap& y) ynothrow
		ImplExpr(std::swap(x.pages, y.pages))

	//! \since build 461
	//@{
	YB_ATTR_nodiscard YB_PURE PDefH(iterator, begin, ) const ynothrow
		ImplRet(HMultiBitmap::iterator(*this, 0))

	YB_ATTR_nodiscard YB_PURE PDefH(iterator, end, ) const ynothrow
		ImplRet(HMultiBitmap::iterator(*this, GetPageCount()))
	//@}
};
//@}


/*!
\brief 图像元数据标签。
\note 若不具有所有权则依赖特定的位图。
\warning 被依赖的位图在标签生存期内不存在则程序行为未定义。
\since build 556
*/
class YF_API ImageTag final
{
public:
	using DataPtr = ::FITAG*;
	using ID = std::uint16_t;
	/*!
	\brief 元数据类型标识。
	\note 数值对应 FreeImage 实现的 \c ::FREE_IMAGE_MDTYPE 类型。
	*/
	enum Type
	{
		NoType = 0,
		Byte = 1,
		ASCII = 2,
		Short = 3,
		Long = 4,
		Rational = 5,
		SByte = 6,
		Undefined = 7,
		SShort = 8,
		SLong = 9,
		SRational = 10,
		Float = 11,
		Double = 12,
		IFD = 13,
		Palette = 14,
		Long8 = 16,
		SLong8 = 17,
		IFD8 = 18
	};

private:
	DataPtr p_tag = {};
	//! \since build 556
	bool owns = {};

public:
	/*!
	\post \c !*this 。
	\post \c !owns 。
	*/
	DefDeCtor(ImageTag)
	/*!
	\brief 构造：使用现有数据指针。
	\param o 所有权标签，若为 true 取得所有权。
	\post <tt>owns == o</tt> 。
	\since build 557
	*/
	ImageTag(DataPtr ptr, bool o = true) ynothrow
		: p_tag(ptr), owns(o)
	{}
	/*!
	\pre 断言：表示名称的指针参数非空。
	\post \c !owns 。
	\throw GeneralEvent 没有找到指定的标签。
	\throw std::invalid_argument 位图为空。
	\since build 557
	\todo 使用 \c YB_NONNULL(3) 。
	*/
	//@{
	//! \brief 构造：使用依赖的位图指针、模型和元数据名称。
	ImageTag(HBitmap::DataPtr, ImageMetadataModel, const char*);
	//! \brief 构造：使用依赖的位图、模型和元数据名称。
	ImageTag(const HBitmap&, ImageMetadataModel, const char*);
	template<typename _tBitmap, class _tString,
		yimpl(typename = ystdex::enable_for_string_class_t<_tString>)>
	ImageTag(const _tBitmap& bmp, ImageMetadataModel model, _tString& name)
		: ImageTag(bmp, model, &name[0])
	{}
	//@}
	//! \post \c owns 。
	ImageTag(const ImageTag&) ythrow(BadImageAlloc);
	/*!
	\post \c !owns 。
	\since build 730
	*/
	ImageTag(const ImageTag& tag, ystdex::false_) ynothrow
		: ImageTag(tag.p_tag, {})
	{}
	~ImageTag();

	DefBoolNeg(explicit, p_tag)

	YB_ATTR_nodiscard YB_PURE size_t
	GetCount() const ynothrow;
	//! \since build 557
	DefGetter(const ynothrow, DataPtr, DataPtr, p_tag)
	YB_ATTR_nodiscard YB_PURE const char*
	GetDescription() const ynothrow;
	YB_ATTR_nodiscard YB_PURE ID
	GetID() const ynothrow;
	YB_ATTR_nodiscard YB_PURE const char*
	GetKey() const ynothrow;
	YB_ATTR_nodiscard YB_PURE size_t
	GetLength() const ynothrow;
	YB_ATTR_nodiscard YB_PURE Type
	GetType() const ynothrow;
	/*!
	\brief 取指定类型的标签值。
	\warning 不检查类型。
	\since build 713
	*/
	template<typename _type>
	YB_ATTR_nodiscard YB_PURE const _type&
	GetValue() const ythrow(GeneralEvent)
	{
		if(const auto p = GetValuePtr())
			return Deref(static_cast<const _type*>(p));
		throw GeneralEvent("Null tag value found.");
	}
	//! \since build 557
	const void*
	GetValuePtr() const ynothrow;

	//! \warning 参数截断至 <tt>unsigned</tt> 。
	bool
	SetCount(size_t) const ynothrow;
	bool
	SetDescription(const char*) const ynothrow;
	bool
	SetID(ID) const ynothrow;
	bool
	SetKey(const char*) const ynothrow;
	//! \warning 参数截断至 <tt>unsigned</tt> 。
	bool
	SetLength(size_t) const ynothrow;
	bool
	SetType(Type) const ynothrow;
	bool
	SetValue(const void*) const ynothrow;

	/*!
	\brief 释放所有权。
	\post \c !*this 。
	*/
	YB_ATTR_nodiscard DataPtr
	Release() ynothrow;
};

/*!
\brief 取按指定图像元数据模型解释的标签值字符串表示。
\note 线程安全：仅在不调用 \c ::FreeImage_TagToString 时保证。
\relates ImageTag
\since build 557
*/
YB_ATTR_nodiscard YF_API YB_PURE string
to_string(const ImageTag&, ImageMetadataModel);


/*!
\brief 图像元数据。
\warning 若参与构造的图像被释放则继续操作的行为未定义。
\warning 非虚析构。
\since build 557
*/
class YF_API ImageMetadataFindData
{
public:
	using DataPtr = ::FIMETADATA*;

	/*!
	\brief 当前生效的模型。

	当前状态为空时开始第一次迭代后决定使用的模型。
	*/
	ImageMetadataModel CurrentModel;

private:
	HBitmap::DataPtr p_bitmap = {};
	ImageTag::DataPtr p_tag = {};
	DataPtr p_metadata = {};

public:
	//! \since build 879
	DefDeCtor(ImageMetadataFindData)
	//! \throw std::invalid_argument 图像指针为空。
	//@{
	ImageMetadataFindData(HBitmap::DataPtr, ImageMetadataModel);
	ImageMetadataFindData(const HBitmap&, ImageMetadataModel);
	//@}
	//! \since build 879
	ImageMetadataFindData(ImageMetadataFindData&&) ynothrow;
	//! \brief 析构：关闭查找状态。
	~ImageMetadataFindData();

	//! \since build 879
	PDefHOp(ImageMetadataFindData&, =, ImageMetadataFindData&& dat) ynothrow
		ImplRet(swap(*this, dat), *this)

	DefBoolNeg(explicit, p_metadata)

	DefGetter(const ynothrow, ImageTag, Tag, {p_tag, {}})

private:
	void
	Close() ynothrow;

public:
	//! \brief 读取：迭代当前查找状态。
	void
	Read() ynothrow;

	void
	Rewind() ynothrow;

	//! \since build 879
	friend DefSwap(ynothrow, ImageMetadataFindData,
		std::swap(_x.p_bitmap, _y.p_bitmap), std::swap(_x.p_tag, _y.p_tag),
		std::swap(_x.p_metadata, _y.p_metadata))
};


/*!
\brief 元数据句柄：表示打开的元数据和内容迭代状态。
\warning 若参与构造的图像被释放则继续操作的行为未定义。
\since build 557
*/
class YF_API HImageMetadata final : private ImageMetadataFindData,
	private ystdex::deref_self<HImageMetadata>
{
public:
	//! \since build 849
	template<typename... _tParams, yimpl(typename
		= ystdex::exclude_self_params_t<HImageMetadata, _tParams...>)>
	explicit
	HImageMetadata(_tParams&&... args)
		: ImageMetadataFindData(yforward(args)...)
	{}
	DefDeMoveCtor(HImageMetadata)

	DefDeMoveAssignment(HImageMetadata)

	/*!
	\brief 间接操作：取自身引用。
	\note 使用 ystdex::indirect_input_iterator 和转换函数访问。
	*/
	using deref_self<HImageMetadata>::operator*;

	//! \brief 迭代：向后遍历。
	PDefHOp(HImageMetadata&, ++, ) ynothrow
		ImplRet(Read(), *this)

	//! \since build 849
	DefBoolNeg(explicit, bool(static_cast<const ImageMetadataFindData&>(*this)))

	//! \brief 转换：取图像标签。
	DefCvt(const, ImageTag, GetTag())

	//! \since build 849
	using ImageMetadataFindData::Rewind;
};


/*!
\brief 元数据迭代器。
\since build 557
*/
using HImageMetadataIterator = ystdex::indirect_input_iterator<HImageMetadata*>;


//! \since build 417
class YF_API ImageCodec final
{
public:
	//! \note 初始化时暂存和重置 errno 。
	ImageCodec();
	//! \since build 461
	~ImageCodec();

	/*!
	\brief 检测图像格式。
	\note 对文件，若根据内容检测失败则根据扩展名判断（不保证正确性）。
	\since build 457
	*/
	//@{
	/*!
	\note 使用图像内存的本机句柄和大小。
	\warning 参数截断至 <tt>unsigned long</tt> 。
	*/
	YB_ATTR_nodiscard static ImageFormat
	DetectFormat(ImageMemory::NativeHandle, size_t);
	//! \note 使用指定 UTF-8 文件名。
	YB_ATTR_nodiscard static ImageFormat
	DetectFormat(const char*);
	//! \note 使用指定 UCS-2 文件名。
	YB_ATTR_nodiscard static ImageFormat
	DetectFormat(const char16_t*);
	//@}

	//! \since build 470
	YB_ATTR_nodiscard YB_PURE static CompactPixmap
	Load(ImageMemory::Buffer);

	/*!
	\brief 读取指定路径的多页面图片文件为用于直接呈现的帧序列。
	\note 非多页面读取结果为空。
	\note 对多页面图片自动选择解码器标识（当前仅支持 GIF 格式）；其它为默认参数。
	\since build 458
	*/
	//@{
	//! \note 使用指定 UTF-8 文件名。
	YB_ATTR_nodiscard static HMultiBitmap
	LoadForPlaying(const char*);
	//! \note 使用指定 UCS-2 文件名。
	YB_ATTR_nodiscard static HMultiBitmap
	LoadForPlaying(const char16_t*);
	/*!
	\brief 构造：使用指定字符串文件名。
	\since build 483
	*/
	template<class _tString,
		yimpl(typename = ystdex::enable_for_string_class_t<_tString>)>
	YB_ATTR_nodiscard static HMultiBitmap
	LoadForPlaying(const _tString& filename)
	{
		return LoadForPlaying(&filename[0]);
	}
	//@}

	/*!
	\brief 读取指定路径的图片文件为用于直接呈现的帧序列。
	\note 使用 LoadForPlaying 按多页面读取，若结果为空按单页面读取。
	\sa LoadForPlaying
	\since build 461
	*/
	template<class _tSeqCon, typename _type>
	YB_ATTR_nodiscard static _tSeqCon
	LoadSequence(const _type& path)
	{
		const auto multi_bitmap(LoadForPlaying(path));
		_tSeqCon con(multi_bitmap.begin(), multi_bitmap.end());

		if(con.empty())
			con.emplace_back(path);
		return con;
	}
};

} // namespace Drawing;

} // namespace YSLib;

#endif

