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