imageinfo.hpp 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419
  1. //
  2. // Created by xiaozhuai on 2021/4/1.
  3. // https://github.com/xiaozhuai/imageinfo
  4. //
  5. //
  6. // MIT License
  7. //
  8. // Copyright (c) 2021 xiaozhuai
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining a copy
  11. // of this software and associated documentation files (the "Software"), to deal
  12. // in the Software without restriction, including without limitation the rights
  13. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  14. // copies of the Software, and to permit persons to whom the Software is
  15. // furnished to do so, subject to the following conditions:
  16. //
  17. // The above copyright notice and this permission notice shall be included in all
  18. // copies or substantial portions of the Software.
  19. //
  20. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  23. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  26. // SOFTWARE.
  27. //
  28. #pragma once
  29. #ifndef IMAGEINFO_IMAGEINFO_H
  30. #define IMAGEINFO_IMAGEINFO_H
  31. #include <functional>
  32. #include <algorithm>
  33. #include <iostream>
  34. #include <fstream>
  35. #include <string>
  36. #include <regex>
  37. #include <utility>
  38. #include <unordered_map>
  39. #include <unordered_set>
  40. #include <set>
  41. #include <vector>
  42. #include <tuple>
  43. #include <array>
  44. #include <cstdio>
  45. #include <cassert>
  46. #ifdef ANDROID
  47. #include <android/asset_manager.h>
  48. #endif
  49. static_assert(sizeof(uint8_t) == 1, "sizeof(uint8_t) != 1");
  50. static_assert(sizeof(int8_t) == 1, "sizeof(int8_t) != 1");
  51. static_assert(sizeof(uint16_t) == 2, "sizeof(uint16_t) != 2");
  52. static_assert(sizeof(int16_t) == 2, "sizeof(int16_t) != 2");
  53. static_assert(sizeof(uint32_t) == 4, "sizeof(uint32_t) != 4");
  54. static_assert(sizeof(int32_t) == 4, "sizeof(int32_t) != 4");
  55. static_assert(sizeof(uint64_t) == 8, "sizeof(uint64_t) != 8");
  56. static_assert(sizeof(int64_t) == 8, "sizeof(int64_t) != 8");
  57. #ifdef __clang__
  58. #pragma clang diagnostic push
  59. #pragma ide diagnostic ignored "OCUnusedStructInspection"
  60. #pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
  61. #endif
  62. enum IIFormat {
  63. II_FORMAT_UNKNOWN = 0,
  64. II_FORMAT_AVIF,
  65. II_FORMAT_BMP,
  66. II_FORMAT_CUR,
  67. II_FORMAT_DDS,
  68. II_FORMAT_GIF,
  69. II_FORMAT_HDR,
  70. II_FORMAT_HEIC,
  71. II_FORMAT_ICNS,
  72. II_FORMAT_ICO,
  73. II_FORMAT_JP2,
  74. II_FORMAT_JPEG,
  75. II_FORMAT_JPX,
  76. II_FORMAT_KTX,
  77. II_FORMAT_PNG,
  78. II_FORMAT_PSD,
  79. II_FORMAT_TGA,
  80. II_FORMAT_TIFF,
  81. II_FORMAT_WEBP,
  82. };
  83. enum IIErrorCode {
  84. II_ERR_OK = 0,
  85. II_ERR_UNRECOGNIZED_FORMAT,
  86. II_ERR_DECODE_SIZE_FAILED,
  87. };
  88. #ifdef ANDROID
  89. class IIAndroidAssetFileReader {
  90. public:
  91. explicit IIAndroidAssetFileReader(AAsset *file) : m_file(file) {}
  92. inline size_t size() const {
  93. if (m_file != nullptr) {
  94. return AAsset_getLength(m_file);
  95. } else {
  96. return 0;
  97. }
  98. }
  99. inline void read(void *buf, off_t offset, size_t size) {
  100. AAsset_seek(m_file, offset, SEEK_SET);
  101. AAsset_read(m_file, buf, size);
  102. }
  103. private:
  104. AAsset *m_file = nullptr;
  105. };
  106. #endif
  107. class IIFileReader {
  108. public:
  109. explicit IIFileReader(FILE *file) : m_file(file) {}
  110. inline size_t size() {
  111. if (m_file != nullptr) {
  112. fseek(m_file, 0, SEEK_END);
  113. return ftell(m_file);
  114. } else {
  115. return 0;
  116. }
  117. }
  118. inline void read(void *buf, off_t offset, size_t size) {
  119. fseek(m_file, offset, SEEK_SET);
  120. fread(buf, 1, size, m_file);
  121. }
  122. private:
  123. FILE *m_file = nullptr;
  124. };
  125. class IIFilePathReader {
  126. public:
  127. explicit IIFilePathReader(const std::string &path) : m_file(path, std::ios::in | std::ios::binary) {}
  128. ~IIFilePathReader() {
  129. if (m_file.is_open()) {
  130. m_file.close();
  131. }
  132. }
  133. inline size_t size() {
  134. if (m_file.is_open()) {
  135. m_file.seekg(0, std::ios::end);
  136. return (size_t) m_file.tellg();
  137. } else {
  138. return 0;
  139. }
  140. }
  141. inline void read(void *buf, off_t offset, size_t size) {
  142. m_file.seekg(offset, std::ios::beg);
  143. m_file.read((char *) buf, (std::streamsize) size);
  144. }
  145. private:
  146. std::ifstream m_file;
  147. };
  148. class IIFileStreamReader {
  149. public:
  150. explicit IIFileStreamReader(std::ifstream &file) : m_file(file) {}
  151. explicit IIFileStreamReader(std::ifstream *file) : m_file(*file) {}
  152. inline size_t size() {
  153. if (m_file.is_open()) {
  154. m_file.seekg(0, std::ios::end);
  155. return (size_t) m_file.tellg();
  156. } else {
  157. return 0;
  158. }
  159. }
  160. inline void read(void *buf, off_t offset, size_t size) {
  161. m_file.seekg(offset, std::ios::beg);
  162. m_file.read((char *) buf, (std::streamsize) size);
  163. }
  164. private:
  165. std::ifstream &m_file;
  166. };
  167. struct IIRawData {
  168. IIRawData(const void *d, size_t s) : data(d), length(s) {}
  169. const void *data = nullptr;
  170. size_t length = 0;
  171. };
  172. class IIRawDataReader {
  173. public:
  174. explicit IIRawDataReader(IIRawData data) : m_data(data) {}
  175. inline size_t size() const {
  176. return m_data.length;
  177. }
  178. inline void read(void *buf, off_t offset, size_t size) const {
  179. memcpy(buf, ((char *) m_data.data) + offset, size);
  180. }
  181. private:
  182. IIRawData m_data;
  183. };
  184. class IIBuffer {
  185. public:
  186. IIBuffer() = default;
  187. explicit IIBuffer(size_t size) : m_size(size) {
  188. m_data = std::shared_ptr<uint8_t>(new uint8_t[size], std::default_delete<uint8_t[]>());
  189. }
  190. inline const uint8_t *data() const {
  191. return m_data.get();
  192. }
  193. inline uint8_t *data() {
  194. return m_data.get();
  195. }
  196. inline size_t size() const {
  197. return m_size;
  198. }
  199. inline uint8_t &operator[](int offset) {
  200. return m_data.get()[offset];
  201. }
  202. inline uint8_t operator[](int offset) const {
  203. return m_data.get()[offset];
  204. }
  205. public:
  206. inline uint8_t readU8(off_t offset) {
  207. return readInt<uint8_t>(offset, false);
  208. }
  209. inline int8_t readS8(off_t offset) {
  210. return readInt<int8_t>(offset, false);
  211. }
  212. inline uint16_t readU16LE(off_t offset) {
  213. return readInt<uint16_t>(offset, false);
  214. }
  215. inline uint16_t readU16BE(off_t offset) {
  216. return readInt<uint16_t>(offset, true);
  217. }
  218. inline int16_t readS16LE(off_t offset) {
  219. return readInt<int16_t>(offset, false);
  220. }
  221. inline int16_t readS16BE(off_t offset) {
  222. return readInt<int16_t>(offset, true);
  223. }
  224. inline uint32_t readU32LE(off_t offset) {
  225. return readInt<uint32_t>(offset, false);
  226. }
  227. inline uint32_t readU32BE(off_t offset) {
  228. return readInt<uint32_t>(offset, true);
  229. }
  230. inline int32_t readS32LE(off_t offset) {
  231. return readInt<int32_t>(offset, false);
  232. }
  233. inline int32_t readS32BE(off_t offset) {
  234. return readInt<int32_t>(offset, true);
  235. }
  236. inline uint64_t readU64LE(off_t offset) {
  237. return readInt<uint64_t>(offset, false);
  238. }
  239. inline uint64_t readU64BE(off_t offset) {
  240. return readInt<uint64_t>(offset, true);
  241. }
  242. inline int64_t readS64LE(off_t offset) {
  243. return readInt<int64_t>(offset, false);
  244. }
  245. inline int64_t readS64BE(off_t offset) {
  246. return readInt<int64_t>(offset, true);
  247. }
  248. template<typename T>
  249. inline T readInt(off_t offset, bool swapEndian = false) {
  250. T val = *((T *) (data() + offset));
  251. return swapEndian ? swapE<T>(val) : val;
  252. }
  253. inline std::string readString(off_t offset, size_t size) {
  254. return std::string((char *) data() + offset, size);
  255. }
  256. inline std::string toString() {
  257. return std::string((char *) data(), size());
  258. }
  259. inline bool cmp(off_t offset, size_t size, const void *buf) {
  260. return memcmp(data() + offset, buf, size) == 0;
  261. }
  262. inline bool cmpAnyOf(off_t offset, size_t size, const std::initializer_list<const void *> &bufList) {
  263. return std::any_of(bufList.begin(), bufList.end(), [this, offset, size](const void *buf) {
  264. return memcmp(data() + offset, buf, size) == 0;
  265. });
  266. }
  267. private:
  268. template<typename T>
  269. static T swapE(T u) {
  270. union {
  271. T u;
  272. uint8_t u8[sizeof(T)];
  273. } source{}, dest{};
  274. source.u = u;
  275. for (size_t k = 0; k < sizeof(T); k++)
  276. dest.u8[k] = source.u8[sizeof(T) - k - 1];
  277. return dest.u;
  278. }
  279. private:
  280. std::shared_ptr<uint8_t> m_data = nullptr;
  281. size_t m_size = 0;
  282. };
  283. typedef std::function<void(void *buf, off_t offset, size_t size)> IIReadFunc;
  284. class IIReadInterface {
  285. public:
  286. IIReadInterface() = delete;
  287. IIReadInterface(IIReadFunc &readFunc, size_t length)
  288. : m_readFunc(readFunc),
  289. m_length(length) {}
  290. inline IIBuffer readBuffer(off_t offset, size_t size) {
  291. assert(offset >= 0);
  292. assert(offset + size <= m_length);
  293. IIBuffer buffer(size);
  294. read(buffer.data(), offset, size);
  295. return buffer;
  296. }
  297. inline size_t length() const {
  298. return m_length;
  299. }
  300. private:
  301. inline void read(void *buf, off_t offset, size_t size) {
  302. m_readFunc(buf, offset, size);
  303. }
  304. private:
  305. IIReadFunc &m_readFunc;
  306. size_t m_length = 0;
  307. };
  308. typedef std::function<bool(size_t length, IIReadInterface &ri,
  309. int64_t &width, int64_t &height,
  310. std::vector<std::array<int64_t, 2>> &entrySizes)> IIProcessFunc;
  311. struct IIDetector {
  312. IIDetector(IIFormat f, const char *e, const char *fe, const char *mt, IIProcessFunc p)
  313. : format(f),
  314. ext(e),
  315. fullExt(fe),
  316. mimetype(mt),
  317. process(std::move(p)) {}
  318. IIFormat format;
  319. const char *ext;
  320. const char *fullExt;
  321. const char *mimetype;
  322. IIProcessFunc process;
  323. };
  324. static const std::vector<IIDetector> s_ii_detectors = { // NOLINT(cert-err58-cpp)
  325. ///////////////////////// AVIF /////////////////////////
  326. IIDetector(
  327. II_FORMAT_AVIF,
  328. "avif",
  329. "avif",
  330. "image/avif",
  331. [](size_t length, IIReadInterface &ri,
  332. int64_t &width, int64_t &height,
  333. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  334. if (length < 4) {
  335. return false;
  336. }
  337. auto buffer = ri.readBuffer(0, 4);
  338. uint32_t ftypBoxLength = buffer.readU32BE(0);
  339. if (length < ftypBoxLength + 12) {
  340. return false;
  341. }
  342. buffer = ri.readBuffer(0, ftypBoxLength + 12);
  343. if (!buffer.cmp(4, 4, "ftyp")) {
  344. return false;
  345. }
  346. /**
  347. * Major Brand
  348. *
  349. * AVIF: "avif"
  350. * HEIF: "mif1", "msf1"
  351. * HEIC: "heic", "heix", "hevc", "hevx"
  352. *
  353. */
  354. if (!buffer.cmpAnyOf(8, 4, {"avif", "mif1", "msf1", "heic", "heix", "hevc", "hevx"})) {
  355. return false;
  356. }
  357. uint32_t compatibleBrandSize = (ftypBoxLength - 16) / 4;
  358. std::unordered_set<std::string> compatibleBrands;
  359. for (uint32_t i = 0; i < compatibleBrandSize; ++i) {
  360. compatibleBrands.insert(buffer.readString(16 + i * 4, 4));
  361. }
  362. // same as heic, compatibleBrands contains "avif"
  363. if (compatibleBrands.find("avif") == compatibleBrands.end()) {
  364. return false;
  365. }
  366. if (!buffer.cmp(ftypBoxLength + 4, 4, "meta")) {
  367. return false;
  368. }
  369. uint32_t metaLength = buffer.readU32BE(ftypBoxLength);
  370. if (length < ftypBoxLength + 12 + metaLength) {
  371. return false;
  372. }
  373. buffer = ri.readBuffer(ftypBoxLength + 12, metaLength);
  374. off_t offset = 0;
  375. off_t end = metaLength;
  376. loop_box:
  377. /**
  378. * find ispe box
  379. *
  380. * meta
  381. * - ...
  382. * - iprp
  383. * - ...
  384. * - ipco
  385. * - ...
  386. * - ispe
  387. */
  388. while (offset < end) {
  389. // std::string boxType = buffer.readString(offset + 4, 4);
  390. uint32_t boxSize = buffer.readU32BE(offset);
  391. // std::cout << boxSize << ", " << boxType << "\n";
  392. if (buffer.cmpAnyOf(offset + 4, 4, {"iprp", "ipco"})) {
  393. end = offset + boxSize;
  394. offset += 8;
  395. goto loop_box;
  396. }
  397. if (buffer.cmp(offset + 4, 4, "ispe")) {
  398. width = buffer.readU32BE(offset + 12);
  399. height = buffer.readU32BE(offset + 16);
  400. break;
  401. }
  402. offset += boxSize;
  403. }
  404. return true;
  405. }
  406. ),
  407. ///////////////////////// BMP /////////////////////////
  408. // https://www.fileformat.info/format/bmp/corion.htm
  409. IIDetector(
  410. II_FORMAT_BMP,
  411. "bmp",
  412. "bmp",
  413. "image/bmp",
  414. [](size_t length, IIReadInterface &ri,
  415. int64_t &width, int64_t &height,
  416. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  417. if (length < 26) {
  418. return false;
  419. }
  420. auto buffer = ri.readBuffer(0, 26);
  421. if (!buffer.cmp(0, 2, "BM")) {
  422. return false;
  423. }
  424. width = buffer.readS32LE(18);
  425. // bmp height can be negative, it means flip Y
  426. height = std::abs(buffer.readS32LE(22));
  427. return true;
  428. }
  429. ),
  430. ///////////////////////// CUR /////////////////////////
  431. IIDetector(
  432. II_FORMAT_CUR,
  433. "cur",
  434. "cur",
  435. "image/cur",
  436. [](size_t length, IIReadInterface &ri,
  437. int64_t &width, int64_t &height,
  438. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  439. if (length < 6) {
  440. return false;
  441. }
  442. auto buffer = ri.readBuffer(0, 6);
  443. // Same with ico, but TYPE == 2
  444. if (!buffer.cmp(0, 4, "\x00\x00\x02\x00")) {
  445. return false;
  446. }
  447. uint16_t entryCount = buffer.readU16LE(4);
  448. if (entryCount == 0) {
  449. return false;
  450. }
  451. const int ENTRY_SIZE = 16;
  452. off_t entryTotalSize = entryCount * ENTRY_SIZE;
  453. off_t offset = 6;
  454. if (length < offset + entryTotalSize) {
  455. return false;
  456. }
  457. buffer = ri.readBuffer(offset, entryTotalSize);
  458. offset += entryTotalSize;
  459. std::vector<std::array<int64_t, 2>> sizes;
  460. for (int i = 0; i < entryCount; ++i) {
  461. uint8_t w1 = buffer.readU8(i * ENTRY_SIZE);
  462. uint8_t h1 = buffer.readU8(i * ENTRY_SIZE + 1);
  463. int64_t w2 = w1 == 0 ? 256 : w1;
  464. int64_t h2 = h1 == 0 ? 256 : h1;
  465. sizes.push_back({w2, h2});
  466. uint32_t bytes = buffer.readS32LE(i * ENTRY_SIZE + 8);
  467. offset += bytes;
  468. }
  469. if (length < (size_t) offset) {
  470. return false;
  471. }
  472. width = sizes.front()[0];
  473. height = sizes.front()[1];
  474. sizes.swap(entrySizes);
  475. return true;
  476. }
  477. ),
  478. ///////////////////////// DDS /////////////////////////
  479. IIDetector(
  480. II_FORMAT_DDS,
  481. "dds",
  482. "dds",
  483. "image/dds",
  484. [](size_t length, IIReadInterface &ri,
  485. int64_t &width, int64_t &height,
  486. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  487. if (length < 20) {
  488. return false;
  489. }
  490. auto buffer = ri.readBuffer(0, 20);
  491. if (!buffer.cmp(0, 4, "DDS ")) {
  492. return false;
  493. }
  494. height = buffer.readU32LE(12);
  495. width = buffer.readU32LE(16);
  496. return true;
  497. }
  498. ),
  499. ///////////////////////// GIF /////////////////////////
  500. // https://www.fileformat.info/format/gif/corion.htm
  501. IIDetector(
  502. II_FORMAT_GIF,
  503. "gif",
  504. "gif",
  505. "image/gif",
  506. [](size_t length, IIReadInterface &ri,
  507. int64_t &width, int64_t &height,
  508. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  509. if (length < 10) {
  510. return false;
  511. }
  512. auto buffer = ri.readBuffer(0, 10);
  513. if (!buffer.cmpAnyOf(0, 6, {"GIF87a", "GIF89a"})) {
  514. return false;
  515. }
  516. width = buffer.readU16LE(6);
  517. height = buffer.readU16LE(8);
  518. return true;
  519. }
  520. ),
  521. ///////////////////////// HDR /////////////////////////
  522. // http://paulbourke.net/dataformats/pic/
  523. IIDetector(
  524. II_FORMAT_HDR,
  525. "hdr",
  526. "hdr",
  527. "image/vnd.radiance",
  528. [](size_t length, IIReadInterface &ri,
  529. int64_t &width, int64_t &height,
  530. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  531. if (length < 6) {
  532. return false;
  533. }
  534. // TODO Max header size ? Or just read header line by line
  535. auto buffer = ri.readBuffer(0, std::min<size_t>(length, 256));
  536. if (!buffer.cmpAnyOf(0, 6, {"#?RGBE", "#?XYZE"})) {
  537. return false;
  538. }
  539. auto header = buffer.toString();
  540. std::smatch results;
  541. static const std::regex XPattern(R"(\s(\-|\+)X\s(\d+)\s)");
  542. static const std::regex YPattern(R"(\s(\-|\+)Y\s(\d+)\s)");
  543. std::string widthStr;
  544. std::string heightStr;
  545. std::regex_search(header, results, XPattern);
  546. if (results.size() >= 3) widthStr = results.str(2);
  547. std::regex_search(header, results, YPattern);
  548. if (results.size() >= 3) heightStr = results.str(2);
  549. if (!widthStr.empty() && !heightStr.empty()) {
  550. width = std::stol(widthStr);
  551. height = std::stol(heightStr);
  552. }
  553. return true;
  554. }
  555. ),
  556. ///////////////////////// HEIC /////////////////////////
  557. // https://nokiatech.github.io/heif/technical.html
  558. // https://www.jianshu.com/p/b016d10a087d
  559. IIDetector(
  560. II_FORMAT_HEIC,
  561. "heic",
  562. "heic",
  563. "image/heic",
  564. [](size_t length, IIReadInterface &ri,
  565. int64_t &width, int64_t &height,
  566. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  567. if (length < 4) {
  568. return false;
  569. }
  570. auto buffer = ri.readBuffer(0, 4);
  571. uint32_t ftypBoxLength = buffer.readU32BE(0);
  572. if (length < ftypBoxLength + 12) {
  573. return false;
  574. }
  575. buffer = ri.readBuffer(0, ftypBoxLength + 12);
  576. if (!buffer.cmp(4, 4, "ftyp")) {
  577. return false;
  578. }
  579. /**
  580. * Major Brand
  581. *
  582. * AVIF: "avif"
  583. * HEIF: "mif1", "msf1"
  584. * HEIC: "heic", "heix", "hevc", "hevx"
  585. *
  586. */
  587. if (!buffer.cmpAnyOf(8, 4, {"avif", "mif1", "msf1", "heic", "heix", "hevc", "hevx"})) {
  588. return false;
  589. }
  590. uint32_t compatibleBrandSize = (ftypBoxLength - 16) / 4;
  591. std::unordered_set<std::string> compatibleBrands;
  592. for (uint32_t i = 0; i < compatibleBrandSize; ++i) {
  593. compatibleBrands.insert(buffer.readString(16 + i * 4, 4));
  594. }
  595. // compatibleBrands contains "heic"
  596. if (compatibleBrands.find("heic") == compatibleBrands.end()) {
  597. return false;
  598. }
  599. if (!buffer.cmp(ftypBoxLength + 4, 4, "meta")) {
  600. return false;
  601. }
  602. uint32_t metaLength = buffer.readU32BE(ftypBoxLength);
  603. if (length < ftypBoxLength + 12 + metaLength) {
  604. return false;
  605. }
  606. buffer = ri.readBuffer(ftypBoxLength + 12, metaLength);
  607. off_t offset = 0;
  608. off_t end = metaLength;
  609. loop_box:
  610. /**
  611. * find ispe box
  612. *
  613. * meta
  614. * - ...
  615. * - iprp
  616. * - ...
  617. * - ipco
  618. * - ...
  619. * - ispe
  620. */
  621. while (offset < end) {
  622. // std::string boxType = buffer.readString(offset + 4, 4);
  623. uint32_t boxSize = buffer.readU32BE(offset);
  624. // std::cout << boxSize << ", " << boxType << "\n";
  625. if (buffer.cmpAnyOf(offset + 4, 4, {"iprp", "ipco"})) {
  626. end = offset + boxSize;
  627. offset += 8;
  628. goto loop_box;
  629. }
  630. if (buffer.cmp(offset + 4, 4, "ispe")) {
  631. width = buffer.readU32BE(offset + 12);
  632. height = buffer.readU32BE(offset + 16);
  633. break;
  634. }
  635. offset += boxSize;
  636. }
  637. return true;
  638. }
  639. ),
  640. ///////////////////////// ICNS /////////////////////////
  641. IIDetector(
  642. II_FORMAT_ICNS,
  643. "icns",
  644. "icns",
  645. "image/icns",
  646. [](size_t length, IIReadInterface &ri,
  647. int64_t &width, int64_t &height,
  648. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  649. if (length < 8) {
  650. return false;
  651. }
  652. auto buffer = ri.readBuffer(0, 8);
  653. uint32_t fileLength = buffer.readU32BE(4);
  654. if (!buffer.cmp(0, 4, "icns") || fileLength != length) {
  655. return false;
  656. }
  657. static const std::unordered_map<std::string, int64_t> TYPE_SIZE_MAP = {
  658. {"ICON", 32},
  659. {"ICN#", 32},
  660. {"icm#", 16},
  661. {"icm4", 16},
  662. {"icm8", 16},
  663. {"ics#", 16},
  664. {"ics4", 16},
  665. {"ics8", 16},
  666. {"is32", 16},
  667. {"s8mk", 16},
  668. {"icl4", 32},
  669. {"icl8", 32},
  670. {"il32", 32},
  671. {"l8mk", 32},
  672. {"ich#", 48},
  673. {"ich4", 48},
  674. {"ich8", 48},
  675. {"ih32", 48},
  676. {"h8mk", 48},
  677. {"it32", 128},
  678. {"t8mk", 128},
  679. {"icp4", 16},
  680. {"icp5", 32},
  681. {"icp6", 64},
  682. {"ic07", 128},
  683. {"ic08", 256},
  684. {"ic09", 512},
  685. {"ic10", 1024},
  686. {"ic11", 32},
  687. {"ic12", 64},
  688. {"ic13", 256},
  689. {"ic14", 512},
  690. {"ic04", 16},
  691. {"ic05", 32},
  692. {"icsB", 36},
  693. {"icsb", 18},
  694. };
  695. int64_t maxSize = 0;
  696. off_t offset = 8;
  697. while (offset + 8 <= length) {
  698. buffer = ri.readBuffer(offset, 8);
  699. auto type = buffer.readString(0, 4);
  700. uint32_t entrySize = buffer.readU32BE(4);
  701. int64_t s = TYPE_SIZE_MAP.at(type);
  702. entrySizes.push_back({s, s});
  703. maxSize = std::max(maxSize, s);
  704. offset += entrySize;
  705. }
  706. width = maxSize;
  707. height = maxSize;
  708. return true;
  709. }
  710. ),
  711. ///////////////////////// ICO /////////////////////////
  712. IIDetector(
  713. II_FORMAT_ICO,
  714. "ico",
  715. "ico",
  716. "image/ico",
  717. [](size_t length, IIReadInterface &ri,
  718. int64_t &width, int64_t &height,
  719. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  720. if (length < 6) {
  721. return false;
  722. }
  723. auto buffer = ri.readBuffer(0, 6);
  724. // TYPE == 1
  725. if (!buffer.cmp(0, 4, "\x00\x00\x01\x00")) {
  726. return false;
  727. }
  728. uint16_t entryCount = buffer.readU16LE(4);
  729. if (entryCount == 0) {
  730. return false;
  731. }
  732. const int ENTRY_SIZE = 16;
  733. off_t entryTotalSize = entryCount * ENTRY_SIZE;
  734. off_t offset = 6;
  735. if (length < offset + entryTotalSize) {
  736. return false;
  737. }
  738. buffer = ri.readBuffer(offset, entryTotalSize);
  739. offset += entryTotalSize;
  740. std::vector<std::array<int64_t, 2>> sizes;
  741. for (int i = 0; i < entryCount; ++i) {
  742. uint8_t w1 = buffer.readU8(i * ENTRY_SIZE);
  743. uint8_t h1 = buffer.readU8(i * ENTRY_SIZE + 1);
  744. int64_t w2 = w1 == 0 ? 256 : w1;
  745. int64_t h2 = h1 == 0 ? 256 : h1;
  746. sizes.push_back({w2, h2});
  747. uint32_t bytes = buffer.readS32LE(i * ENTRY_SIZE + 8);
  748. offset += bytes;
  749. }
  750. if (length < (size_t) offset) {
  751. return false;
  752. }
  753. width = sizes.front()[0];
  754. height = sizes.front()[1];
  755. sizes.swap(entrySizes);
  756. return true;
  757. }
  758. ),
  759. ///////////////////////// JP2 /////////////////////////
  760. // https://docs.fileformat.com/image/jp2/
  761. IIDetector(
  762. II_FORMAT_JP2,
  763. "jp2",
  764. "jp2",
  765. "image/jp2",
  766. [](size_t length, IIReadInterface &ri,
  767. int64_t &width, int64_t &height,
  768. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  769. if (length < 8) {
  770. return false;
  771. }
  772. auto buffer = ri.readBuffer(0, 8);
  773. if (!buffer.cmp(4, 4, "jP ")) {
  774. return false;
  775. }
  776. uint32_t signatureLength = buffer.readU32BE(0);
  777. off_t offset = signatureLength;
  778. if (length < offset + 12) {
  779. return false;
  780. }
  781. buffer = ri.readBuffer(offset, 12);
  782. // type == "jp2 "
  783. if (!buffer.cmp(4, 4, "ftyp") || !buffer.cmp(8, 4, "jp2 ")) {
  784. return false;
  785. }
  786. uint32_t ftypLength = buffer.readU32BE(0);
  787. offset += ftypLength;
  788. while (offset + 24 <= length) {
  789. buffer = ri.readBuffer(offset, 24);
  790. if (buffer.cmp(4, 4, "jp2h")) {
  791. if (buffer.cmp(12, 4, "ihdr")) {
  792. height = buffer.readU32BE(16);
  793. width = buffer.readU32BE(20);
  794. }
  795. break;
  796. }
  797. uint32_t boxLength = buffer.readU32BE(0);
  798. offset += boxLength;
  799. }
  800. return true;
  801. }
  802. ),
  803. ///////////////////////// JPEG /////////////////////////
  804. // https://www.fileformat.info/format/jpeg/corion.htm
  805. IIDetector(
  806. II_FORMAT_JPEG,
  807. "jpg",
  808. "jpeg",
  809. "image/jpeg",
  810. [](size_t length, IIReadInterface &ri,
  811. int64_t &width, int64_t &height,
  812. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  813. if (length < 2) {
  814. return false;
  815. }
  816. auto buffer = ri.readBuffer(0, 2);
  817. if (!buffer.cmp(0, 2, "\xFF\xD8")) {
  818. return false;
  819. }
  820. off_t offset = 2;
  821. while (offset + 9 <= length) {
  822. buffer = ri.readBuffer(offset, 9);
  823. uint16_t sectionSize = buffer.readU16BE(2);
  824. // 0xFFC0 is baseline standard (SOF0)
  825. // 0xFFC1 is baseline optimized (SOF1)
  826. // 0xFFC2 is progressive (SOF2)
  827. if (buffer.cmpAnyOf(0, 2, {"\xFF\xC0", "\xFF\xC1", "\xFF\xC2"})) {
  828. height = buffer.readU16BE(5);
  829. width = buffer.readU16BE(7);
  830. break;
  831. }
  832. offset += sectionSize + 2;
  833. }
  834. return true;
  835. }
  836. ),
  837. ///////////////////////// JPX /////////////////////////
  838. // https://docs.fileformat.com/image/jpx/
  839. IIDetector(
  840. II_FORMAT_JPX,
  841. "jpx",
  842. "jpx",
  843. "image/jpx",
  844. [](size_t length, IIReadInterface &ri,
  845. int64_t &width, int64_t &height,
  846. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  847. if (length < 8) {
  848. return false;
  849. }
  850. auto buffer = ri.readBuffer(0, 8);
  851. if (!buffer.cmp(4, 4, "jP ")) {
  852. return false;
  853. }
  854. uint32_t signatureLength = buffer.readU32BE(0);
  855. off_t offset = signatureLength;
  856. if (length < offset + 12) {
  857. return false;
  858. }
  859. buffer = ri.readBuffer(offset, 12);
  860. // same as jp2, type == "jpx "
  861. if (!buffer.cmp(4, 4, "ftyp") || !buffer.cmp(8, 4, "jpx ")) {
  862. return false;
  863. }
  864. uint32_t ftypLength = buffer.readU32BE(0);
  865. offset += ftypLength;
  866. while (offset + 24 <= length) {
  867. buffer = ri.readBuffer(offset, 24);
  868. if (buffer.cmp(4, 4, "jp2h")) {
  869. if (buffer.cmp(12, 4, "ihdr")) {
  870. height = buffer.readU32BE(16);
  871. width = buffer.readU32BE(20);
  872. }
  873. break;
  874. }
  875. uint32_t boxLength = buffer.readU32BE(0);
  876. offset += boxLength;
  877. }
  878. return true;
  879. }
  880. ),
  881. ///////////////////////// KTX /////////////////////////
  882. IIDetector(
  883. II_FORMAT_KTX,
  884. "ktx",
  885. "ktx",
  886. "image/ktx",
  887. [](size_t length, IIReadInterface &ri,
  888. int64_t &width, int64_t &height,
  889. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  890. if (length < 44) {
  891. return false;
  892. }
  893. auto buffer = ri.readBuffer(0, 44);
  894. if (!buffer.cmp(1, 6, "KTX 11")) {
  895. return false;
  896. }
  897. width = buffer.readU32LE(36);
  898. height = buffer.readU32LE(40);
  899. return true;
  900. }
  901. ),
  902. ///////////////////////// PNG /////////////////////////
  903. // https://www.fileformat.info/format/png/corion.htm
  904. IIDetector(
  905. II_FORMAT_PNG,
  906. "png",
  907. "png",
  908. "image/png",
  909. [](size_t length, IIReadInterface &ri,
  910. int64_t &width, int64_t &height,
  911. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  912. if (length < 4) {
  913. return false;
  914. }
  915. auto buffer = ri.readBuffer(0, std::min<size_t>(length, 40));
  916. if (!buffer.cmp(0, 4, "\x89PNG")) {
  917. return false;
  918. }
  919. std::string firstChunkType = buffer.readString(12, 4);
  920. if (firstChunkType == "IHDR" && buffer.size() >= 24) {
  921. width = buffer.readU32BE(16);
  922. height = buffer.readU32BE(20);
  923. } else if (firstChunkType == "CgBI") {
  924. if (buffer.readString(28, 4) == "IHDR" && buffer.size() >= 40) {
  925. width = buffer.readU32BE(32);
  926. height = buffer.readU32BE(36);
  927. }
  928. }
  929. return true;
  930. }
  931. ),
  932. ///////////////////////// PSD /////////////////////////
  933. IIDetector(
  934. II_FORMAT_PSD,
  935. "psd",
  936. "psd",
  937. "image/psd",
  938. [](size_t length, IIReadInterface &ri,
  939. int64_t &width, int64_t &height,
  940. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  941. if (length < 22) {
  942. return false;
  943. }
  944. auto buffer = ri.readBuffer(0, 22);
  945. if (!buffer.cmp(0, 6, "8BPS\x00\x01")) {
  946. return false;
  947. }
  948. height = buffer.readU32BE(14);
  949. width = buffer.readU32BE(18);
  950. return true;
  951. }
  952. ),
  953. ///////////////////////// TIFF /////////////////////////
  954. // https://www.fileformat.info/format/tiff/corion.htm
  955. IIDetector(
  956. II_FORMAT_TIFF,
  957. "tif",
  958. "tiff",
  959. "image/tiff",
  960. [](size_t length, IIReadInterface &ri,
  961. int64_t &width, int64_t &height,
  962. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  963. if (length < 8) {
  964. return false;
  965. }
  966. auto buffer = ri.readBuffer(0, 8);
  967. if (!buffer.cmpAnyOf(0, 4, {"\x49\x49\x2A\x00", "\x4D\x4D\x00\x2A"})) {
  968. return false;
  969. }
  970. bool swapEndian = buffer[0] == 0x4D;
  971. auto offset = buffer.readInt<uint32_t>(4, swapEndian);
  972. if (length < offset + 2) {
  973. return true;
  974. }
  975. buffer = ri.readBuffer(offset, 2);
  976. auto numEntry = buffer.readInt<uint16_t>(0, swapEndian);
  977. offset += 2;
  978. for (uint16_t i = 0;
  979. i < numEntry
  980. && length >= offset + 12
  981. && (width == -1 || height == -1);
  982. ++i, offset += 12) {
  983. buffer = ri.readBuffer(offset, 12);
  984. auto tag = buffer.readInt<uint16_t>(0, swapEndian);
  985. auto type = buffer.readInt<uint16_t>(2, swapEndian);
  986. if (tag == 256) { // Found ImageWidth entry
  987. if (type == 3) {
  988. width = buffer.readInt<uint16_t>(8, swapEndian);
  989. } else if (type == 4) {
  990. width = buffer.readInt<uint32_t>(8, swapEndian);
  991. }
  992. } else if (tag == 257) { // Found ImageHeight entry
  993. if (type == 3) {
  994. height = buffer.readInt<uint16_t>(8, swapEndian);
  995. } else if (type == 4) {
  996. height = buffer.readInt<uint32_t>(8, swapEndian);
  997. }
  998. }
  999. }
  1000. return true;
  1001. }
  1002. ),
  1003. ///////////////////////// WEBP /////////////////////////
  1004. // https://developers.google.com/speed/webp/docs/riff_container
  1005. IIDetector(
  1006. II_FORMAT_WEBP,
  1007. "webp",
  1008. "webp",
  1009. "image/webp",
  1010. [](size_t length, IIReadInterface &ri,
  1011. int64_t &width, int64_t &height,
  1012. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  1013. if (length < 16) {
  1014. return false;
  1015. }
  1016. auto buffer = ri.readBuffer(0, std::min<size_t>(length, 30));
  1017. if (!buffer.cmp(0, 4, "RIFF") || !buffer.cmp(8, 4, "WEBP")) {
  1018. return false;
  1019. }
  1020. std::string type = buffer.readString(12, 4);
  1021. if (type == "VP8 " && buffer.size() >= 30) {
  1022. width = buffer.readU16LE(26) & 0x3FFF;
  1023. height = buffer.readU16LE(28) & 0x3FFF;
  1024. } else if (type == "VP8L" && buffer.size() >= 25) {
  1025. uint32_t n = buffer.readU32LE(21);
  1026. width = (n & 0x3FFF) + 1;
  1027. height = ((n >> 14) & 0x3FFF) + 1;
  1028. } else if (type == "VP8X" && buffer.size() >= 30) {
  1029. uint8_t extendedHeader = buffer.readU8(20);
  1030. bool validStart = (extendedHeader & 0xc0) == 0;
  1031. bool validEnd = (extendedHeader & 0x01) == 0;
  1032. if (validStart && validEnd) {
  1033. width = (buffer.readU32LE(24) & 0x00FFFFFF) + 1;
  1034. height = ((buffer.readU32LE(26) & 0xFFFFFF00) >> 8) + 1;
  1035. } else {
  1036. // Invalid
  1037. }
  1038. }
  1039. return true;
  1040. }
  1041. ),
  1042. ///////////////////////// TGA /////////////////////////
  1043. // TODO Not rigorous enough, keep it as last detector
  1044. // https://www.fileformat.info/format/tga/corion.htm
  1045. IIDetector(
  1046. II_FORMAT_TGA,
  1047. "tga",
  1048. "tga",
  1049. "image/tga",
  1050. [](size_t length, IIReadInterface &ri,
  1051. int64_t &width, int64_t &height,
  1052. std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
  1053. if (length < 18) {
  1054. return false;
  1055. }
  1056. auto buffer = ri.readBuffer((off_t) (length - 18), 18);
  1057. if (buffer.cmp(0, 18, "TRUEVISION-XFILE.\x00")) {
  1058. buffer = ri.readBuffer(0, 18);
  1059. width = buffer.readU16LE(12);
  1060. height = buffer.readU16LE(14);
  1061. return true;
  1062. }
  1063. buffer = ri.readBuffer(0, 18);
  1064. uint8_t idLen = buffer.readU8(0);
  1065. if (length < (size_t) idLen + 18) {
  1066. return false;
  1067. }
  1068. uint8_t colorMapType = buffer.readU8(1);
  1069. uint8_t imageType = buffer.readU8(2);
  1070. uint16_t firstColorMapEntryIndex = buffer.readU16LE(3);
  1071. uint16_t colorMapLength = buffer.readU16LE(5);
  1072. uint8_t colorMapEntrySize = buffer.readU8(7);
  1073. // uint16_t xOrigin = buffer.readU16LE(8);
  1074. // uint16_t yOrigin = buffer.readU16LE(10);
  1075. uint16_t w = buffer.readU16LE(12);
  1076. uint16_t h = buffer.readU16LE(14);
  1077. // uint8_t pixelDepth = buffer.readU8(16);
  1078. // uint8_t flags = buffer.readU8(17);
  1079. if (colorMapType == 0) { // no color map
  1080. if (imageType == 0
  1081. || imageType == 2
  1082. || imageType == 3
  1083. || imageType == 10
  1084. || imageType == 11
  1085. || imageType == 32
  1086. || imageType == 33) {
  1087. if (firstColorMapEntryIndex == 0
  1088. && colorMapLength == 0
  1089. && colorMapEntrySize == 0) {
  1090. width = w;
  1091. height = h;
  1092. return true;
  1093. }
  1094. }
  1095. } else if (colorMapType == 1) { // 256 entry palette
  1096. if (imageType == 1
  1097. || imageType == 9) {
  1098. width = w;
  1099. height = h;
  1100. return true;
  1101. }
  1102. }
  1103. return false;
  1104. }
  1105. ),
  1106. };
  1107. class ImageInfo {
  1108. public:
  1109. ImageInfo() = delete;
  1110. explicit ImageInfo(IIReadInterface &ri, IIFormat likelyFormat = II_FORMAT_UNKNOWN, bool mustBe = false) {
  1111. if (likelyFormat != II_FORMAT_UNKNOWN) {
  1112. for (const auto &detector : s_ii_detectors) {
  1113. if (detector.format == likelyFormat) {
  1114. if (tryDetector(detector, ri)) return;
  1115. break;
  1116. }
  1117. }
  1118. if (mustBe) {
  1119. m_err = II_ERR_UNRECOGNIZED_FORMAT;
  1120. return;
  1121. }
  1122. }
  1123. for (const auto &detector : s_ii_detectors) {
  1124. if (detector.format == likelyFormat) continue;
  1125. if (tryDetector(detector, ri)) return;
  1126. }
  1127. m_err = II_ERR_UNRECOGNIZED_FORMAT;
  1128. }
  1129. bool tryDetector(const IIDetector &detector, IIReadInterface &ri) {
  1130. bool match = detector.process(ri.length(), ri, m_width, m_height, m_entrySizes);
  1131. if (match) {
  1132. m_format = detector.format;
  1133. m_ext = detector.ext;
  1134. m_fullExt = detector.fullExt;
  1135. m_mimetype = detector.mimetype;
  1136. m_err = m_width != -1 && m_height != -1
  1137. ? II_ERR_OK
  1138. : II_ERR_DECODE_SIZE_FAILED;
  1139. }
  1140. return match;
  1141. }
  1142. inline explicit operator bool() const {
  1143. return m_err == II_ERR_OK;
  1144. }
  1145. inline bool ok() const {
  1146. return m_err == II_ERR_OK;
  1147. }
  1148. inline IIFormat getFormat() const {
  1149. return m_format;
  1150. }
  1151. inline const char *getExt() const {
  1152. return m_ext;
  1153. }
  1154. inline const char *getFullExt() const {
  1155. return m_fullExt;
  1156. }
  1157. inline const char *getMimetype() const {
  1158. return m_mimetype;
  1159. }
  1160. inline int64_t getWidth() const {
  1161. return m_width;
  1162. }
  1163. inline int64_t getHeight() const {
  1164. return m_height;
  1165. }
  1166. inline std::array<int64_t, 2> getSize() const {
  1167. return {m_width, m_height};
  1168. }
  1169. inline std::vector<std::array<int64_t, 2>> getEntrySizes() const {
  1170. if (m_entrySizes.empty()) {
  1171. return {getSize()};
  1172. }
  1173. return m_entrySizes;
  1174. }
  1175. inline IIErrorCode getErrorCode() const {
  1176. return m_err;
  1177. }
  1178. inline const char *getErrorMsg() const {
  1179. switch (m_err) {
  1180. default:
  1181. return "Unknown error";
  1182. case II_ERR_OK:
  1183. return "Ok";
  1184. case II_ERR_UNRECOGNIZED_FORMAT:
  1185. return "Unrecognized format";
  1186. case II_ERR_DECODE_SIZE_FAILED:
  1187. return "Decode image size failed";
  1188. }
  1189. }
  1190. private:
  1191. IIFormat m_format = II_FORMAT_UNKNOWN;
  1192. const char *m_ext = "";
  1193. const char *m_fullExt = "";
  1194. const char *m_mimetype = "";
  1195. int64_t m_width = -1;
  1196. int64_t m_height = -1;
  1197. IIErrorCode m_err = II_ERR_OK;
  1198. std::vector<std::array<int64_t, 2>> m_entrySizes;
  1199. };
  1200. template<typename ReaderType, typename InputType>
  1201. static inline ImageInfo
  1202. getImageInfo(InputType file, IIFormat likelyFormat = II_FORMAT_UNKNOWN, bool mustBe = false) {
  1203. ReaderType fileReader(file);
  1204. size_t length = fileReader.size();
  1205. IIReadFunc read = [&](void *buf, off_t offset, size_t size) { fileReader.read(buf, offset, size); };
  1206. IIReadInterface ri(read, length);
  1207. return ImageInfo(ri, likelyFormat, mustBe);
  1208. }
  1209. #ifdef __clang__
  1210. #pragma clang diagnostic pop
  1211. #endif
  1212. #endif //IMAGEINFO_IMAGEINFO_H