YSTest: Image.cpp 源文件

YSTest PreAlpha_b900_20201012 The YSLib Test Project

Image.cpp
浏览该文件的文档.
1 /*
2 © 2013-2016, 2018-2020 FrankHB.
3
4 This file is part of the YSLib project, and may only be used,
5 modified, and distributed under the terms of the YSLib project
6 license, LICENSE.TXT. By continuing to use, modify, or distribute
7 this file you indicate that you have read the license and
8 understand and accept it fully.
9 */
10
28 #include "YSLib/Service/YModules.h"
29 #include "CHRLib/YModules.h"
30 #include YFM_YSLib_Adaptor_Image // for Timers::TimeSpan,
31 // ystdex::aligned_store_cast, ystdex::replace_cast;
32 #include <FreeImage.h>
33 #include YFM_YSLib_Service_YBlit
34 #include YFM_YSLib_Service_YGDI
35 #include YFM_CHRLib_CharacterProcessing // for CHRLib::ToASCII;
36
37 namespace YSLib
38 {
39
41 using Timers::TimeSpan;
42
43 namespace Drawing
44 {
45
47 static_assert(int(SamplingFilter::Box) == ::FILTER_BOX
48 && int(SamplingFilter::Bicubic) == ::FILTER_BICUBIC
49 && int(SamplingFilter::Bilinear) == ::FILTER_BILINEAR
50 && int(SamplingFilter::BSpline) == ::FILTER_BSPLINE
51 && int(SamplingFilter::CatmullRom) == ::FILTER_CATMULLROM
52 && int(SamplingFilter::Lanczos3) == ::FILTER_LANCZOS3,
53 "Incompatible filter found.");
54
56 namespace
57 {
58
60 void DLL_CALLCONV
61 FI_OutputMessage(::FREE_IMAGE_FORMAT fif, const char* msg)
62 {
63 YTraceDe(Warning, "FreeImage failed, format = %d: %s.", int(fif), msg);
64
65 yunused(fif),
66 yunused(msg);
67 }
68
69 ::FreeImageIO u8_io{
70 [](void *buffer, unsigned size, unsigned nmemb, fi_handle h) ynothrow{
71 return unsigned(std::fread(buffer, size_t(size),
72 size_t(nmemb), static_cast<std::FILE*>(h)));
73 },
74 [](void *buffer, unsigned size, unsigned nmemb, fi_handle h) ynothrow{
75 return unsigned(std::fwrite(buffer, size_t(size),
76 size_t(nmemb), static_cast<std::FILE*>(h)));
77 },
78 [](::fi_handle h, long offset, int whence) ynothrow{
79 return std::fseek(static_cast<std::FILE*>(h), offset, whence);
80 },
81 [](::fi_handle h) ynothrow{
82 // XXX: The case is a workaround for newlib, e.g. with devkitARM r48.
83 return long(std::ftell(static_cast<std::FILE*>(h)));
84 }
85 };
86
88
89 YB_ATTR_nodiscard ::FIBITMAP*
90 LoadImage(ImageFormat fmt, std::FILE* fp, ImageDecoderFlags flags)
91 {
92 if(fp)
93 {
94 const auto bitmap(::FreeImage_LoadFromHandle(::FREE_IMAGE_FORMAT(fmt),
95 &u8_io, ::fi_handle(fp), int(flags)));
96
97 std::fclose(fp);
98 if(bitmap)
99 return bitmap;
100 throw GeneralEvent("Loading image failed.");
101 }
102 throw std::invalid_argument("Invalid file found on loading image.");
103 }
104 YB_ATTR_nodiscard ::FIBITMAP*
105 LoadImage(ImageFormat fmt, const char* filename, ImageDecoderFlags flags)
106 {
107 return LoadImage(fmt, ufopen(filename, "rb"), flags);
108 }
109 YB_ATTR_nodiscard ::FIBITMAP*
110 LoadImage(ImageFormat fmt, const char16_t* filename, ImageDecoderFlags flags)
111 {
112 return LoadImage(fmt, ufopen(filename, u"rb"), flags);
113 }
115
117
118 void
119 SaveImage(ImageFormat fmt, ::FIBITMAP* dib, std::FILE* fp,
120 ImageDecoderFlags flags)
121 {
122 if(dib)
123 {
124 const bool res(::FreeImage_SaveToHandle(::FREE_IMAGE_FORMAT(fmt),
125 dib, &u8_io, ::fi_handle(fp), int(flags)));
126
127 std::fclose(fp);
128 if(!res)
129 throw GeneralEvent("Failed saving image");
130 }
131 else
132 throw std::invalid_argument(
133 "No valid bitmap data found on saving image.");
134 }
135 void
136 SaveImage(ImageFormat fmt, ::FIBITMAP* dib, const char* filename,
137 ImageDecoderFlags flags)
138 {
139 SaveImage(fmt, dib, ufopen(filename, "wb"), flags);
140 }
141 void
142 SaveImage(ImageFormat fmt, ::FIBITMAP* dib, const char16_t* filename,
143 ImageDecoderFlags flags)
144 {
145 SaveImage(fmt, dib, ufopen(filename, u"wb"), flags);
146 }
148
150
151 YB_ATTR_nodiscard ImageFormat
152 GetFileType(std::FILE* fp)
153 {
154 if(fp)
155 {
156 const auto
157 fif(::FreeImage_GetFileTypeFromHandle(&u8_io, ::fi_handle(fp)));
158
159 std::fclose(fp);
160 return ImageFormat(fif);
161 }
162 return ImageFormat::Unknown;
163 }
164 YB_ATTR_nodiscard ImageFormat
165 GetFileType(const char* filename)
166 {
167 return GetFileType(ufopen(filename, "rb"));
168 }
169 YB_ATTR_nodiscard ImageFormat
170 GetFileType(const char16_t* filename)
171 {
172 return GetFileType(ufopen(filename, u"rb"));
173 }
175
176 YB_ATTR_nodiscard ImageFormat
177 GetFormatFromFilename(const char16_t* filename)
178 {
179 const auto len(ystdex::ntctslen(filename));
180 const auto p_buf(make_unique_default_init<char[]>(len + 1));
181 const auto str(p_buf.get());
182
183 for(size_t i{}; i < len; ++i)
184 str[i] = CHRLib::ToASCII(filename[i]);
185 str[len] = char();
186 return ImageFormat(::FreeImage_GetFIFFromFilename(str));
187 }
188
189 YB_ATTR_nodiscard ::FI_PluginRec&
190 LookupPlugin(::FREE_IMAGE_FORMAT fif)
191 {
192 if(const auto p_node = ::FreeImageEx_GetPluginNodeFromFIF(fif))
193 {
194 if(const auto p_plugin = p_node->m_plugin)
195 return *p_plugin;
196 else
197 throw GeneralEvent("Invalid plugin node found.");
198 }
199 else
200 throw GeneralEvent("No proper plugin found.");
201 }
202
203
204 #if (YCL_PIXEL_FORMAT_XYZ555 & 0x00FFFFFF) == 0x00BBCCDD
205 # define YF_PixConvSpec \
206 16, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK
207 #elif (YCL_PIXEL_FORMAT_XYZ555 & 0x00FFFFFF) == 0x00DDCCBB
208 # define YF_PixConvSpec \
209 16, FI16_555_BLUE_MASK, FI16_555_GREEN_MASK, FI16_555_RED_MASK
210 //#elif (YCL_PIXEL_FORMAT_XYZ555 & 0x00FFFFFF) == 0x00DDCCBB
211 #elif (YCL_PIXEL_FORMAT_XYZ888 & 0x00FFFFFF) == 0x00BBCCDD
212 # define YF_PixConvSpec \
213 32, FI_RGBA_BLUE_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_RED_MASK
214 #elif (YCL_PIXEL_FORMAT_XYZ888 & 0x00FFFFFF) == 0x00DDCCBB
215 # define YF_PixConvSpec \
216 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK
217 # if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
218 //# elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
219 // TODO: Add support for different color orders.
220 # else
221 # error "No supported FreeImage pixel format found."
222 # endif
223 #else
224 # error "Unsupported pixel format found."
225 #endif
226
227 } // unnamed namespace;
228
229
230 ImplDeDtor(BadImageAlloc)
231
232
233 ImplDeDtor(UnsupportedImageFormat)
234
235
236 ImplDeDtor(UnknownImageFormat)
237
238
239 void
240 ImageMemory::Deleter::operator()(pointer p) const ynothrow
241 {
242 ::FreeImage_CloseMemory(p);
243 }
244
245
246 ImageMemory::ImageMemory(const HBitmap& pixmap, ImageFormat fmt,
247 ImageDecoderFlags flags)
248 : buffer(), format([=]{
249 if(fmt == ImageFormat::Unknown)
250 throw UnknownImageFormat("Unknown image format found when saving.");
251 return fmt;
252 }()), p_memory([&]{
253 if(!pixmap.GetDataPtr())
254 throw GeneralEvent("Source image is empty.");
255 return ::FreeImage_OpenMemory();
256 }())
257 {
258 if(p_memory)
259 {
260 if(!::FreeImage_SaveToMemory(::FREE_IMAGE_FORMAT(format),
261 pixmap.GetDataPtr().get(), GetNativeHandle(), int(flags)))
262 throw GeneralEvent("Saving image to memory failed.");
263 }
264 else
265 throw BadImageAlloc();
266 }
267 ImageMemory::ImageMemory(Buffer buf)
268 : ImageMemory(std::move(buf), ImageFormat::Unknown)
269 {
270 format = ImageCodec::DetectFormat(GetNativeHandle(), buffer.size());
271 }
272 ImageMemory::ImageMemory(Buffer buf, ImageFormat fmt)
273 : buffer([&]{
274 if(buf.empty())
275 throw GeneralEvent("Null buffer found.");
276 return std::move(buf);
277 }()), format(fmt),
278 p_memory(::FreeImage_OpenMemory(static_cast< octet*>(buffer.data()),
279 unsigned(buffer.size())))
280 {
281 if(!p_memory)
282 throw GeneralEvent("Opening image memory failed.");
283 }
284
285
286 void
287 HBitmap::Deleter::operator()(pointer p) const ynothrow
288 {
289 ::FreeImage_Unload(p);
290 }
291
292
293 HBitmap::HBitmap(const Size& s, BitPerPixel bpp)
294 : p_bitmap(::FreeImage_Allocate(CheckArithmetic<int>(s.Width),
295 CheckArithmetic<int>(s.Height), bpp, 0, 0, 0))
296 {
297 if(!p_bitmap)
298 throw BadImageAlloc();
299 }
300 HBitmap::HBitmap(BitmapPtr src, const Size& s, size_t pitch_delta)
301 : p_bitmap([&]{
302 return ::FreeImage_ConvertFromRawBits(ystdex::aligned_store_cast<octet*>
303 (Nonnull(src)), CheckArithmetic<int>(s.Width),
304 CheckArithmetic<int>(s.Height), CheckArithmetic<int>(s.Width
305 * sizeof(Pixel) + pitch_delta), YF_PixConvSpec, true);
306 }())
307 {
308 if(!p_bitmap)
309 throw GeneralEvent("Converting compact pixmap failed.");
310 }
311 HBitmap::HBitmap(const CompactPixmap& buf)
312 : HBitmap(buf.GetBufferPtr(), buf.GetSize())
313 {}
314 HBitmap::HBitmap(const char* filename, ImageDecoderFlags flags)
315 : HBitmap(filename, ImageCodec::DetectFormat(filename), flags)
316 {}
317 HBitmap::HBitmap(const char* filename, ImageFormat fmt, ImageDecoderFlags flags)
318 : p_bitmap(Nonnull(LoadImage(fmt, filename, flags)))
319 {}
320 HBitmap::HBitmap(const char16_t* filename, ImageDecoderFlags flags)
321 : HBitmap(filename, ImageCodec::DetectFormat(filename), flags)
322 {}
323 HBitmap::HBitmap(const char16_t* filename, ImageFormat fmt,
324 ImageDecoderFlags flags)
325 : p_bitmap(Nonnull(LoadImage(fmt, filename, flags)))
326 {}
327 HBitmap::HBitmap(const ImageMemory& mem, ImageDecoderFlags flags)
328 : p_bitmap(::FreeImage_LoadFromMemory(::FREE_IMAGE_FORMAT(mem.GetFormat()),
329 mem.GetNativeHandle(), int(flags)))
330 {
331 if(!p_bitmap)
332 throw GeneralEvent("Loading image failed.");
333 }
334 HBitmap::HBitmap(const HBitmap& pixmap, BitPerPixel bpp)
335 : p_bitmap([bpp](::FIBITMAP* p_bmp){
336 switch(bpp)
337 {
338 case 32:
339 return FreeImage_ConvertTo32Bits(p_bmp);
340 case 24:
341 return FreeImage_ConvertTo24Bits(p_bmp);
342 case 16:
343 return FreeImage_ConvertTo16Bits555(p_bmp);
344 case 8:
345 return FreeImage_ConvertTo8Bits(p_bmp);
346 case 4:
347 return FreeImage_ConvertTo4Bits(p_bmp);
348 default:
349 throw UnsupportedImageFormat("Unsupported bit per pixel found.");
350 }
351 }(pixmap.p_bitmap.get()))
352 {
353 if(!p_bitmap)
354 throw GeneralEvent("Converting bitmap failed.");
355 }
356 HBitmap::HBitmap(const HBitmap& pixmap, const Size& s, SamplingFilter sf)
357 : p_bitmap(::FreeImage_Rescale(pixmap.p_bitmap.get(),
358 CheckArithmetic<int>(s.Width), CheckArithmetic<int>(s.Height),
359 ::FREE_IMAGE_FILTER(sf)))
360 {
361 if(!p_bitmap)
362 throw GeneralEvent("Rescaling image failed.");
363 }
364 HBitmap::HBitmap(const HBitmap& pixmap)
365 : p_bitmap(::FreeImage_Clone(pixmap.p_bitmap.get()))
366 {
367 if(!p_bitmap)
368 throw BadImageAlloc();
369 }
370
371 byte*
372 HBitmap::operator[](size_t idx) const ynothrowv
373 {
374 YAssertNonnull(*this);
375 YAssert(idx < GetHeight(), "Index is out of range.");
376 // NOTE: The document of %::FreeImage_GetScanLine implies it returns nonnull
377 // pointer when the bitmap is existed, which has been asserted.
378 return ystdex::replace_cast<byte*>(::FreeImage_GetScanLine(
379 Nonnull(p_bitmap.get()), int(idx)));
380 }
381
382 HBitmap::operator CompactPixmap() const
383 {
384 const Size& s(GetSize());
385 // XXX: Depends right behavior on external API.
386 auto pixels(make_unique_default_init<Pixel[]>(size_t(GetAreaOf(s))));
387
388 ::FreeImage_ConvertToRawBits(ystdex::aligned_store_cast<octet*>(&pixels[0]),
389 GetDataPtr().get(), CheckArithmetic<int>(s.Width * sizeof(Pixel)),
390 YF_PixConvSpec, true);
391 return CompactPixmap(std::move(pixels), s);
392 }
393
394 BitPerPixel
395 HBitmap::GetBPP() const ynothrow
396 {
397 return ::FreeImage_GetBPP(p_bitmap.get());
398 }
399 SDst
400 HBitmap::GetHeight() const ynothrow
401 {
402 return ::FreeImage_GetHeight(p_bitmap.get());
403 }
404 SDst
405 HBitmap::GetPitch() const ynothrow
406 {
407 return ::FreeImage_GetPitch(p_bitmap.get());
408 }
409 byte*
410 HBitmap::GetPixels() const ynothrow
411 {
412 return ystdex::replace_cast<byte*>(::FreeImage_GetBits(p_bitmap.get()));
413 }
414 SDst
415 HBitmap::GetWidth() const ynothrow
416 {
417 return ::FreeImage_GetWidth(p_bitmap.get());
418 }
419
420 HBitmap::Deleter::pointer
421 HBitmap::Release() ynothrow
422 {
423 return p_bitmap.release();
424 }
425
426 void
427 HBitmap::Rescale(const Size& s, SamplingFilter sf)
428 {
429 *this = HBitmap(*this, s, sf);
430 }
431
432 void
433 HBitmap::SaveTo(const char* filename, ImageFormat fmt, ImageDecoderFlags flags)
434 const
435 {
436 SaveImage(fmt, GetDataPtr().get(), filename, flags);
437 }
438 void
439 HBitmap::SaveTo(const char16_t* filename, ImageFormat fmt,
440 ImageDecoderFlags flags) const
441 {
442 SaveImage(fmt, GetDataPtr().get(), filename, flags);
443 }
444
445 TimeSpan
446 GetFrameTimeOf(const HBitmap& bmp)
447 {
448 return TimeSpan(CheckNonnegative<long>(ImageTag(bmp,
449 ImageMetadataModel::Animation, "FrameTime").GetValue<long>(),
450 "frame time", Warning));
451 }
452
453 Size
454 GetLogicalSizeOf(const HBitmap& bmp)
455 {
456 return {CheckPositive<SDst>(ImageTag(bmp, ImageMetadataModel::Animation,
457 "LogicalWidth").GetValue<short>(), "logical width", Warning),
458 CheckPositive<SDst>(ImageTag(bmp, ImageMetadataModel::Animation,
459 "LogicalHeight").GetValue<short>(), "logical height", Warning)};
460 }
461
462
463 class MultiBitmapData final : private noncopyable
464 {
465 private:
466 bool read;
467 ::fi_handle handle;
468 int load_flags;
470 lref<::FreeImageIO> io_ref;
472 lref<::FI_PluginRec> plugin_ref;
473 size_t page_count = 1;
474 void* data = {};
475
476 public:
478 MultiBitmapData(::fi_handle, int, ::FI_PluginRec&, ::FreeImageIO& = u8_io,
479 bool = true);
481 MultiBitmapData(ImageFormat, std::FILE&, int = 0, ::FreeImageIO& = u8_io,
482 bool = true);
484 ~MultiBitmapData();
485
486 DefPred(const ynothrow, OpenForRead, read)
487
488 DefGetter(const ynothrow, size_t, PageCount, page_count)
489
491 observer_ptr<::FIBITMAP>
492 LockPage(size_t = 0) const ynothrow;
493 };
494
495 MultiBitmapData::MultiBitmapData(::fi_handle h, int flags,
496 ::FI_PluginRec& plugin, ::FreeImageIO& io, bool open_for_reading)
497 : read(open_for_reading), handle(h), load_flags(flags), io_ref(io),
498 plugin_ref(plugin)
499 {
500 io.seek_proc(handle, 0, SEEK_SET);
501 if(const auto open = plugin.open_proc)
502 {
503 data = open(&io_ref.get(), handle, open_for_reading);
504 if(const auto proc = plugin_ref.get().pagecount_proc)
505 {
506 const int cnt(proc(&io_ref.get(), handle, data));
507
508 if(cnt > 0)
509 page_count = size_t(cnt);
510 }
511 }
512 }
513 MultiBitmapData::MultiBitmapData(ImageFormat fmt, std::FILE& f, int flags,
514 ::FreeImageIO& io, bool open_for_reading)
515 : MultiBitmapData(::fi_handle(&f), flags,
516 LookupPlugin(::FREE_IMAGE_FORMAT(fmt)), io, open_for_reading)
517 {}
518 MultiBitmapData::~MultiBitmapData()
519 {
520 if(const auto close = plugin_ref.get().close_proc)
521 close(&io_ref.get(), handle, data);
522 }
523
524 observer_ptr<::FIBITMAP>
525 MultiBitmapData::LockPage(size_t index) const ynothrow
526 {
527 YAssert(index < page_count, sfmt("Invalid page index %zu found, should be"
528 " less than %zu.", index, page_count).c_str());
529 if(const auto load = plugin_ref.get().load_proc)
530 return make_observer(load(&io_ref.get(), handle, int(index), load_flags,
531 data));
532 return {};
533 }
534
535
537 namespace
538 {
539
541
542 MultiBitmapData*
543 LoadImagePages(ImageFormat fmt, std::FILE* fp, ImageDecoderFlags flags)
544 {
545 if(fp)
546 TryRet(new MultiBitmapData(fmt, *fp, int(flags)))
547 CatchThrow(std::bad_alloc&, BadImageAlloc())
548 throw std::invalid_argument("Invalid file found on loading image pages.");
549 }
550 MultiBitmapData*
551 LoadImagePages(ImageFormat fmt, const char* filename, ImageDecoderFlags flags)
552 {
553 return LoadImagePages(fmt, ufopen(filename, "rb"), flags);
554 }
555 MultiBitmapData*
556 LoadImagePages(ImageFormat fmt, const char16_t* filename,
557 ImageDecoderFlags flags)
558 {
559 return LoadImagePages(fmt, ufopen(filename, u"rb"), flags);
560 }
562
563 } // unnamed namespace;
564
565
566 HMultiBitmap::HMultiBitmap(const char* filename, ImageDecoderFlags flags)
567 : HMultiBitmap(filename, ImageCodec::DetectFormat(filename), flags)
568 {}
569 HMultiBitmap::HMultiBitmap(const char* filename, ImageFormat fmt,
570 ImageDecoderFlags flags)
571 : pages(Nonnull(LoadImagePages(fmt, filename, flags)))
572 {}
573 HMultiBitmap::HMultiBitmap(const char16_t* filename, ImageDecoderFlags flags)
574 : HMultiBitmap(filename, ImageCodec::DetectFormat(filename), flags)
575 {}
576 HMultiBitmap::HMultiBitmap(const char16_t* filename, ImageFormat fmt,
577 ImageDecoderFlags flags)
578 : pages(Nonnull(LoadImagePages(fmt, filename, flags)))
579 {}
580
581 size_t
582 HMultiBitmap::GetPageCount() const ynothrow
583 {
584 return pages ? pages->GetPageCount() : 0;
585 }
586
587 HBitmap
588 HMultiBitmap::Lock(size_t i) const ynothrowv
589 {
590 return pages ? pages->LockPage(i) : nullptr;
591 }
592
593
594 ImageTag::ImageTag(const ImageTag& tag) ythrow(BadImageAlloc)
595 : p_tag(::FreeImage_CloneTag(tag.p_tag)), owns(true)
596 {
597 if(bool(p_tag) != bool(tag.p_tag))
598 throw BadImageAlloc();
599 }
600 ImageTag::ImageTag(HBitmap::DataPtr p_bmp, ImageMetadataModel model,
601 const char* name)
602 : p_tag([=]{
603 YAssertNonnull(name);
604 if(!p_bmp)
605 throw std::invalid_argument("Invalid bitmap found.");
606
607 ::FITAG* p_new_tag{};
608
609 if(::FreeImage_GetMetadata(::FREE_IMAGE_MDMODEL(model), p_bmp.get(),
610 name, &p_new_tag) && p_new_tag)
611 return p_new_tag;
612 throw GeneralEvent("Specified tag not found.");
613 }())
614 {}
615 ImageTag::ImageTag(const HBitmap& bmp, ImageMetadataModel model,
616 const char* name)
617 : ImageTag(bmp.GetDataPtr(), model, name)
618 {}
619 ImageTag::~ImageTag()
620 {
621 if(owns)
622 ::FreeImage_DeleteTag(p_tag);
623 }
624
625 size_t
626 ImageTag::GetCount() const ynothrow
627 {
628 return ::FreeImage_GetTagCount(p_tag);
629 }
630 const char*
631 ImageTag::GetDescription() const ynothrow
632 {
633 return ::FreeImage_GetTagDescription(p_tag);
634 }
635 ImageTag::ID
636 ImageTag::GetID() const ynothrow
637 {
638 return ::FreeImage_GetTagID(p_tag);
639 }
640 const char*
641 ImageTag::GetKey() const ynothrow
642 {
643 return ::FreeImage_GetTagKey(p_tag);
644 }
645 size_t
646 ImageTag::GetLength() const ynothrow
647 {
648 return ::FreeImage_GetTagLength(p_tag);
649 }
650 ImageTag::Type
651 ImageTag::GetType() const ynothrow
652 {
653 return ImageTag::Type(::FreeImage_GetTagType(p_tag));
654 }
655 const void*
656 ImageTag::GetValuePtr() const ynothrow
657 {
658 return ::FreeImage_GetTagValue(p_tag);
659 }
660
661 bool
662 ImageTag::SetCount(size_t count) const ynothrow
663 {
664 return ::FreeImage_SetTagCount(p_tag, unsigned(count));
665 }
666 bool
667 ImageTag::SetDescription(const char* desc) const ynothrow
668 {
669 return ::FreeImage_SetTagDescription(p_tag, desc);
670 }
671 bool
672 ImageTag::SetID(ImageTag::ID id) const ynothrow
673 {
674 return ::FreeImage_SetTagID(p_tag, id);
675 }
676 bool
677 ImageTag::SetKey(const char* key) const ynothrow
678 {
679 return ::FreeImage_SetTagKey(p_tag, key);
680 }
681 bool
682 ImageTag::SetLength(size_t len) const ynothrow
683 {
684 return ::FreeImage_SetTagLength(p_tag, unsigned(len));
685 }
686 bool
687 ImageTag::SetType(ImageTag::Type type) const ynothrow
688 {
689 return ::FreeImage_SetTagType(p_tag, ::FREE_IMAGE_MDTYPE(type));
690 }
691 bool
692 ImageTag::SetValue(const void* value) const ynothrow
693 {
694 return ::FreeImage_SetTagValue(p_tag, value);
695 }
696
697 ImageTag::DataPtr
698 ImageTag::Release() ynothrow
699 {
700 const auto ptr(p_tag);
701
702 p_tag = {};
703 return ptr;
704 }
705
706 string
707 to_string(const ImageTag& tag, ImageMetadataModel model)
708 {
709 static mutex mtx;
710 lock_guard<mutex> lck(mtx);
711
712 // XXX: The documentation does not guarantee non null return value, but
713 // The implementation actually use static string as buffer, as well as
714 // suggested "not thread safe".
715 return string(Nonnull(::FreeImage_TagToString(::FREE_IMAGE_MDMODEL(model),
716 tag.GetDataPtr())));
717 }
718
719
720 ImageMetadataFindData::ImageMetadataFindData(HBitmap::DataPtr ptr,
721 ImageMetadataModel model)
722 : CurrentModel(model), p_bitmap(ptr)
723 {
724 if(!p_bitmap)
725 throw std::invalid_argument("Invalid bitmap found.");
726 }
727 ImageMetadataFindData::ImageMetadataFindData(const HBitmap& bmp,
728 ImageMetadataModel model)
729 : ImageMetadataFindData(bmp.GetDataPtr(), model)
730 {}
731 ImageMetadataFindData::ImageMetadataFindData(ImageMetadataFindData&& dat)
732 ynothrow
733 : p_bitmap(dat.p_bitmap), p_tag(dat.p_tag), p_metadata(dat.p_metadata)
734 {
735 p_metadata = {};
736 }
737 ImageMetadataFindData::~ImageMetadataFindData()
738 {
739 if(p_metadata)
740 Close();
741 }
742
743 void
744 ImageMetadataFindData::Close() ynothrow
745 {
746 ::FreeImage_FindCloseMetadata(p_metadata);
747 }
748
749 void
750 ImageMetadataFindData::Read() ynothrow
751 {
752 if(!p_metadata)
753 p_metadata = ::FreeImage_FindFirstMetadata(
754 ::FREE_IMAGE_MDMODEL(CurrentModel), p_bitmap.get(), &p_tag);
755 else if(!::FreeImage_FindNextMetadata(p_metadata, &p_tag))
756 {
757 Close();
758 p_metadata = {};
759 }
760 }
761
762 void
763 ImageMetadataFindData::Rewind() ynothrow
764 {
765 if(p_metadata)
766 {
767 Close();
768 p_metadata = {};
769 }
770 }
771
772
773 ImageCodec::ImageCodec()
774 {
775 // XXX: Thread safety of errno.
776 const auto old_errno(errno);
777
778 ::FreeImage_Initialise(false);
779 ::FreeImage_SetOutputMessageStdCall(FI_OutputMessage);
780 errno = old_errno;
781 }
782 ImageCodec::~ImageCodec()
783 {
784 ::FreeImage_DeInitialise();
785 }
786
787 ImageFormat
788 ImageCodec::DetectFormat(ImageMemory::NativeHandle handle, size_t size)
789 {
790 return ImageFormat(::FreeImage_GetFileTypeFromMemory(handle,
791 int(static_cast<unsigned long>(size))));
792 }
793 ImageFormat
794 ImageCodec::DetectFormat(const char* filename)
795 {
796 const auto fmt(GetFileType(filename));
797
798 return fmt == ImageFormat::Unknown
799 ? ImageFormat(::FreeImage_GetFIFFromFilename(filename)) : fmt;
800 }
801 ImageFormat
802 ImageCodec::DetectFormat(const char16_t* filename)
803 {
804 const auto fmt(GetFileType(filename));
805
806 // TODO: Implement detection based on file content rather than filename.
807 return fmt == ImageFormat::Unknown ? GetFormatFromFilename(filename) : fmt;
808 }
809
810 CompactPixmap
811 ImageCodec::Load(ImageMemory::Buffer buf)
812 {
813 const ImageMemory mem(std::move(buf));
814
815 if(mem.GetFormat() == ImageFormat::Unknown)
816 throw UnknownImageFormat("Unknown image format found when loading.");
817 return HBitmap(mem);
818 }
819
820 HMultiBitmap
821 ImageCodec::LoadForPlaying(const char* path)
822 {
823 const auto fmt(DetectFormat(path));
824
825 return HMultiBitmap(path, fmt, fmt == ImageFormat::GIF
826 ? ImageDecoderFlags::GIF_Playback : ImageDecoderFlags::Default);
827 }
828 HMultiBitmap
829 ImageCodec::LoadForPlaying(const char16_t* path)
830 {
831 const auto fmt(DetectFormat(path));
832
833 return HMultiBitmap(path, fmt, fmt == ImageFormat::GIF
834 ? ImageDecoderFlags::GIF_Playback : ImageDecoderFlags::Default);
835 }
836
837 #undef YF_PixConvSpec
838
839 } // namespace Drawing;
840
841 } // namespace YSLib;
842
YSLib::Drawing::ImageMetadataModel
ImageMetadataModel
图像元数据模型标识。
Definition: Image.h:140
YSLib::Drawing::buffer
The boundary is out of the buffer
Definition: YDraw.h:118
YSLib::Drawing::ImageTag::SetLength
bool SetLength(size_t) const ynothrow
Definition: Image.cpp:682
YSLib::Drawing::MultiBitmapData
Definition: Image.cpp:464
YSLib::Drawing::HBitmap::Deleter::pointer
::FIBITMAP * pointer
Definition: Image.h:309
ystdex::make_unique_default_init
enable_if_t<!is_array< _type >::value, std::unique_ptr< _type > > make_unique_default_init()
Definition: memory.hpp:788
YB_ATTR_nodiscard
#define YB_ATTR_nodiscard
标记返回值不被忽略的属性。
Definition: ydef.h:715
YSLib::Drawing::ImageTag::p_tag
DataPtr p_tag
Definition: Image.h:722
YSLib::Drawing::MultiBitmapData::load_flags
int load_flags
Definition: Image.cpp:468
YSLib::Drawing::HBitmap::GetBPP
YB_ATTR_nodiscard YB_PURE BitPerPixel GetBPP() const ynothrow
Definition: Image.cpp:395
platform::Nonnull
YB_PURE _type && Nonnull(_type &&p) ynothrowv
断言并返回非空参数。
Definition: Debug.h:518
size
size_t size
Definition: memory_resource.cpp:295
YSLib::Drawing::ImageMemory::format
ImageFormat format
Definition: Image.h:239
YSLib::Drawing::GetLogicalSizeOf
YB_ATTR_nodiscard YF_API YB_PURE Size GetLogicalSizeOf(const HBitmap &)
取图像逻辑大小。
Definition: Image.cpp:454
CHRLib::ToASCII
YB_ATTR_nodiscard YB_STATELESS yconstfn char ToASCII(_tChar c)
任意整数类型字符转换为 ASCII 取值范围兼容的字符。
Definition: CharacterProcessing.h:64
ystdex::single_thread::mutex
Definition: pseudo_mutex.h:747
YSLib::Drawing::Size
屏幕区域大小。
Definition: YGDIBase.h:536
ystdex::lref
左值引用包装。
Definition: ref.hpp:61
YSLib::Drawing::HBitmap::Deleter::operator()
void operator()(pointer) const ynothrow
Definition: Image.cpp:287
YSLib::Drawing::ImageDecoderFlags::GIF_Playback
@ GIF_Playback
YSLib::Drawing::ImageTag::SetKey
bool SetKey(const char *) const ynothrow
Definition: Image.cpp:677
YSLib::Drawing::ImageCodec::DetectFormat
static YB_ATTR_nodiscard ImageFormat DetectFormat(ImageMemory::NativeHandle, size_t)
检测图像格式。
Definition: Image.cpp:788
YSLib::Drawing::ImageMemory::ImageMemory
ImageMemory(const HBitmap &, ImageFormat=ImageFormat::BMP, ImageDecoderFlags=ImageDecoderFlags::Default)
构造：从现有图像打开。
YSLib::Drawing::HBitmap::p_bitmap
unique_ptr_from< Deleter > p_bitmap
Definition: Image.h:317
YSLib::Drawing::ImageMemory::Deleter::operator()
void operator()(pointer) const ynothrow
YSLib::Drawing::ImageTag::SetDescription
bool SetDescription(const char *) const ynothrow
Definition: Image.cpp:667
YSLib::Drawing::ImageTag::ImageTag
ImageTag(const HBitmap &, ImageMetadataModel, const char *)
构造：使用依赖的位图、模型和元数据名称。
Definition: Image.cpp:615
YSLib::IO::int
YF_API int
Definition: File.h:51
YSLib::Drawing::MultiBitmapData::io_ref
lref<::FreeImageIO > io_ref
Definition: Image.cpp:470
YModules.h
CHRLib 库模块配置文件。
YSLib::Drawing::HMultiBitmap::HMultiBitmap
HMultiBitmap(const char *, ImageFormat, ImageDecoderFlags=ImageDecoderFlags::Default)
构造：使用指定 UTF-8 文件名、指定格式和解码器标识。
Definition: Image.cpp:569
YSLib::Drawing::HBitmap::SaveTo
void SaveTo(const char *, ImageFormat=ImageFormat::BMP, ImageDecoderFlags=ImageDecoderFlags::Default) const
\ brief 保存：使用指定 UTF-8 文件名、格式和解码器标识。
Definition: Image.cpp:433
YSLib::GeneralEvent
std::runtime_error GeneralEvent
一般运行时异常事件类。
Definition: YException.h:46
ythrow
#define ythrow(...)
YSLib 动态异常规范：根据是否使用异常规范宏指定或忽略动态异常规范。
Definition: ydef.h:1256
YSLib::Drawing::GetFrameTimeOf
TimeSpan GetFrameTimeOf(const HBitmap &bmp)
Definition: Image.cpp:446
YSLib::Drawing::ImageDecoderFlags::Default
@ Default
platform_ex::Windows::Invalid
@ Invalid
Definition: Win32/include/YCLib/MinGW32.h:485
YSLib::Drawing::HBitmap::Rescale
void Rescale(const Size &, SamplingFilter=SamplingFilter::Box)
缩放为指定大小。
Definition: Image.cpp:427
YSLib::CheckArithmetic
_tDst CheckArithmetic(_type val, const string &name={}, RecordLevel lv=Err)
检查算术类型数值在指定类型的范围内。
Definition: YCoreUtilities.h:320
CHRLib::f
f(d, s)
YSLib::Drawing::ImageMemory
图像内存对象；保存图像内容的数据缓冲对象。
Definition: Image.h:221
YModules.h
YSLib 服务模块配置文件。
YSLib::Drawing::ImageTag
图像元数据标签。
Definition: Image.h:691
platform_ex::Windows::unsigned
YF_API unsigned
Definition: NLS.h:52
ystdex::ntctslen
size_t ntctslen(const _tChar *s)
计算简单 NTCTS 长度。
Definition: cstring.h:456
YSLib::Drawing::ImageMetadataFindData::CurrentModel
ImageMetadataModel CurrentModel
当前生效的模型。
Definition: Image.h:855
YSLib::Drawing::HBitmap::GetPixels
YB_ATTR_nodiscard YB_PURE byte * GetPixels() const ynothrow
取像素数据。
Definition: Image.cpp:410
YSLib::Drawing::BadImageAlloc
图像资源分配失败异常：表示存储等资源不足导致无法创建图像。
Definition: Image.h:162
YSLib::Drawing::ImageTag::Release
YB_ATTR_nodiscard DataPtr Release() ynothrow
释放所有权。
Definition: Image.cpp:698
YSLib::Drawing::HBitmap
位图句柄：指向位图数据。
Definition: Image.h:302
platform::ufopen
std::FILE * ufopen(const char *filename, const char *mode) ynothrowv
Definition: FileIO.cpp:897
YSLib::Drawing::ImageTag::SetID
bool SetID(ID) const ynothrow
Definition: Image.cpp:672
YSLib::Drawing::get
YB_ATTR_nodiscard YB_PURE yconstfn _type & get(GBinaryGroup< _type > &val)
取分量。
Definition: YGDIBase.h:484
YSLib::Drawing::ImageTag::owns
bool owns
Definition: Image.h:724
YSLib::Drawing::MultiBitmapData::data
void * data
Definition: Image.cpp:474
YSLib::YAssert
YAssert(!(v< Deref(a)), "Value less than lower bound found.")
YSLib::Drawing::ImageTag::SetCount
bool SetCount(size_t) const ynothrow
Definition: Image.cpp:662
ystdex::make_observer
observer_ptr< _type > make_observer(_type *p)
Definition: pointer.hpp:405
YSLib::Drawing::ImageTag::GetLength
YB_ATTR_nodiscard YB_PURE size_t GetLength() const ynothrow
Definition: Image.cpp:646
YSLib::Drawing::ImageMetadataFindData::Read
void Read() ynothrow
读取：迭代当前查找状态。
Definition: Image.cpp:750
YSLib::Drawing::ImageFormat::GIF
@ GIF
ystdex::threading::lock_guard
Definition: pseudo_mutex.h:223
ystdex::cpp2017::data
auto data(_tRange &c) -> decltype(c.data())
Definition: range.hpp:581
YSLib::Drawing::src
src
Definition: YGDI.h:442
YSLib::Drawing::ImageMetadataFindData::p_bitmap
HBitmap::DataPtr p_bitmap
Definition: Image.h:858
YSLib::Drawing::UnknownImageFormat
未知图像格式异常：表示请求的操作涉及的图像格式因为不明确而不受库的支持。
Definition: Image.h:201
YSLib::Drawing::HMultiBitmap
多页面位图句柄：指向多页面位图数据。
Definition: Image.h:587
YSLib::Drawing::MultiBitmapData::page_count
size_t page_count
Definition: Image.cpp:473
YSLib::Drawing::HBitmap::HBitmap
HBitmap(const Size &, BitPerPixel=0)
Definition: Image.cpp:293
std
Definition: optional.h:982
YSLib::Drawing::ImageMetadataFindData::Rewind
void Rewind() ynothrow
Definition: Image.cpp:763
ystdex::sfmt
_tString sfmt(const typename string_traits< _tString >::value_type *fmt,...)
以 C 标准输出格式的输出 basic_string 实例的对象。
Definition: string.hpp:2028
YAssertNonnull
#define YAssertNonnull(_expr)
Definition: cassert.h:165
YSLib::Drawing::ImageMetadataFindData::ImageMetadataFindData
DefDeCtor(ImageMetadataFindData) ImageMetadataFindData(HBitmap ImageMetadataFindData(const HBitmap &, ImageMetadataModel)
Definition: Image.cpp:727
YSLib::Drawing::ImageFormat
ImageFormat
图像格式。
Definition: Image.h:69
YSLib::i
return i
Definition: YCoreUtilities.h:177
YSLib::Drawing::ImplDeDtor
ImplDeDtor(FontException) namespace
Definition: Font.cpp:80
YSLib::Drawing::GetSize
dst GetSize()
YSLib::Drawing::ImageFormat::Unknown
@ Unknown
YTraceDe
#define YTraceDe(...)
YCLib 默认调试跟踪。
Definition: Debug.h:407
YSLib::Drawing::BitPerPixel
std::uint8_t BitPerPixel
Definition: Image.h:59
platform::SDst
std::uint16_t SDst
屏幕坐标距离。
Definition: Video.h:44
YSLib::UI::Close
void Close(IWidget &wgt)
Definition: YWidget.cpp:92
YSLib::Drawing::CompactPixmap
标准矩形像素图缓冲区。
Definition: YGDI.h:233
YSLib::Drawing::ImageCodec::ImageCodec
ImageCodec()
Definition: Image.cpp:773
YSLib::Drawing::MultiBitmapData::plugin_ref
lref<::FI_PluginRec > plugin_ref
Definition: Image.cpp:472
CatchThrow
#define CatchThrow(_ex,...)
指定抛出指定异常作为异常处理器。
Definition: YBaseMacro.h:141
YSLib::Drawing::ImageTag::ID
std::uint16_t ID
Definition: Image.h:694
YSLib::Drawing::ImageDecoderFlags
ImageDecoderFlags
图像解码器标识。
Definition: Image.h:86
platform::Descriptions::Warning
@ Warning
Definition: YCommon.h:183
YSLib::path
YB_ATTR_nodiscard YF_API const char * path
Definition: Initialization.h:116
YSLib::Drawing::ImageCodec::LoadForPlaying
static YB_ATTR_nodiscard HMultiBitmap LoadForPlaying(const char *)
读取指定路径的多页面图片文件为用于直接呈现的帧序列。
Definition: Image.cpp:821
YSLib
YSLib 命名空间。
Definition: AndroidHost.h:46
YSLib::Drawing::ImageMetadataFindData::p_tag
ImageTag::DataPtr p_tag
Definition: Image.h:859
CHRLib::s
_tDst const _tSrc * s
Definition: CharacterProcessing.h:143
YSLib::Drawing::ImageMetadataModel::Animation
@ Animation
YSLib::Drawing::ImageMemory::Buffer
vector< octet > Buffer
Definition: Image.h:225
YSLib::str
const char * str
Definition: YException.cpp:60
ystdex::lref::get
_type & get() const
Definition: ref.hpp:96
YSLib::Drawing::HMultiBitmap::Lock
YB_ATTR_nodiscard YB_PURE HBitmap Lock(size_t=0) const ynothrowv
Definition: Image.cpp:588
YSLib::Drawing::ImageTag::SetType
bool SetType(Type) const ynothrow
Definition: Image.cpp:687
NPL::A1::name
const char * name
Definition: NPLA1.h:1876
YSLib::Drawing::ynothrow
YB_ATTR_nodiscard yconstfn YB_PURE x x x Bottom YB_ATTR_nodiscard yconstfn YB_PURE x Right y x Top y x Bottom y Bottom YB_ATTR_nodiscard yconstfn YB_PURE x Right y x Top y x Bottom y Bottom YB_ATTR_nodiscard yconstfn YB_PURE const Padding & ynothrow
Definition: YGDI.h:132
YSLib::Drawing::MultiBitmapData::LockPage
page_count observer_ptr<::FIBITMAP > LockPage(size_t=0) const ynothrow
Definition: Image.cpp:525
YSLib::Drawing::HBitmap::DataPtr
observer_ptr<::FIBITMAP > DataPtr
Definition: Image.h:304
CHRLib::char16_t
YB_ATTR_nodiscard YB_PURE char16_t((*c_ptr<< CHAR_BIT)|c_ptr[1])) YB_ATTR_nodiscard YB_PURE inline PDefH(char16_t
取 c_ptr 指向的小端序双字节字符。
ynothrowv
#define ynothrowv
YSLib 无异常抛出保证验证：有条件地使用无异常抛出规范。
Definition: ydef.h:1219
YSLib::Text::buf
YB_ATTR_nodiscard YB_PURE const char * buf
Definition: TextFile.h:83
platform_ex::Windows::proc
const char * proc
Definition: Win32/include/YCLib/MinGW32.h:254
YSLib::Drawing::ImageMetadataFindData::~ImageMetadataFindData
~ImageMetadataFindData()
析构：关闭查找状态。
Definition: Image.cpp:737
YSLib::Drawing::ImageTag::GetKey
YB_ATTR_nodiscard YB_PURE const char * GetKey() const ynothrow
Definition: Image.cpp:641
YSLib::Drawing::ImageTag::GetType
YB_ATTR_nodiscard YB_PURE Type GetType() const ynothrow
Definition: Image.cpp:651
ystdex::octet
void octet
Definition: ydef.h:1294
NPL::c_str
YF_API const char src c_str())) YF_API void InstallDirectory(const string &
安装目录：复制目录树。
Definition: Dependency.h:140
YSLib::Drawing::ImageMemory::buffer
Buffer buffer
Definition: Image.h:238
fp
tidy_ptr< std::streambuf > fp
Definition: TextManager.cpp:93
YSLib::Drawing::ImageTag::GetID
YB_ATTR_nodiscard YB_PURE ID GetID() const ynothrow
Definition: Image.cpp:636
YSLib::Drawing::MultiBitmapData::MultiBitmapData
MultiBitmapData(::fi_handle, int, ::FI_PluginRec &, ::FreeImageIO &=u8_io, bool=true)
Definition: Image.cpp:495
DefGetter
#define DefGetter(_q, _t, _n,...)
Definition: YBaseMacro.h:241
YSLib::Drawing::ImageTag::SetValue
bool SetValue(const void *) const ynothrow
Definition: Image.cpp:692
YSLib::Drawing::ImageMetadataFindData
图像元数据。
Definition: Image.h:846
YSLib::Drawing::ImageTag::GetValuePtr
const void * GetValuePtr() const ynothrow
Definition: Image.cpp:656
platform::p
return p p
Definition: YCLib/FileSystem.h:209
const
const Decapsulate const Decapsulate &y ynothrow ContextNode &ctx const
Definition: NPLA1Forms.cpp:1629
ystdex::cpp2017::string
basic_string< char > string
Definition: string.hpp:772
YSLib::Drawing::ImageCodec::Load
YB_ATTR_nodiscard static YB_PURE CompactPixmap Load(ImageMemory::Buffer)
Definition: Image.cpp:811
YSLib::Drawing::ImageTag::DataPtr
::FITAG * DataPtr
Definition: Image.h:693
YSLib::Drawing::BitmapPtr
Pixel * BitmapPtr
Definition: YGraphics.h:42
YSLib::Timers::TimeSpan
std::chrono::duration< Duration::rep, std::milli > TimeSpan
低精度时间间隔。
Definition: YClock.h:81
YSLib::Drawing::GetHeight
YB_ATTR_nodiscard YB_PURE tr GetHeight())) inline PDefH(void
按字体高度和行距调整文本区域的底边距。
YSLib::Drawing::ImageMemory::NativeHandle
::FIMEMORY * NativeHandle
Definition: Image.h:223
YSLib::Drawing::MultiBitmapData::handle
::fi_handle handle
Definition: Image.cpp:467
YSLib::Drawing::HMultiBitmap::pages
DataPtr pages
Definition: Image.h:597
ystdex::bases::noncopyable
不可复制对象：禁止派生类调用默认原型的复制构造函数和复制赋值操作符。
Definition: base.h:50
TryRet
#define TryRet(...)
指定表达式在 try 块中求值并返回。
Definition: YBaseMacro.h:124
YSLib::Drawing::MultiBitmapData::size_t
size_t
Definition: Image.cpp:488
yunused
#define yunused(...)
标记未使用的表达式。
Definition: ydef.h:1458
YSLib::Drawing::ImageCodec
Definition: Image.h:952
YSLib::Drawing::ImageCodec::~ImageCodec
~ImageCodec()
Definition: Image.cpp:782
YSLib::Drawing::UnsupportedImageFormat
未被支持的图像格式异常：表示请求的操作涉及的图像格式不受库的支持。
Definition: Image.h:180
YSLib::Drawing::MultiBitmapData::~MultiBitmapData
~MultiBitmapData()
Definition: Image.cpp:518
YSLib::Drawing::to_string
YB_ATTR_nodiscard YB_PURE std::string to_string(const GBinaryGroup< _type > &val)
转换为字符串。
Definition: YGDIBase.h:507
YSLib::Drawing::MultiBitmapData::DefPred
DefPred(const ynothrow, OpenForRead, read) DefGetter(const ynothrow
YSLib::Drawing::MultiBitmapData::read
bool read
Definition: Image.cpp:466
YSLib::Drawing::ImageTag::Type
Type
元数据类型标识。
Definition: Image.h:700
YSLib::Drawing::HBitmap::Release
YB_ATTR_nodiscard YB_PURE PDefH(byte *, GetScanLine, size_t idx) const ynothrow ImplRet(p_bitmap ?(*this) Release[idx] YB_ATTR_nodiscard Deleter::pointer() ynothrow
取扫描线数据。
Definition: Image.h:502
YSLib::Drawing::ImageMetadataFindData::p_metadata
DataPtr p_metadata
Definition: Image.h:860
YSLib::Drawing::MultiBitmapData::PageCount
PageCount
Definition: Image.cpp:488
YSLib::Drawing::ImageTag::~ImageTag
~ImageTag()
Definition: Image.cpp:619