webpinfo.c 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186
  1. // Copyright 2017 Google Inc. All Rights Reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style license
  4. // that can be found in the COPYING file in the root of the source
  5. // tree. An additional intellectual property rights grant can be found
  6. // in the file PATENTS. All contributing project authors may
  7. // be found in the AUTHORS file in the root of the source tree.
  8. // -----------------------------------------------------------------------------
  9. //
  10. // Command-line tool to print out the chunk level structure of WebP files
  11. // along with basic integrity checks.
  12. //
  13. // Author: Hui Su ([email protected])
  14. #include <assert.h>
  15. #include <stdio.h>
  16. #ifdef HAVE_CONFIG_H
  17. #include "webp/config.h"
  18. #endif
  19. #include "../imageio/imageio_util.h"
  20. #include "./unicode.h"
  21. #include "webp/decode.h"
  22. #include "webp/format_constants.h"
  23. #include "webp/mux_types.h"
  24. #if defined(_MSC_VER) && _MSC_VER < 1900
  25. #define snprintf _snprintf
  26. #endif
  27. #define LOG_ERROR(MESSAGE) \
  28. do { \
  29. if (webp_info->show_diagnosis_) { \
  30. fprintf(stderr, "Error: %s\n", MESSAGE); \
  31. } \
  32. } while (0)
  33. #define LOG_WARN(MESSAGE) \
  34. do { \
  35. if (webp_info->show_diagnosis_) { \
  36. fprintf(stderr, "Warning: %s\n", MESSAGE); \
  37. } \
  38. ++webp_info->num_warnings_; \
  39. } while (0)
  40. static const char* const kFormats[3] = {
  41. "Unknown",
  42. "Lossy",
  43. "Lossless"
  44. };
  45. static const char* const kLosslessTransforms[4] = {
  46. "Predictor",
  47. "Cross Color",
  48. "Subtract Green",
  49. "Color Indexing"
  50. };
  51. static const char* const kAlphaFilterMethods[4] = {
  52. "None",
  53. "Horizontal",
  54. "Vertical",
  55. "Gradient"
  56. };
  57. typedef enum {
  58. WEBP_INFO_OK = 0,
  59. WEBP_INFO_TRUNCATED_DATA,
  60. WEBP_INFO_PARSE_ERROR,
  61. WEBP_INFO_INVALID_PARAM,
  62. WEBP_INFO_BITSTREAM_ERROR,
  63. WEBP_INFO_MISSING_DATA,
  64. WEBP_INFO_INVALID_COMMAND
  65. } WebPInfoStatus;
  66. typedef enum ChunkID {
  67. CHUNK_VP8,
  68. CHUNK_VP8L,
  69. CHUNK_VP8X,
  70. CHUNK_ALPHA,
  71. CHUNK_ANIM,
  72. CHUNK_ANMF,
  73. CHUNK_ICCP,
  74. CHUNK_EXIF,
  75. CHUNK_XMP,
  76. CHUNK_UNKNOWN,
  77. CHUNK_TYPES = CHUNK_UNKNOWN
  78. } ChunkID;
  79. typedef struct {
  80. size_t start_;
  81. size_t end_;
  82. const uint8_t* buf_;
  83. } MemBuffer;
  84. typedef struct {
  85. size_t offset_;
  86. size_t size_;
  87. const uint8_t* payload_;
  88. ChunkID id_;
  89. } ChunkData;
  90. typedef struct WebPInfo {
  91. int canvas_width_;
  92. int canvas_height_;
  93. int loop_count_;
  94. int num_frames_;
  95. int chunk_counts_[CHUNK_TYPES];
  96. int anmf_subchunk_counts_[3]; // 0 VP8; 1 VP8L; 2 ALPH.
  97. uint32_t bgcolor_;
  98. int feature_flags_;
  99. int has_alpha_;
  100. // Used for parsing ANMF chunks.
  101. int frame_width_, frame_height_;
  102. size_t anim_frame_data_size_;
  103. int is_processing_anim_frame_, seen_alpha_subchunk_, seen_image_subchunk_;
  104. // Print output control.
  105. int quiet_, show_diagnosis_, show_summary_;
  106. int num_warnings_;
  107. int parse_bitstream_;
  108. } WebPInfo;
  109. static void WebPInfoInit(WebPInfo* const webp_info) {
  110. memset(webp_info, 0, sizeof(*webp_info));
  111. }
  112. static const uint32_t kWebPChunkTags[CHUNK_TYPES] = {
  113. MKFOURCC('V', 'P', '8', ' '),
  114. MKFOURCC('V', 'P', '8', 'L'),
  115. MKFOURCC('V', 'P', '8', 'X'),
  116. MKFOURCC('A', 'L', 'P', 'H'),
  117. MKFOURCC('A', 'N', 'I', 'M'),
  118. MKFOURCC('A', 'N', 'M', 'F'),
  119. MKFOURCC('I', 'C', 'C', 'P'),
  120. MKFOURCC('E', 'X', 'I', 'F'),
  121. MKFOURCC('X', 'M', 'P', ' '),
  122. };
  123. // -----------------------------------------------------------------------------
  124. // Data reading.
  125. static int GetLE16(const uint8_t* const data) {
  126. return (data[0] << 0) | (data[1] << 8);
  127. }
  128. static int GetLE24(const uint8_t* const data) {
  129. return GetLE16(data) | (data[2] << 16);
  130. }
  131. static uint32_t GetLE32(const uint8_t* const data) {
  132. return GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16);
  133. }
  134. static int ReadLE16(const uint8_t** data) {
  135. const int val = GetLE16(*data);
  136. *data += 2;
  137. return val;
  138. }
  139. static int ReadLE24(const uint8_t** data) {
  140. const int val = GetLE24(*data);
  141. *data += 3;
  142. return val;
  143. }
  144. static uint32_t ReadLE32(const uint8_t** data) {
  145. const uint32_t val = GetLE32(*data);
  146. *data += 4;
  147. return val;
  148. }
  149. static int ReadFileToWebPData(const char* const filename,
  150. WebPData* const webp_data) {
  151. const uint8_t* data;
  152. size_t size;
  153. if (!ImgIoUtilReadFile(filename, &data, &size)) return 0;
  154. webp_data->bytes = data;
  155. webp_data->size = size;
  156. return 1;
  157. }
  158. // -----------------------------------------------------------------------------
  159. // MemBuffer object.
  160. static void InitMemBuffer(MemBuffer* const mem, const WebPData* webp_data) {
  161. mem->buf_ = webp_data->bytes;
  162. mem->start_ = 0;
  163. mem->end_ = webp_data->size;
  164. }
  165. static size_t MemDataSize(const MemBuffer* const mem) {
  166. return (mem->end_ - mem->start_);
  167. }
  168. static const uint8_t* GetBuffer(MemBuffer* const mem) {
  169. return mem->buf_ + mem->start_;
  170. }
  171. static void Skip(MemBuffer* const mem, size_t size) {
  172. mem->start_ += size;
  173. }
  174. static uint32_t ReadMemBufLE32(MemBuffer* const mem) {
  175. const uint8_t* const data = mem->buf_ + mem->start_;
  176. const uint32_t val = GetLE32(data);
  177. assert(MemDataSize(mem) >= 4);
  178. Skip(mem, 4);
  179. return val;
  180. }
  181. // -----------------------------------------------------------------------------
  182. // Lossy bitstream analysis.
  183. static int GetBits(const uint8_t* const data, size_t data_size, size_t nb,
  184. int* val, uint64_t* const bit_pos) {
  185. *val = 0;
  186. while (nb-- > 0) {
  187. const uint64_t p = (*bit_pos)++;
  188. if ((p >> 3) >= data_size) {
  189. return 0;
  190. } else {
  191. const int bit = !!(data[p >> 3] & (128 >> ((p & 7))));
  192. *val = (*val << 1) | bit;
  193. }
  194. }
  195. return 1;
  196. }
  197. static int GetSignedBits(const uint8_t* const data, size_t data_size, size_t nb,
  198. int* val, uint64_t* const bit_pos) {
  199. int sign;
  200. if (!GetBits(data, data_size, nb, val, bit_pos)) return 0;
  201. if (!GetBits(data, data_size, 1, &sign, bit_pos)) return 0;
  202. if (sign) *val = -(*val);
  203. return 1;
  204. }
  205. #define GET_BITS(v, n) \
  206. do { \
  207. if (!GetBits(data, data_size, n, &(v), bit_pos)) { \
  208. LOG_ERROR("Truncated lossy bitstream."); \
  209. return WEBP_INFO_TRUNCATED_DATA; \
  210. } \
  211. } while (0)
  212. #define GET_SIGNED_BITS(v, n) \
  213. do { \
  214. if (!GetSignedBits(data, data_size, n, &(v), bit_pos)) { \
  215. LOG_ERROR("Truncated lossy bitstream."); \
  216. return WEBP_INFO_TRUNCATED_DATA; \
  217. } \
  218. } while (0)
  219. static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info,
  220. const uint8_t* const data,
  221. size_t data_size,
  222. uint64_t* const bit_pos) {
  223. int use_segment;
  224. GET_BITS(use_segment, 1);
  225. printf(" Use segment: %d\n", use_segment);
  226. if (use_segment) {
  227. int update_map, update_data;
  228. GET_BITS(update_map, 1);
  229. GET_BITS(update_data, 1);
  230. printf(" Update map: %d\n"
  231. " Update data: %d\n",
  232. update_map, update_data);
  233. if (update_data) {
  234. int i, a_delta;
  235. int quantizer[4] = {0, 0, 0, 0};
  236. int filter_strength[4] = {0, 0, 0, 0};
  237. GET_BITS(a_delta, 1);
  238. printf(" Absolute delta: %d\n", a_delta);
  239. for (i = 0; i < 4; ++i) {
  240. int bit;
  241. GET_BITS(bit, 1);
  242. if (bit) GET_SIGNED_BITS(quantizer[i], 7);
  243. }
  244. for (i = 0; i < 4; ++i) {
  245. int bit;
  246. GET_BITS(bit, 1);
  247. if (bit) GET_SIGNED_BITS(filter_strength[i], 6);
  248. }
  249. printf(" Quantizer: %d %d %d %d\n", quantizer[0], quantizer[1],
  250. quantizer[2], quantizer[3]);
  251. printf(" Filter strength: %d %d %d %d\n", filter_strength[0],
  252. filter_strength[1], filter_strength[2], filter_strength[3]);
  253. }
  254. if (update_map) {
  255. int i;
  256. int prob_segment[3] = {255, 255, 255};
  257. for (i = 0; i < 3; ++i) {
  258. int bit;
  259. GET_BITS(bit, 1);
  260. if (bit) GET_BITS(prob_segment[i], 8);
  261. }
  262. printf(" Prob segment: %d %d %d\n",
  263. prob_segment[0], prob_segment[1], prob_segment[2]);
  264. }
  265. }
  266. return WEBP_INFO_OK;
  267. }
  268. static WebPInfoStatus ParseLossyFilterHeader(const WebPInfo* const webp_info,
  269. const uint8_t* const data,
  270. size_t data_size,
  271. uint64_t* const bit_pos) {
  272. int simple_filter, level, sharpness, use_lf_delta;
  273. GET_BITS(simple_filter, 1);
  274. GET_BITS(level, 6);
  275. GET_BITS(sharpness, 3);
  276. GET_BITS(use_lf_delta, 1);
  277. printf(" Simple filter: %d\n", simple_filter);
  278. printf(" Level: %d\n", level);
  279. printf(" Sharpness: %d\n", sharpness);
  280. printf(" Use lf delta: %d\n", use_lf_delta);
  281. if (use_lf_delta) {
  282. int update;
  283. GET_BITS(update, 1);
  284. printf(" Update lf delta: %d\n", update);
  285. if (update) {
  286. int i;
  287. for (i = 0; i < 4 + 4; ++i) {
  288. int temp;
  289. GET_BITS(temp, 1);
  290. if (temp) GET_BITS(temp, 7);
  291. }
  292. }
  293. }
  294. return WEBP_INFO_OK;
  295. }
  296. static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
  297. const WebPInfo* const webp_info) {
  298. const uint8_t* data = chunk_data->payload_;
  299. size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
  300. const uint32_t bits = (uint32_t)data[0] | (data[1] << 8) | (data[2] << 16);
  301. const int key_frame = !(bits & 1);
  302. const int profile = (bits >> 1) & 7;
  303. const int display = (bits >> 4) & 1;
  304. const uint32_t partition0_length = (bits >> 5);
  305. WebPInfoStatus status = WEBP_INFO_OK;
  306. uint64_t bit_position = 0;
  307. uint64_t* const bit_pos = &bit_position;
  308. int colorspace, clamp_type;
  309. printf(" Parsing lossy bitstream...\n");
  310. // Calling WebPGetFeatures() in ProcessImageChunk() should ensure this.
  311. assert(chunk_data->size_ >= CHUNK_HEADER_SIZE + 10);
  312. if (profile > 3) {
  313. LOG_ERROR("Unknown profile.");
  314. return WEBP_INFO_BITSTREAM_ERROR;
  315. }
  316. if (!display) {
  317. LOG_ERROR("Frame is not displayable.");
  318. return WEBP_INFO_BITSTREAM_ERROR;
  319. }
  320. data += 3;
  321. data_size -= 3;
  322. printf(
  323. " Key frame: %s\n"
  324. " Profile: %d\n"
  325. " Display: Yes\n"
  326. " Part. 0 length: %d\n",
  327. key_frame ? "Yes" : "No", profile, partition0_length);
  328. if (key_frame) {
  329. if (!(data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a)) {
  330. LOG_ERROR("Invalid lossy bitstream signature.");
  331. return WEBP_INFO_BITSTREAM_ERROR;
  332. }
  333. printf(" Width: %d\n"
  334. " X scale: %d\n"
  335. " Height: %d\n"
  336. " Y scale: %d\n",
  337. ((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6,
  338. ((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6);
  339. data += 7;
  340. data_size -= 7;
  341. } else {
  342. LOG_ERROR("Non-keyframe detected in lossy bitstream.");
  343. return WEBP_INFO_BITSTREAM_ERROR;
  344. }
  345. if (partition0_length >= data_size) {
  346. LOG_ERROR("Bad partition length.");
  347. return WEBP_INFO_BITSTREAM_ERROR;
  348. }
  349. GET_BITS(colorspace, 1);
  350. GET_BITS(clamp_type, 1);
  351. printf(" Color space: %d\n", colorspace);
  352. printf(" Clamp type: %d\n", clamp_type);
  353. status = ParseLossySegmentHeader(webp_info, data, data_size, bit_pos);
  354. if (status != WEBP_INFO_OK) return status;
  355. status = ParseLossyFilterHeader(webp_info, data, data_size, bit_pos);
  356. if (status != WEBP_INFO_OK) return status;
  357. { // Partition number and size.
  358. const uint8_t* part_size = data + partition0_length;
  359. int num_parts, i;
  360. size_t part_data_size;
  361. GET_BITS(num_parts, 2);
  362. num_parts = 1 << num_parts;
  363. if ((int)(data_size - partition0_length) < (num_parts - 1) * 3) {
  364. LOG_ERROR("Truncated lossy bitstream.");
  365. return WEBP_INFO_TRUNCATED_DATA;
  366. }
  367. part_data_size = data_size - partition0_length - (num_parts - 1) * 3;
  368. printf(" Total partitions: %d\n", num_parts);
  369. for (i = 1; i < num_parts; ++i) {
  370. const size_t psize =
  371. part_size[0] | (part_size[1] << 8) | (part_size[2] << 16);
  372. if (psize > part_data_size) {
  373. LOG_ERROR("Truncated partition.");
  374. return WEBP_INFO_TRUNCATED_DATA;
  375. }
  376. printf(" Part. %d length: %d\n", i, (int)psize);
  377. part_data_size -= psize;
  378. part_size += 3;
  379. }
  380. }
  381. // Quantizer.
  382. {
  383. int base_q, bit;
  384. int dq_y1_dc = 0, dq_y2_dc = 0, dq_y2_ac = 0, dq_uv_dc = 0, dq_uv_ac = 0;
  385. GET_BITS(base_q, 7);
  386. GET_BITS(bit, 1);
  387. if (bit) GET_SIGNED_BITS(dq_y1_dc, 4);
  388. GET_BITS(bit, 1);
  389. if (bit) GET_SIGNED_BITS(dq_y2_dc, 4);
  390. GET_BITS(bit, 1);
  391. if (bit) GET_SIGNED_BITS(dq_y2_ac, 4);
  392. GET_BITS(bit, 1);
  393. if (bit) GET_SIGNED_BITS(dq_uv_dc, 4);
  394. GET_BITS(bit, 1);
  395. if (bit) GET_SIGNED_BITS(dq_uv_ac, 4);
  396. printf(" Base Q: %d\n", base_q);
  397. printf(" DQ Y1 DC: %d\n", dq_y1_dc);
  398. printf(" DQ Y2 DC: %d\n", dq_y2_dc);
  399. printf(" DQ Y2 AC: %d\n", dq_y2_ac);
  400. printf(" DQ UV DC: %d\n", dq_uv_dc);
  401. printf(" DQ UV AC: %d\n", dq_uv_ac);
  402. }
  403. if ((*bit_pos >> 3) >= partition0_length) {
  404. LOG_ERROR("Truncated lossy bitstream.");
  405. return WEBP_INFO_TRUNCATED_DATA;
  406. }
  407. return WEBP_INFO_OK;
  408. }
  409. // -----------------------------------------------------------------------------
  410. // Lossless bitstream analysis.
  411. static int LLGetBits(const uint8_t* const data, size_t data_size, size_t nb,
  412. int* val, uint64_t* const bit_pos) {
  413. uint32_t i = 0;
  414. *val = 0;
  415. while (i < nb) {
  416. const uint64_t p = (*bit_pos)++;
  417. if ((p >> 3) >= data_size) {
  418. return 0;
  419. } else {
  420. const int bit = !!(data[p >> 3] & (1 << ((p & 7))));
  421. *val = *val | (bit << i);
  422. ++i;
  423. }
  424. }
  425. return 1;
  426. }
  427. #define LL_GET_BITS(v, n) \
  428. do { \
  429. if (!LLGetBits(data, data_size, n, &(v), bit_pos)) { \
  430. LOG_ERROR("Truncated lossless bitstream."); \
  431. return WEBP_INFO_TRUNCATED_DATA; \
  432. } \
  433. } while (0)
  434. static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
  435. const uint8_t* const data,
  436. size_t data_size,
  437. uint64_t* const bit_pos) {
  438. int use_transform, block_size, n_colors;
  439. LL_GET_BITS(use_transform, 1);
  440. printf(" Use transform: %s\n", use_transform ? "Yes" : "No");
  441. if (use_transform) {
  442. int type;
  443. LL_GET_BITS(type, 2);
  444. printf(" 1st transform: %s (%d)\n", kLosslessTransforms[type], type);
  445. switch (type) {
  446. case PREDICTOR_TRANSFORM:
  447. case CROSS_COLOR_TRANSFORM:
  448. LL_GET_BITS(block_size, 3);
  449. block_size = 1 << (block_size + 2);
  450. printf(" Tran. block size: %d\n", block_size);
  451. break;
  452. case COLOR_INDEXING_TRANSFORM:
  453. LL_GET_BITS(n_colors, 8);
  454. n_colors += 1;
  455. printf(" No. of colors: %d\n", n_colors);
  456. break;
  457. default: break;
  458. }
  459. }
  460. return WEBP_INFO_OK;
  461. }
  462. static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data,
  463. WebPInfo* const webp_info) {
  464. const uint8_t* data = chunk_data->payload_;
  465. size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
  466. uint64_t bit_position = 0;
  467. uint64_t* const bit_pos = &bit_position;
  468. WebPInfoStatus status;
  469. printf(" Parsing lossless bitstream...\n");
  470. if (data_size < VP8L_FRAME_HEADER_SIZE) {
  471. LOG_ERROR("Truncated lossless bitstream.");
  472. return WEBP_INFO_TRUNCATED_DATA;
  473. }
  474. if (data[0] != VP8L_MAGIC_BYTE) {
  475. LOG_ERROR("Invalid lossless bitstream signature.");
  476. return WEBP_INFO_BITSTREAM_ERROR;
  477. }
  478. data += 1;
  479. data_size -= 1;
  480. {
  481. int width, height, has_alpha, version;
  482. LL_GET_BITS(width, 14);
  483. LL_GET_BITS(height, 14);
  484. LL_GET_BITS(has_alpha, 1);
  485. LL_GET_BITS(version, 3);
  486. width += 1;
  487. height += 1;
  488. printf(" Width: %d\n", width);
  489. printf(" Height: %d\n", height);
  490. printf(" Alpha: %d\n", has_alpha);
  491. printf(" Version: %d\n", version);
  492. }
  493. status = ParseLosslessTransform(webp_info, data, data_size, bit_pos);
  494. if (status != WEBP_INFO_OK) return status;
  495. return WEBP_INFO_OK;
  496. }
  497. static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data,
  498. WebPInfo* const webp_info) {
  499. const uint8_t* data = chunk_data->payload_;
  500. size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
  501. if (data_size <= ALPHA_HEADER_LEN) {
  502. LOG_ERROR("Truncated ALPH chunk.");
  503. return WEBP_INFO_TRUNCATED_DATA;
  504. }
  505. printf(" Parsing ALPH chunk...\n");
  506. {
  507. const int compression_method = (data[0] >> 0) & 0x03;
  508. const int filter = (data[0] >> 2) & 0x03;
  509. const int pre_processing = (data[0] >> 4) & 0x03;
  510. const int reserved_bits = (data[0] >> 6) & 0x03;
  511. printf(" Compression: %d\n", compression_method);
  512. printf(" Filter: %s (%d)\n",
  513. kAlphaFilterMethods[filter], filter);
  514. printf(" Pre-processing: %d\n", pre_processing);
  515. if (compression_method > ALPHA_LOSSLESS_COMPRESSION) {
  516. LOG_ERROR("Invalid Alpha compression method.");
  517. return WEBP_INFO_BITSTREAM_ERROR;
  518. }
  519. if (pre_processing > ALPHA_PREPROCESSED_LEVELS) {
  520. LOG_ERROR("Invalid Alpha pre-processing method.");
  521. return WEBP_INFO_BITSTREAM_ERROR;
  522. }
  523. if (reserved_bits != 0) {
  524. LOG_WARN("Reserved bits in ALPH chunk header are not all 0.");
  525. }
  526. data += ALPHA_HEADER_LEN;
  527. data_size -= ALPHA_HEADER_LEN;
  528. if (compression_method == ALPHA_LOSSLESS_COMPRESSION) {
  529. uint64_t bit_pos = 0;
  530. WebPInfoStatus status =
  531. ParseLosslessTransform(webp_info, data, data_size, &bit_pos);
  532. if (status != WEBP_INFO_OK) return status;
  533. }
  534. }
  535. return WEBP_INFO_OK;
  536. }
  537. // -----------------------------------------------------------------------------
  538. // Chunk parsing.
  539. static WebPInfoStatus ParseRIFFHeader(WebPInfo* const webp_info,
  540. MemBuffer* const mem) {
  541. const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
  542. size_t riff_size;
  543. if (MemDataSize(mem) < min_size) {
  544. LOG_ERROR("Truncated data detected when parsing RIFF header.");
  545. return WEBP_INFO_TRUNCATED_DATA;
  546. }
  547. if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
  548. memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
  549. LOG_ERROR("Corrupted RIFF header.");
  550. return WEBP_INFO_PARSE_ERROR;
  551. }
  552. riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
  553. if (riff_size < CHUNK_HEADER_SIZE) {
  554. LOG_ERROR("RIFF size is too small.");
  555. return WEBP_INFO_PARSE_ERROR;
  556. }
  557. if (riff_size > MAX_CHUNK_PAYLOAD) {
  558. LOG_ERROR("RIFF size is over limit.");
  559. return WEBP_INFO_PARSE_ERROR;
  560. }
  561. riff_size += CHUNK_HEADER_SIZE;
  562. if (!webp_info->quiet_) {
  563. printf("RIFF HEADER:\n");
  564. printf(" File size: %6d\n", (int)riff_size);
  565. }
  566. if (riff_size < mem->end_) {
  567. LOG_WARN("RIFF size is smaller than the file size.");
  568. mem->end_ = riff_size;
  569. } else if (riff_size > mem->end_) {
  570. LOG_ERROR("Truncated data detected when parsing RIFF payload.");
  571. return WEBP_INFO_TRUNCATED_DATA;
  572. }
  573. Skip(mem, RIFF_HEADER_SIZE);
  574. return WEBP_INFO_OK;
  575. }
  576. static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
  577. MemBuffer* const mem,
  578. ChunkData* const chunk_data) {
  579. memset(chunk_data, 0, sizeof(*chunk_data));
  580. if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
  581. LOG_ERROR("Truncated data detected when parsing chunk header.");
  582. return WEBP_INFO_TRUNCATED_DATA;
  583. } else {
  584. const size_t chunk_start_offset = mem->start_;
  585. const uint32_t fourcc = ReadMemBufLE32(mem);
  586. const uint32_t payload_size = ReadMemBufLE32(mem);
  587. const uint32_t payload_size_padded = payload_size + (payload_size & 1);
  588. const size_t chunk_size = CHUNK_HEADER_SIZE + payload_size_padded;
  589. int i;
  590. if (payload_size > MAX_CHUNK_PAYLOAD) {
  591. LOG_ERROR("Size of chunk payload is over limit.");
  592. return WEBP_INFO_INVALID_PARAM;
  593. }
  594. if (payload_size_padded > MemDataSize(mem)){
  595. LOG_ERROR("Truncated data detected when parsing chunk payload.");
  596. return WEBP_INFO_TRUNCATED_DATA;
  597. }
  598. for (i = 0; i < CHUNK_TYPES; ++i) {
  599. if (kWebPChunkTags[i] == fourcc) break;
  600. }
  601. chunk_data->offset_ = chunk_start_offset;
  602. chunk_data->size_ = chunk_size;
  603. chunk_data->id_ = (ChunkID)i;
  604. chunk_data->payload_ = GetBuffer(mem);
  605. if (chunk_data->id_ == CHUNK_ANMF) {
  606. if (payload_size != payload_size_padded) {
  607. LOG_ERROR("ANMF chunk size should always be even.");
  608. return WEBP_INFO_PARSE_ERROR;
  609. }
  610. // There are sub-chunks to be parsed in an ANMF chunk.
  611. Skip(mem, ANMF_CHUNK_SIZE);
  612. } else {
  613. Skip(mem, payload_size_padded);
  614. }
  615. return WEBP_INFO_OK;
  616. }
  617. }
  618. // -----------------------------------------------------------------------------
  619. // Chunk analysis.
  620. static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data,
  621. WebPInfo* const webp_info) {
  622. const uint8_t* data = chunk_data->payload_;
  623. if (webp_info->chunk_counts_[CHUNK_VP8] ||
  624. webp_info->chunk_counts_[CHUNK_VP8L] ||
  625. webp_info->chunk_counts_[CHUNK_VP8X]) {
  626. LOG_ERROR("Already seen a VP8/VP8L/VP8X chunk when parsing VP8X chunk.");
  627. return WEBP_INFO_PARSE_ERROR;
  628. }
  629. if (chunk_data->size_ != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
  630. LOG_ERROR("Corrupted VP8X chunk.");
  631. return WEBP_INFO_PARSE_ERROR;
  632. }
  633. ++webp_info->chunk_counts_[CHUNK_VP8X];
  634. webp_info->feature_flags_ = *data;
  635. data += 4;
  636. webp_info->canvas_width_ = 1 + ReadLE24(&data);
  637. webp_info->canvas_height_ = 1 + ReadLE24(&data);
  638. if (!webp_info->quiet_) {
  639. printf(" ICCP: %d\n Alpha: %d\n EXIF: %d\n XMP: %d\n Animation: %d\n",
  640. (webp_info->feature_flags_ & ICCP_FLAG) != 0,
  641. (webp_info->feature_flags_ & ALPHA_FLAG) != 0,
  642. (webp_info->feature_flags_ & EXIF_FLAG) != 0,
  643. (webp_info->feature_flags_ & XMP_FLAG) != 0,
  644. (webp_info->feature_flags_ & ANIMATION_FLAG) != 0);
  645. printf(" Canvas size %d x %d\n",
  646. webp_info->canvas_width_, webp_info->canvas_height_);
  647. }
  648. if (webp_info->canvas_width_ > MAX_CANVAS_SIZE) {
  649. LOG_WARN("Canvas width is out of range in VP8X chunk.");
  650. }
  651. if (webp_info->canvas_height_ > MAX_CANVAS_SIZE) {
  652. LOG_WARN("Canvas height is out of range in VP8X chunk.");
  653. }
  654. if ((uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
  655. MAX_IMAGE_AREA) {
  656. LOG_WARN("Canvas area is out of range in VP8X chunk.");
  657. }
  658. return WEBP_INFO_OK;
  659. }
  660. static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data,
  661. WebPInfo* const webp_info) {
  662. const uint8_t* data = chunk_data->payload_;
  663. if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
  664. LOG_ERROR("ANIM chunk detected before VP8X chunk.");
  665. return WEBP_INFO_PARSE_ERROR;
  666. }
  667. if (chunk_data->size_ != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
  668. LOG_ERROR("Corrupted ANIM chunk.");
  669. return WEBP_INFO_PARSE_ERROR;
  670. }
  671. webp_info->bgcolor_ = ReadLE32(&data);
  672. webp_info->loop_count_ = ReadLE16(&data);
  673. ++webp_info->chunk_counts_[CHUNK_ANIM];
  674. if (!webp_info->quiet_) {
  675. printf(" Background color:(ARGB) %02x %02x %02x %02x\n",
  676. (webp_info->bgcolor_ >> 24) & 0xff,
  677. (webp_info->bgcolor_ >> 16) & 0xff,
  678. (webp_info->bgcolor_ >> 8) & 0xff,
  679. webp_info->bgcolor_ & 0xff);
  680. printf(" Loop count : %d\n", webp_info->loop_count_);
  681. }
  682. if (webp_info->loop_count_ > MAX_LOOP_COUNT) {
  683. LOG_WARN("Loop count is out of range in ANIM chunk.");
  684. }
  685. return WEBP_INFO_OK;
  686. }
  687. static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
  688. WebPInfo* const webp_info) {
  689. const uint8_t* data = chunk_data->payload_;
  690. int offset_x, offset_y, width, height, duration, blend, dispose, temp;
  691. if (webp_info->is_processing_anim_frame_) {
  692. LOG_ERROR("ANMF chunk detected within another ANMF chunk.");
  693. return WEBP_INFO_PARSE_ERROR;
  694. }
  695. if (!webp_info->chunk_counts_[CHUNK_ANIM]) {
  696. LOG_ERROR("ANMF chunk detected before ANIM chunk.");
  697. return WEBP_INFO_PARSE_ERROR;
  698. }
  699. if (chunk_data->size_ <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) {
  700. LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
  701. return WEBP_INFO_TRUNCATED_DATA;
  702. }
  703. offset_x = 2 * ReadLE24(&data);
  704. offset_y = 2 * ReadLE24(&data);
  705. width = 1 + ReadLE24(&data);
  706. height = 1 + ReadLE24(&data);
  707. duration = ReadLE24(&data);
  708. temp = *data;
  709. dispose = temp & 1;
  710. blend = (temp >> 1) & 1;
  711. ++webp_info->chunk_counts_[CHUNK_ANMF];
  712. if (!webp_info->quiet_) {
  713. printf(" Offset_X: %d\n Offset_Y: %d\n Width: %d\n Height: %d\n"
  714. " Duration: %d\n Dispose: %d\n Blend: %d\n",
  715. offset_x, offset_y, width, height, duration, dispose, blend);
  716. }
  717. if (duration > MAX_DURATION) {
  718. LOG_ERROR("Invalid duration parameter in ANMF chunk.");
  719. return WEBP_INFO_INVALID_PARAM;
  720. }
  721. if (offset_x > MAX_POSITION_OFFSET || offset_y > MAX_POSITION_OFFSET) {
  722. LOG_ERROR("Invalid offset parameters in ANMF chunk.");
  723. return WEBP_INFO_INVALID_PARAM;
  724. }
  725. if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width_ ||
  726. (uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height_) {
  727. LOG_ERROR("Frame exceeds canvas in ANMF chunk.");
  728. return WEBP_INFO_INVALID_PARAM;
  729. }
  730. webp_info->is_processing_anim_frame_ = 1;
  731. webp_info->seen_alpha_subchunk_ = 0;
  732. webp_info->seen_image_subchunk_ = 0;
  733. webp_info->frame_width_ = width;
  734. webp_info->frame_height_ = height;
  735. webp_info->anim_frame_data_size_ =
  736. chunk_data->size_ - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE;
  737. return WEBP_INFO_OK;
  738. }
  739. static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data,
  740. WebPInfo* const webp_info) {
  741. const uint8_t* data = chunk_data->payload_ - CHUNK_HEADER_SIZE;
  742. WebPBitstreamFeatures features;
  743. const VP8StatusCode vp8_status =
  744. WebPGetFeatures(data, chunk_data->size_, &features);
  745. if (vp8_status != VP8_STATUS_OK) {
  746. LOG_ERROR("VP8/VP8L bitstream error.");
  747. return WEBP_INFO_BITSTREAM_ERROR;
  748. }
  749. if (!webp_info->quiet_) {
  750. assert(features.format >= 0 && features.format <= 2);
  751. printf(" Width: %d\n Height: %d\n Alpha: %d\n Animation: %d\n"
  752. " Format: %s (%d)\n",
  753. features.width, features.height, features.has_alpha,
  754. features.has_animation, kFormats[features.format], features.format);
  755. }
  756. if (webp_info->is_processing_anim_frame_) {
  757. ++webp_info->anmf_subchunk_counts_[chunk_data->id_ == CHUNK_VP8 ? 0 : 1];
  758. if (chunk_data->id_ == CHUNK_VP8L && webp_info->seen_alpha_subchunk_) {
  759. LOG_ERROR("Both VP8L and ALPH sub-chunks are present in an ANMF chunk.");
  760. return WEBP_INFO_PARSE_ERROR;
  761. }
  762. if (webp_info->frame_width_ != features.width ||
  763. webp_info->frame_height_ != features.height) {
  764. LOG_ERROR("Frame size in VP8/VP8L sub-chunk differs from ANMF header.");
  765. return WEBP_INFO_PARSE_ERROR;
  766. }
  767. if (webp_info->seen_image_subchunk_) {
  768. LOG_ERROR("Consecutive VP8/VP8L sub-chunks in an ANMF chunk.");
  769. return WEBP_INFO_PARSE_ERROR;
  770. }
  771. webp_info->seen_image_subchunk_ = 1;
  772. } else {
  773. if (webp_info->chunk_counts_[CHUNK_VP8] ||
  774. webp_info->chunk_counts_[CHUNK_VP8L]) {
  775. LOG_ERROR("Multiple VP8/VP8L chunks detected.");
  776. return WEBP_INFO_PARSE_ERROR;
  777. }
  778. if (chunk_data->id_ == CHUNK_VP8L &&
  779. webp_info->chunk_counts_[CHUNK_ALPHA]) {
  780. LOG_WARN("Both VP8L and ALPH chunks are detected.");
  781. }
  782. if (webp_info->chunk_counts_[CHUNK_ANIM] ||
  783. webp_info->chunk_counts_[CHUNK_ANMF]) {
  784. LOG_ERROR("VP8/VP8L chunk and ANIM/ANMF chunk are both detected.");
  785. return WEBP_INFO_PARSE_ERROR;
  786. }
  787. if (webp_info->chunk_counts_[CHUNK_VP8X]) {
  788. if (webp_info->canvas_width_ != features.width ||
  789. webp_info->canvas_height_ != features.height) {
  790. LOG_ERROR("Image size in VP8/VP8L chunk differs from VP8X chunk.");
  791. return WEBP_INFO_PARSE_ERROR;
  792. }
  793. } else {
  794. webp_info->canvas_width_ = features.width;
  795. webp_info->canvas_height_ = features.height;
  796. if (webp_info->canvas_width_ < 1 || webp_info->canvas_height_ < 1 ||
  797. webp_info->canvas_width_ > MAX_CANVAS_SIZE ||
  798. webp_info->canvas_height_ > MAX_CANVAS_SIZE ||
  799. (uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
  800. MAX_IMAGE_AREA) {
  801. LOG_WARN("Invalid parameters in VP8/VP8L chunk.");
  802. }
  803. }
  804. ++webp_info->chunk_counts_[chunk_data->id_];
  805. }
  806. ++webp_info->num_frames_;
  807. webp_info->has_alpha_ |= features.has_alpha;
  808. if (webp_info->parse_bitstream_) {
  809. const int is_lossy = (chunk_data->id_ == CHUNK_VP8);
  810. const WebPInfoStatus status =
  811. is_lossy ? ParseLossyHeader(chunk_data, webp_info)
  812. : ParseLosslessHeader(chunk_data, webp_info);
  813. if (status != WEBP_INFO_OK) return status;
  814. }
  815. return WEBP_INFO_OK;
  816. }
  817. static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data,
  818. WebPInfo* const webp_info) {
  819. if (webp_info->is_processing_anim_frame_) {
  820. ++webp_info->anmf_subchunk_counts_[2];
  821. if (webp_info->seen_alpha_subchunk_) {
  822. LOG_ERROR("Consecutive ALPH sub-chunks in an ANMF chunk.");
  823. return WEBP_INFO_PARSE_ERROR;
  824. }
  825. webp_info->seen_alpha_subchunk_ = 1;
  826. if (webp_info->seen_image_subchunk_) {
  827. LOG_ERROR("ALPHA sub-chunk detected after VP8 sub-chunk "
  828. "in an ANMF chunk.");
  829. return WEBP_INFO_PARSE_ERROR;
  830. }
  831. } else {
  832. if (webp_info->chunk_counts_[CHUNK_ANIM] ||
  833. webp_info->chunk_counts_[CHUNK_ANMF]) {
  834. LOG_ERROR("ALPHA chunk and ANIM/ANMF chunk are both detected.");
  835. return WEBP_INFO_PARSE_ERROR;
  836. }
  837. if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
  838. LOG_ERROR("ALPHA chunk detected before VP8X chunk.");
  839. return WEBP_INFO_PARSE_ERROR;
  840. }
  841. if (webp_info->chunk_counts_[CHUNK_VP8]) {
  842. LOG_ERROR("ALPHA chunk detected after VP8 chunk.");
  843. return WEBP_INFO_PARSE_ERROR;
  844. }
  845. if (webp_info->chunk_counts_[CHUNK_ALPHA]) {
  846. LOG_ERROR("Multiple ALPHA chunks detected.");
  847. return WEBP_INFO_PARSE_ERROR;
  848. }
  849. ++webp_info->chunk_counts_[CHUNK_ALPHA];
  850. }
  851. webp_info->has_alpha_ = 1;
  852. if (webp_info->parse_bitstream_) {
  853. const WebPInfoStatus status = ParseAlphaHeader(chunk_data, webp_info);
  854. if (status != WEBP_INFO_OK) return status;
  855. }
  856. return WEBP_INFO_OK;
  857. }
  858. static WebPInfoStatus ProcessICCPChunk(const ChunkData* const chunk_data,
  859. WebPInfo* const webp_info) {
  860. (void)chunk_data;
  861. if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
  862. LOG_ERROR("ICCP chunk detected before VP8X chunk.");
  863. return WEBP_INFO_PARSE_ERROR;
  864. }
  865. if (webp_info->chunk_counts_[CHUNK_VP8] ||
  866. webp_info->chunk_counts_[CHUNK_VP8L] ||
  867. webp_info->chunk_counts_[CHUNK_ANIM]) {
  868. LOG_ERROR("ICCP chunk detected after image data.");
  869. return WEBP_INFO_PARSE_ERROR;
  870. }
  871. ++webp_info->chunk_counts_[CHUNK_ICCP];
  872. return WEBP_INFO_OK;
  873. }
  874. static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
  875. WebPInfo* const webp_info) {
  876. WebPInfoStatus status = WEBP_INFO_OK;
  877. ChunkID id = chunk_data->id_;
  878. if (chunk_data->id_ == CHUNK_UNKNOWN) {
  879. char error_message[50];
  880. snprintf(error_message, 50, "Unknown chunk at offset %6d, length %6d",
  881. (int)chunk_data->offset_, (int)chunk_data->size_);
  882. LOG_WARN(error_message);
  883. } else {
  884. if (!webp_info->quiet_) {
  885. char tag[4];
  886. uint32_t fourcc = kWebPChunkTags[chunk_data->id_];
  887. #ifdef WORDS_BIGENDIAN
  888. fourcc = (fourcc >> 24) | ((fourcc >> 8) & 0xff00) |
  889. ((fourcc << 8) & 0xff0000) | (fourcc << 24);
  890. #endif
  891. memcpy(tag, &fourcc, sizeof(tag));
  892. printf("Chunk %c%c%c%c at offset %6d, length %6d\n",
  893. tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset_,
  894. (int)chunk_data->size_);
  895. }
  896. }
  897. switch (id) {
  898. case CHUNK_VP8:
  899. case CHUNK_VP8L:
  900. status = ProcessImageChunk(chunk_data, webp_info);
  901. break;
  902. case CHUNK_VP8X:
  903. status = ProcessVP8XChunk(chunk_data, webp_info);
  904. break;
  905. case CHUNK_ALPHA:
  906. status = ProcessALPHChunk(chunk_data, webp_info);
  907. break;
  908. case CHUNK_ANIM:
  909. status = ProcessANIMChunk(chunk_data, webp_info);
  910. break;
  911. case CHUNK_ANMF:
  912. status = ProcessANMFChunk(chunk_data, webp_info);
  913. break;
  914. case CHUNK_ICCP:
  915. status = ProcessICCPChunk(chunk_data, webp_info);
  916. break;
  917. case CHUNK_EXIF:
  918. case CHUNK_XMP:
  919. ++webp_info->chunk_counts_[id];
  920. break;
  921. case CHUNK_UNKNOWN:
  922. default:
  923. break;
  924. }
  925. if (webp_info->is_processing_anim_frame_ && id != CHUNK_ANMF) {
  926. if (webp_info->anim_frame_data_size_ == chunk_data->size_) {
  927. if (!webp_info->seen_image_subchunk_) {
  928. LOG_ERROR("No VP8/VP8L chunk detected in an ANMF chunk.");
  929. return WEBP_INFO_PARSE_ERROR;
  930. }
  931. webp_info->is_processing_anim_frame_ = 0;
  932. } else if (webp_info->anim_frame_data_size_ > chunk_data->size_) {
  933. webp_info->anim_frame_data_size_ -= chunk_data->size_;
  934. } else {
  935. LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
  936. return WEBP_INFO_TRUNCATED_DATA;
  937. }
  938. }
  939. return status;
  940. }
  941. static WebPInfoStatus Validate(WebPInfo* const webp_info) {
  942. if (webp_info->num_frames_ < 1) {
  943. LOG_ERROR("No image/frame detected.");
  944. return WEBP_INFO_MISSING_DATA;
  945. }
  946. if (webp_info->chunk_counts_[CHUNK_VP8X]) {
  947. const int iccp = !!(webp_info->feature_flags_ & ICCP_FLAG);
  948. const int exif = !!(webp_info->feature_flags_ & EXIF_FLAG);
  949. const int xmp = !!(webp_info->feature_flags_ & XMP_FLAG);
  950. const int animation = !!(webp_info->feature_flags_ & ANIMATION_FLAG);
  951. const int alpha = !!(webp_info->feature_flags_ & ALPHA_FLAG);
  952. if (!alpha && webp_info->has_alpha_) {
  953. LOG_ERROR("Unexpected alpha data detected.");
  954. return WEBP_INFO_PARSE_ERROR;
  955. }
  956. if (alpha && !webp_info->has_alpha_) {
  957. LOG_WARN("Alpha flag is set with no alpha data present.");
  958. }
  959. if (iccp && !webp_info->chunk_counts_[CHUNK_ICCP]) {
  960. LOG_ERROR("Missing ICCP chunk.");
  961. return WEBP_INFO_MISSING_DATA;
  962. }
  963. if (exif && !webp_info->chunk_counts_[CHUNK_EXIF]) {
  964. LOG_ERROR("Missing EXIF chunk.");
  965. return WEBP_INFO_MISSING_DATA;
  966. }
  967. if (xmp && !webp_info->chunk_counts_[CHUNK_XMP]) {
  968. LOG_ERROR("Missing XMP chunk.");
  969. return WEBP_INFO_MISSING_DATA;
  970. }
  971. if (!iccp && webp_info->chunk_counts_[CHUNK_ICCP]) {
  972. LOG_ERROR("Unexpected ICCP chunk detected.");
  973. return WEBP_INFO_PARSE_ERROR;
  974. }
  975. if (!exif && webp_info->chunk_counts_[CHUNK_EXIF]) {
  976. LOG_ERROR("Unexpected EXIF chunk detected.");
  977. return WEBP_INFO_PARSE_ERROR;
  978. }
  979. if (!xmp && webp_info->chunk_counts_[CHUNK_XMP]) {
  980. LOG_ERROR("Unexpected XMP chunk detected.");
  981. return WEBP_INFO_PARSE_ERROR;
  982. }
  983. // Incomplete animation frame.
  984. if (webp_info->is_processing_anim_frame_) return WEBP_INFO_MISSING_DATA;
  985. if (!animation && webp_info->num_frames_ > 1) {
  986. LOG_ERROR("More than 1 frame detected in non-animation file.");
  987. return WEBP_INFO_PARSE_ERROR;
  988. }
  989. if (animation && (!webp_info->chunk_counts_[CHUNK_ANIM] ||
  990. !webp_info->chunk_counts_[CHUNK_ANMF])) {
  991. LOG_ERROR("No ANIM/ANMF chunk detected in animation file.");
  992. return WEBP_INFO_PARSE_ERROR;
  993. }
  994. }
  995. return WEBP_INFO_OK;
  996. }
  997. static void ShowSummary(const WebPInfo* const webp_info) {
  998. int i;
  999. printf("Summary:\n");
  1000. printf("Number of frames: %d\n", webp_info->num_frames_);
  1001. printf("Chunk type : VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP "
  1002. "EXIF XMP\n");
  1003. printf("Chunk counts: ");
  1004. for (i = 0; i < CHUNK_TYPES; ++i) {
  1005. printf("%4d ", webp_info->chunk_counts_[i]);
  1006. if (i == CHUNK_ANMF) {
  1007. printf("%4d %4d %4d ",
  1008. webp_info->anmf_subchunk_counts_[0],
  1009. webp_info->anmf_subchunk_counts_[1],
  1010. webp_info->anmf_subchunk_counts_[2]);
  1011. }
  1012. }
  1013. printf("\n");
  1014. }
  1015. static WebPInfoStatus AnalyzeWebP(WebPInfo* const webp_info,
  1016. const WebPData* webp_data) {
  1017. ChunkData chunk_data;
  1018. MemBuffer mem_buffer;
  1019. WebPInfoStatus webp_info_status = WEBP_INFO_OK;
  1020. InitMemBuffer(&mem_buffer, webp_data);
  1021. webp_info_status = ParseRIFFHeader(webp_info, &mem_buffer);
  1022. if (webp_info_status != WEBP_INFO_OK) goto Error;
  1023. // Loop through all the chunks. Terminate immediately in case of error.
  1024. while (webp_info_status == WEBP_INFO_OK && MemDataSize(&mem_buffer) > 0) {
  1025. webp_info_status = ParseChunk(webp_info, &mem_buffer, &chunk_data);
  1026. if (webp_info_status != WEBP_INFO_OK) goto Error;
  1027. webp_info_status = ProcessChunk(&chunk_data, webp_info);
  1028. }
  1029. if (webp_info_status != WEBP_INFO_OK) goto Error;
  1030. if (webp_info->show_summary_) ShowSummary(webp_info);
  1031. // Final check.
  1032. webp_info_status = Validate(webp_info);
  1033. Error:
  1034. if (!webp_info->quiet_) {
  1035. if (webp_info_status == WEBP_INFO_OK) {
  1036. printf("No error detected.\n");
  1037. } else {
  1038. printf("Errors detected.\n");
  1039. }
  1040. if (webp_info->num_warnings_ > 0) {
  1041. printf("There were %d warning(s).\n", webp_info->num_warnings_);
  1042. }
  1043. }
  1044. return webp_info_status;
  1045. }
  1046. static void Help(void) {
  1047. printf("Usage: webpinfo [options] in_files\n"
  1048. "Note: there could be multiple input files;\n"
  1049. " options must come before input files.\n"
  1050. "Options:\n"
  1051. " -version ........... Print version number and exit.\n"
  1052. " -quiet ............. Do not show chunk parsing information.\n"
  1053. " -diag .............. Show parsing error diagnosis.\n"
  1054. " -summary ........... Show chunk stats summary.\n"
  1055. " -bitstream_info .... Parse bitstream header.\n");
  1056. }
  1057. int main(int argc, const char* argv[]) {
  1058. int c, quiet = 0, show_diag = 0, show_summary = 0;
  1059. int parse_bitstream = 0;
  1060. WebPInfoStatus webp_info_status = WEBP_INFO_OK;
  1061. WebPInfo webp_info;
  1062. INIT_WARGV(argc, argv);
  1063. if (argc == 1) {
  1064. Help();
  1065. FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
  1066. }
  1067. // Parse command-line input.
  1068. for (c = 1; c < argc; ++c) {
  1069. if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help") ||
  1070. !strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
  1071. Help();
  1072. FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
  1073. } else if (!strcmp(argv[c], "-quiet")) {
  1074. quiet = 1;
  1075. } else if (!strcmp(argv[c], "-diag")) {
  1076. show_diag = 1;
  1077. } else if (!strcmp(argv[c], "-summary")) {
  1078. show_summary = 1;
  1079. } else if (!strcmp(argv[c], "-bitstream_info")) {
  1080. parse_bitstream = 1;
  1081. } else if (!strcmp(argv[c], "-version")) {
  1082. const int version = WebPGetDecoderVersion();
  1083. printf("WebP Decoder version: %d.%d.%d\n",
  1084. (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
  1085. FREE_WARGV_AND_RETURN(0);
  1086. } else { // Assume the remaining are all input files.
  1087. break;
  1088. }
  1089. }
  1090. if (c == argc) {
  1091. Help();
  1092. FREE_WARGV_AND_RETURN(WEBP_INFO_INVALID_COMMAND);
  1093. }
  1094. // Process input files one by one.
  1095. for (; c < argc; ++c) {
  1096. WebPData webp_data;
  1097. const W_CHAR* in_file = NULL;
  1098. WebPInfoInit(&webp_info);
  1099. webp_info.quiet_ = quiet;
  1100. webp_info.show_diagnosis_ = show_diag;
  1101. webp_info.show_summary_ = show_summary;
  1102. webp_info.parse_bitstream_ = parse_bitstream;
  1103. in_file = GET_WARGV(argv, c);
  1104. if (in_file == NULL ||
  1105. !ReadFileToWebPData((const char*)in_file, &webp_data)) {
  1106. webp_info_status = WEBP_INFO_INVALID_COMMAND;
  1107. WFPRINTF(stderr, "Failed to open input file %s.\n", in_file);
  1108. continue;
  1109. }
  1110. if (!webp_info.quiet_) WPRINTF("File: %s\n", in_file);
  1111. webp_info_status = AnalyzeWebP(&webp_info, &webp_data);
  1112. WebPDataClear(&webp_data);
  1113. }
  1114. FREE_WARGV_AND_RETURN(webp_info_status);
  1115. }