blob: 7357fd54f5482f41dda9b7d29e48fbf4d2c25c11 [file] [log] [blame]
James Zernad1e1632012-01-06 14:49:06 -08001// Copyright 2011 Google Inc. All Rights Reserved.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05302//
3// This code is licensed under the same terms as WebM:
4// Software License Agreement: http://www.webmproject.org/license/software/
5// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Simple command-line to create a WebP container file and to extract or strip
9// relevant data from the container file.
10//
James Zern04e84cf2011-11-04 15:20:08 -070011// Compile with: gcc -o webpmux webpmux.c -lwebpmux -lwebp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053012//
13//
14// Authors: Vikas (vikaas.arora@gmail.com),
15// Urvang (urvang@google.com)
16
17/* Usage examples:
18
19 Create container WebP file:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053020 webpmux -tile tile_1.webp +0+0 \
21 -tile tile_2.webp +960+0 \
22 -tile tile_3.webp +0+576 \
23 -tile tile_4.webp +960+576 \
24 -o out_tile_container.webp
25
26 webpmux -frame anim_1.webp +0+0+0 \
27 -frame anim_2.webp +25+25+100 \
28 -frame anim_3.webp +50+50+100 \
29 -frame anim_4.webp +0+0+100 \
30 -loop 10 \
31 -o out_animation_container.webp
32
33 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053034 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
35
Urvang Joshia4f32ca2011-09-30 11:07:01 +053036 Extract relevant data from WebP container file:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053037 webpmux -get tile n in.webp -o out_tile.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053038 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053039 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshia4f32ca2011-09-30 11:07:01 +053040 webpmux -get xmp in.webp -o image_metadata.xmp
41
Urvang Joshia4f32ca2011-09-30 11:07:01 +053042 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070043 webpmux -strip icc in.webp -o out.webp
44 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053045
46 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053047 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070048 webpmux [ -h | -help ]
Urvang Joshia4f32ca2011-09-30 11:07:01 +053049*/
50
51#include <assert.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include "webp/mux.h"
56
Urvang Joshia4f32ca2011-09-30 11:07:01 +053057//------------------------------------------------------------------------------
58// Config object to parse command-line arguments.
59
60typedef enum {
61 NIL_ACTION = 0,
62 ACTION_GET,
63 ACTION_SET,
64 ACTION_STRIP,
65 ACTION_INFO,
66 ACTION_HELP
67} ActionType;
68
69typedef enum {
70 NIL_SUBTYPE = 0,
71 SUBTYPE_FRM,
72 SUBTYPE_LOOP
73} FeatureSubType;
74
75typedef struct {
76 FeatureSubType subtype_;
77 const char* filename_;
78 const char* params_;
79} FeatureArg;
80
81typedef enum {
82 NIL_FEATURE = 0,
83 FEATURE_XMP,
84 FEATURE_ICCP,
85 FEATURE_FRM,
86 FEATURE_TILE
87} FeatureType;
88
89typedef struct {
90 FeatureType type_;
91 FeatureArg* args_;
92 int arg_count_;
93} Feature;
94
95typedef struct {
96 ActionType action_type_;
97 const char* input_;
98 const char* output_;
99 Feature feature_;
100} WebPMuxConfig;
101
102//------------------------------------------------------------------------------
103// Helper functions.
104
James Zern04e84cf2011-11-04 15:20:08 -0700105static int CountOccurrences(const char* arglist[], int list_length,
106 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530107 int i;
108 int num_occurences = 0;
109
110 for (i = 0; i < list_length; ++i) {
111 if (!strcmp(arglist[i], arg)) {
112 ++num_occurences;
113 }
114 }
115 return num_occurences;
116}
117
118static int IsNotCompatible(int count1, int count2) {
James Zern04e84cf2011-11-04 15:20:08 -0700119 return (count1 > 0) != (count2 > 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530120}
121
James Zern0f7820e2012-01-24 14:08:27 -0800122// Allocate necessary storage for dst then copy the contents of src.
123// Returns 1 on success.
124static int WebPDataCopy(const WebPData* const src, WebPData* const dst) {
125 if (src == NULL || dst == NULL) return 0;
126
127 memset(dst, 0, sizeof(*dst));
128 if (src->bytes_ != NULL && src->size_ != 0) {
129 dst->bytes_ = (uint8_t*)malloc(src->size_);
130 if (dst->bytes_ == NULL) return 0;
131 memcpy((void*)dst->bytes_, src->bytes_, src->size_);
132 dst->size_ = src->size_;
133 }
134 return 1;
135}
136
137// Frees data allocated by WebPDataCopy.
138static void WebPDataFree(WebPData* const webpdata) {
139 free((void*)webpdata->bytes_);
140 memset(webpdata, 0, sizeof(*webpdata));
141}
142
James Zerna0b27362012-01-27 17:39:47 -0800143#define RETURN_IF_ERROR(ERR_MSG) \
144 if (err != WEBP_MUX_OK) { \
145 fprintf(stderr, ERR_MSG); \
146 return err; \
147 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530148
James Zerna0b27362012-01-27 17:39:47 -0800149#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
150 if (err != WEBP_MUX_OK) { \
151 fprintf(stderr, ERR_MSG, FORMAT_STR); \
152 return err; \
153 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530154
James Zerna0b27362012-01-27 17:39:47 -0800155#define ERROR_GOTO1(ERR_MSG, LABEL) \
156 do { \
157 fprintf(stderr, ERR_MSG); \
158 ok = 0; \
159 goto LABEL; \
160 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530161
James Zerna0b27362012-01-27 17:39:47 -0800162#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
163 do { \
164 fprintf(stderr, ERR_MSG, FORMAT_STR); \
165 ok = 0; \
166 goto LABEL; \
167 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530168
James Zerna0b27362012-01-27 17:39:47 -0800169#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
170 do { \
171 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
172 ok = 0; \
173 goto LABEL; \
174 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530175
176static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530177 uint32_t flag;
178
179 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
180 RETURN_IF_ERROR("Failed to retrieve features\n");
181
182 if (flag == 0) {
183 fprintf(stderr, "No features present.\n");
184 return err;
185 }
186
187 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800188 printf("Features present:");
189 if (flag & ANIMATION_FLAG) printf(" animation");
190 if (flag & TILE_FLAG) printf(" tiling");
191 if (flag & ICCP_FLAG) printf(" icc profile");
192 if (flag & META_FLAG) printf(" metadata");
193 if (flag & ALPHA_FLAG) printf(" transparency");
194 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530195
196 if (flag & ANIMATION_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800197 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530198 uint32_t loop_count;
199 err = WebPMuxGetLoopCount(mux, &loop_count);
200 RETURN_IF_ERROR("Failed to retrieve loop count\n");
James Zern974aaff2012-01-24 12:46:46 -0800201 printf("Loop Count : %d\n", loop_count);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530202
203 err = WebPMuxNumNamedElements(mux, "frame", &nFrames);
204 RETURN_IF_ERROR("Failed to retrieve number of frames\n");
205
James Zern974aaff2012-01-24 12:46:46 -0800206 printf("Number of frames: %d\n", nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530207 if (nFrames > 0) {
208 int i;
James Zern974aaff2012-01-24 12:46:46 -0800209 printf("No.: x_offset y_offset duration image_size");
210 if (flag & ALPHA_FLAG) printf(" alpha_size");
211 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530212 for (i = 1; i <= nFrames; i++) {
James Zern974aaff2012-01-24 12:46:46 -0800213 uint32_t x_offset, y_offset, duration;
James Zerneec4b872012-01-07 12:44:01 -0800214 WebPData image, alpha;
215 err = WebPMuxGetFrame(mux, i, &image, &alpha,
Urvang Joshic398f592011-11-22 14:40:41 +0530216 &x_offset, &y_offset, &duration);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530217 RETURN_IF_ERROR2("Failed to retrieve frame#%d\n", i);
James Zern974aaff2012-01-24 12:46:46 -0800218 printf("%3d: %8d %8d %8d %10u",
219 i, x_offset, y_offset, duration, image.size_);
220 if (flag & ALPHA_FLAG) printf(" %10u", alpha.size_);
221 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530222 }
223 }
224 }
225
226 if (flag & TILE_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800227 int nTiles;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530228 err = WebPMuxNumNamedElements(mux, "tile", &nTiles);
229 RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
230
James Zern974aaff2012-01-24 12:46:46 -0800231 printf("Number of tiles: %d\n", nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530232 if (nTiles > 0) {
233 int i;
James Zern974aaff2012-01-24 12:46:46 -0800234 printf("No.: x_offset y_offset image_size");
235 if (flag & ALPHA_FLAG) printf(" alpha_size");
236 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530237 for (i = 1; i <= nTiles; i++) {
James Zern0f7820e2012-01-24 14:08:27 -0800238 uint32_t x_offset, y_offset;
James Zerneec4b872012-01-07 12:44:01 -0800239 WebPData image, alpha;
240 err = WebPMuxGetTile(mux, i, &image, &alpha, &x_offset, &y_offset);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530241 RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
James Zern974aaff2012-01-24 12:46:46 -0800242 printf("%3d: %8d %8d %10u",
243 i, x_offset, y_offset, image.size_);
244 if (flag & ALPHA_FLAG) printf(" %10u", alpha.size_);
245 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530246 }
247 }
248 }
249
250 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800251 WebPData icc_profile;
252 err = WebPMuxGetColorProfile(mux, &icc_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530253 RETURN_IF_ERROR("Failed to retrieve the color profile\n");
James Zern974aaff2012-01-24 12:46:46 -0800254 printf("Size of the color profile data: %u\n", icc_profile.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530255 }
256
257 if (flag & META_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800258 WebPData metadata;
259 err = WebPMuxGetMetadata(mux, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530260 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern974aaff2012-01-24 12:46:46 -0800261 printf("Size of the XMP metadata: %u\n", metadata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530262 }
263
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530264 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
James Zerneec4b872012-01-07 12:44:01 -0800265 WebPData image, alpha;
266 err = WebPMuxGetImage(mux, &image, &alpha);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530267 RETURN_IF_ERROR("Failed to retrieve the image\n");
James Zern974aaff2012-01-24 12:46:46 -0800268 printf("Size of the alpha data: %u\n", alpha.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530269 }
270
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530271 return WEBP_MUX_OK;
272}
273
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700274static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800275 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
276 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
277 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
278 printf(" webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT\n");
279 printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
280 printf(" -loop LOOP_COUNT -o OUTPUT\n");
281 printf(" webpmux -info INPUT\n");
282 printf(" webpmux [-h|-help]\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530283
James Zern974aaff2012-01-24 12:46:46 -0800284 printf("\n");
285 printf("GET_OPTIONS:\n");
286 printf(" Extract relevant data.\n");
287 printf(" icc Get ICCP Color profile.\n");
288 printf(" xmp Get XMP metadata.\n");
289 printf(" tile n Get nth tile.\n");
290 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530291
James Zern974aaff2012-01-24 12:46:46 -0800292 printf("\n");
293 printf("SET_OPTIONS:\n");
294 printf(" Set color profile/metadata.\n");
295 printf(" icc Set ICC Color profile.\n");
296 printf(" xmp Set XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530297
James Zern974aaff2012-01-24 12:46:46 -0800298 printf("\n");
299 printf("STRIP_OPTIONS:\n");
300 printf(" Strip color profile/metadata.\n");
301 printf(" icc Strip ICCP color profile.\n");
302 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530303
James Zern974aaff2012-01-24 12:46:46 -0800304 printf("\n");
305 printf("TILE_OPTIONS(i):\n");
306 printf(" Create tiled image.\n");
307 printf(" file_i +xi+yi\n");
308 printf(" where: 'file_i' is the i'th tile (webp format),\n");
309 printf(" 'xi','yi' specify the image offset for this tile.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530310
James Zern974aaff2012-01-24 12:46:46 -0800311 printf("\n");
312 printf("FRAME_OPTIONS(i):\n");
313 printf(" Create animation.\n");
314 printf(" file_i +xi+yi+di\n");
315 printf(" where: 'file_i' is the i'th animation frame (webp format),\n");
316 printf(" 'xi','yi' specify the image offset for this frame.\n");
317 printf(" 'di' is the pause duration before next frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530318
James Zern974aaff2012-01-24 12:46:46 -0800319 printf("\nINPUT & OUTPUT are in webp format.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530320}
321
James Zern0f7820e2012-01-24 14:08:27 -0800322static int ReadData(const char* filename,
323 uint8_t** data_ptr, uint32_t* size_ptr) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530324 void* data = NULL;
325 long size = 0;
326 int ok = 0;
327 FILE* in;
328
329 *size_ptr = 0;
330 in = fopen(filename, "rb");
331 if (!in) {
332 fprintf(stderr, "Failed to open file %s\n", filename);
333 return 0;
334 }
335 fseek(in, 0, SEEK_END);
336 size = ftell(in);
337 fseek(in, 0, SEEK_SET);
338 if (size > 0xffffffffu) {
339 fprintf(stderr, "Size (%ld bytes) is out of range for file %s\n",
340 size, filename);
341 size = 0;
342 goto Err;
343 }
344 if (size < 0) {
345 size = 0;
346 goto Err;
347 }
348 data = malloc(size);
349 if (data) {
350 if (fread(data, size, 1, in) != 1) {
351 free(data);
352 data = NULL;
353 size = 0;
354 fprintf(stderr, "Failed to read %ld bytes from file %s\n",
355 size, filename);
356 goto Err;
357 }
358 ok = 1;
359 } else {
360 fprintf(stderr, "Failed to allocate %ld bytes for reading file %s\n",
361 size, filename);
362 size = 0;
363 }
364
365 Err:
366 if (in != stdin) fclose(in);
367 *size_ptr = (uint32_t)size;
James Zern0f7820e2012-01-24 14:08:27 -0800368 *data_ptr = (uint8_t*)data;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530369 return ok;
370}
371
372static int ReadFile(const char* const filename, WebPMux** mux) {
373 uint32_t size = 0;
James Zern0f7820e2012-01-24 14:08:27 -0800374 uint8_t* data = NULL;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530375 WebPMuxState mux_state;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530376
377 assert(mux != NULL);
378
379 if (!ReadData(filename, &data, &size)) return 0;
James Zern0f7820e2012-01-24 14:08:27 -0800380 *mux = WebPMuxCreate(data, size, 1, &mux_state);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530381 free(data);
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530382 if (*mux != NULL && mux_state == WEBP_MUX_STATE_COMPLETE) return 1;
383 fprintf(stderr, "Failed to create mux object from file %s. mux_state = %d.\n",
384 filename, mux_state);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530385 return 0;
386}
387
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530388static int ReadImage(const char* filename,
James Zern0f7820e2012-01-24 14:08:27 -0800389 WebPData* const image_ptr, WebPData* const alpha_ptr) {
390 uint8_t* data = NULL;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530391 uint32_t size = 0;
James Zerneec4b872012-01-07 12:44:01 -0800392 WebPData image, alpha;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530393 WebPMux* mux;
394 WebPMuxError err;
395 int ok = 0;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530396 WebPMuxState mux_state;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530397
398 if (!ReadData(filename, &data, &size)) return 0;
399
James Zern0f7820e2012-01-24 14:08:27 -0800400 mux = WebPMuxCreate(data, size, 1, &mux_state);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530401 free(data);
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530402 if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) {
403 fprintf(stderr,
404 "Failed to create mux object from file %s. mux_state = %d.\n",
405 filename, mux_state);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530406 return 0;
407 }
James Zerneec4b872012-01-07 12:44:01 -0800408 err = WebPMuxGetImage(mux, &image, &alpha);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530409 if (err == WEBP_MUX_OK) {
James Zern0f7820e2012-01-24 14:08:27 -0800410 ok = 1;
411 ok &= WebPDataCopy(&image, image_ptr);
412 ok &= WebPDataCopy(&alpha, alpha_ptr);
413 if (!ok) {
414 fprintf(stderr, "Error allocating storage for image (%u bytes) "
415 "and alpha (%u bytes) data\n", image.size_, alpha.size_);
416 WebPDataFree(image_ptr);
417 WebPDataFree(alpha_ptr);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530418 }
419 } else {
420 fprintf(stderr, "Failed to extract image data from file %s. Error: %d\n",
421 filename, err);
422 }
423 WebPMuxDelete(mux);
424 return ok;
425}
426
James Zern0f7820e2012-01-24 14:08:27 -0800427static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530428 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700429 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530430 if (!fout) {
431 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
432 return 0;
433 }
James Zern0f7820e2012-01-24 14:08:27 -0800434 if (fwrite(webpdata->bytes_, webpdata->size_, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530435 fprintf(stderr, "Error writing file %s!\n", filename);
436 } else {
James Zern0f7820e2012-01-24 14:08:27 -0800437 fprintf(stderr, "Saved file %s (%d bytes)\n", filename, webpdata->size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530438 ok = 1;
439 }
440 if (fout != stdout) fclose(fout);
441 return ok;
442}
443
444static int WriteWebP(WebPMux* const mux, const char* filename) {
James Zern0f7820e2012-01-24 14:08:27 -0800445 WebPData webpdata;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530446 int ok;
447
James Zern0f7820e2012-01-24 14:08:27 -0800448 const WebPMuxError err = WebPMuxAssemble(
449 mux, (uint8_t**)&webpdata.bytes_, &webpdata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530450 if (err != WEBP_MUX_OK) {
451 fprintf(stderr, "Error (%d) assembling the WebP file.\n", err);
452 return 0;
453 }
James Zern0f7820e2012-01-24 14:08:27 -0800454 ok = WriteData(filename, &webpdata);
455 WebPDataFree(&webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530456 return ok;
457}
458
459static int ParseFrameArgs(const char* args, uint32_t* x_offset,
460 uint32_t* y_offset, uint32_t* duration) {
461 return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
462}
463
464static int ParseTileArgs(const char* args, uint32_t* x_offset,
465 uint32_t* y_offset) {
466 return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
467}
468
469//------------------------------------------------------------------------------
470// Clean-up.
471
472static void DeleteConfig(WebPMuxConfig* config) {
473 if (config != NULL) {
474 free(config->feature_.args_);
475 free(config);
476 }
477}
478
479//------------------------------------------------------------------------------
480// Parsing.
481
482// Basic syntactic checks on the command-line arguments.
483// Returns 1 on valid, 0 otherwise.
484// Also fills up num_feature_args to be number of feature arguments given.
485// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
486static int ValidateCommandLine(int argc, const char* argv[],
487 int* num_feature_args) {
488 int num_frame_args;
489 int num_tile_args;
490 int num_loop_args;
491 int ok = 1;
492
493 assert(num_feature_args != NULL);
494 *num_feature_args = 0;
495
496 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700497 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530498 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
499 }
James Zern04e84cf2011-11-04 15:20:08 -0700500 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530501 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
502 }
James Zern04e84cf2011-11-04 15:20:08 -0700503 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530504 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
505 }
James Zern04e84cf2011-11-04 15:20:08 -0700506 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530507 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
508 }
James Zern04e84cf2011-11-04 15:20:08 -0700509 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530510 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
511 }
512
513 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700514 num_frame_args = CountOccurrences(argv, argc, "-frame");
515 num_tile_args = CountOccurrences(argv, argc, "-tile");
516 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530517
518 if (num_loop_args > 1) {
519 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
520 }
521
522 if (IsNotCompatible(num_frame_args, num_loop_args)) {
523 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
524 ErrValidate);
525 }
526 if (num_frame_args > 0 && num_tile_args > 0) {
527 ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
528 "\n", ErrValidate);
529 }
530
531 assert(ok == 1);
532 if (num_frame_args == 0 && num_tile_args == 0) {
533 // Single argument ('set' action for XMP or ICCP, OR a 'get' action).
534 *num_feature_args = 1;
535 } else {
536 // Multiple arguments ('set' action for animation or tiling).
537 if (num_frame_args > 0) {
538 *num_feature_args = num_frame_args + num_loop_args;
539 } else {
540 *num_feature_args = num_tile_args;
541 }
542 }
543
544 ErrValidate:
545 return ok;
546}
547
548#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
549
550#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
551
552#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
553 if (argc < i + (NUM)) { \
554 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
555 goto LABEL; \
556 }
557
558#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
559 if (argc != i + (NUM)) { \
560 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
561 goto LABEL; \
562 }
563
564// Parses command-line arguments to fill up config object. Also performs some
565// semantic checks.
566static int ParseCommandLine(int argc, const char* argv[],
567 WebPMuxConfig* config) {
568 int i = 0;
569 int feature_arg_index = 0;
570 int ok = 1;
571
572 while (i < argc) {
573 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700574 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530575 if (argv[i][0] == '-') { // One of the action types or output.
576 if (!strcmp(argv[i], "-set")) {
577 if (ACTION_IS_NIL) {
578 config->action_type_ = ACTION_SET;
579 } else {
580 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
581 }
582 ++i;
583 } else if (!strcmp(argv[i], "-get")) {
584 if (ACTION_IS_NIL) {
585 config->action_type_ = ACTION_GET;
586 } else {
587 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
588 }
589 ++i;
590 } else if (!strcmp(argv[i], "-strip")) {
591 if (ACTION_IS_NIL) {
592 config->action_type_ = ACTION_STRIP;
593 feature->arg_count_ = 0;
594 } else {
595 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
596 }
597 ++i;
598 } else if (!strcmp(argv[i], "-frame")) {
599 CHECK_NUM_ARGS_LESS(3, ErrParse);
600 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
601 config->action_type_ = ACTION_SET;
602 } else {
603 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
604 }
605 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
606 feature->type_ = FEATURE_FRM;
607 } else {
608 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
609 }
610 arg->subtype_ = SUBTYPE_FRM;
611 arg->filename_ = argv[i + 1];
612 arg->params_ = argv[i + 2];
613 ++feature_arg_index;
614 i += 3;
615 } else if (!strcmp(argv[i], "-loop")) {
616 CHECK_NUM_ARGS_LESS(2, ErrParse);
617 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
618 config->action_type_ = ACTION_SET;
619 } else {
620 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
621 }
622 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
623 feature->type_ = FEATURE_FRM;
624 } else {
625 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
626 }
627 arg->subtype_ = SUBTYPE_LOOP;
628 arg->params_ = argv[i + 1];
629 ++feature_arg_index;
630 i += 2;
631 } else if (!strcmp(argv[i], "-tile")) {
632 CHECK_NUM_ARGS_LESS(3, ErrParse);
633 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
634 config->action_type_ = ACTION_SET;
635 } else {
636 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
637 }
638 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
639 feature->type_ = FEATURE_TILE;
640 } else {
641 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
642 }
643 arg->filename_ = argv[i + 1];
644 arg->params_ = argv[i + 2];
645 ++feature_arg_index;
646 i += 3;
647 } else if (!strcmp(argv[i], "-o")) {
648 CHECK_NUM_ARGS_LESS(2, ErrParse);
649 config->output_ = argv[i + 1];
650 i += 2;
651 } else if (!strcmp(argv[i], "-info")) {
652 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
653 if (config->action_type_ != NIL_ACTION) {
654 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
655 } else {
656 config->action_type_ = ACTION_INFO;
657 feature->arg_count_ = 0;
658 config->input_ = argv[i + 1];
659 }
660 i += 2;
661 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
662 PrintHelp();
663 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800664 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530665 } else {
666 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
667 }
668 } else { // One of the feature types or input.
669 if (ACTION_IS_NIL) {
670 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
671 ErrParse);
672 }
673 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
674 if (FEATURETYPE_IS_NIL) {
675 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
676 FEATURE_XMP;
677 } else {
678 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
679 }
680 if (config->action_type_ == ACTION_SET) {
681 CHECK_NUM_ARGS_LESS(2, ErrParse);
682 arg->filename_ = argv[i + 1];
683 ++feature_arg_index;
684 i += 2;
685 } else {
686 ++i;
687 }
688 } else if ((!strcmp(argv[i], "frame") ||
689 !strcmp(argv[i], "tile")) &&
690 (config->action_type_ == ACTION_GET)) {
691 CHECK_NUM_ARGS_LESS(2, ErrParse);
692 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
693 FEATURE_TILE;
694 arg->params_ = argv[i + 1];
695 ++feature_arg_index;
696 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700697 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530698 if (config->input_ == NULL) {
699 config->input_ = argv[i];
700 } else {
701 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
702 argv[i], ErrParse);
703 }
704 ++i;
705 }
706 }
707 }
708 ErrParse:
709 return ok;
710}
711
James Zern04e84cf2011-11-04 15:20:08 -0700712// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530713static int ValidateConfig(WebPMuxConfig* config) {
714 int ok = 1;
715 Feature* const feature = &config->feature_;
716
717 // Action.
718 if (ACTION_IS_NIL) {
719 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
720 }
721
722 // Feature type.
723 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
724 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
725 }
726
727 // Input file.
728 if (config->input_ == NULL) {
729 if (config->action_type_ != ACTION_SET) {
730 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
731 } else if (feature->type_ != FEATURE_FRM &&
732 feature->type_ != FEATURE_TILE) {
733 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
734 }
735 }
736
737 // Output file.
738 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
739 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
740 }
741
742 ErrValidate2:
743 return ok;
744}
745
746// Create config object from command-line arguments.
747static int InitializeConfig(int argc, const char* argv[],
748 WebPMuxConfig** config) {
749 int num_feature_args = 0;
750 int ok = 1;
751
752 assert(config != NULL);
753 *config = NULL;
754
755 // Validate command-line arguments.
756 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
757 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
758 }
759
760 // Allocate memory.
761 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
762 if (*config == NULL) {
763 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
764 }
765 (*config)->feature_.arg_count_ = num_feature_args;
766 (*config)->feature_.args_ =
767 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
768 if ((*config)->feature_.args_ == NULL) {
769 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
770 }
771
772 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700773 if (!ParseCommandLine(argc, argv, *config) ||
774 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530775 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
776 }
777
778 Err1:
779 return ok;
780}
781
782#undef ACTION_IS_NIL
783#undef FEATURETYPE_IS_NIL
784#undef CHECK_NUM_ARGS_LESS
785#undef CHECK_NUM_ARGS_MORE
786
787//------------------------------------------------------------------------------
788// Processing.
789
James Zern04e84cf2011-11-04 15:20:08 -0700790static int GetFrameTile(const WebPMux* mux,
791 const WebPMuxConfig* config, int isFrame) {
James Zerneec4b872012-01-07 12:44:01 -0800792 WebPData image, alpha;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530793 uint32_t x_offset = 0;
794 uint32_t y_offset = 0;
795 uint32_t duration = 0;
796 WebPMuxError err = WEBP_MUX_OK;
797 WebPMux* mux_single = NULL;
798 long num = 0;
799 int ok = 1;
800
801 num = strtol(config->feature_.args_[0].params_, NULL, 10);
802 if (num < 0) {
803 ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
804 }
805
806 if (isFrame) {
James Zerneec4b872012-01-07 12:44:01 -0800807 err = WebPMuxGetFrame(mux, num, &image, &alpha,
Urvang Joshic398f592011-11-22 14:40:41 +0530808 &x_offset, &y_offset, &duration);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530809 if (err != WEBP_MUX_OK) {
810 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
811 }
812 } else {
James Zerneec4b872012-01-07 12:44:01 -0800813 err = WebPMuxGetTile(mux, num, &image, &alpha, &x_offset, &y_offset);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530814 if (err != WEBP_MUX_OK) {
815 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
816 }
817 }
818
819 mux_single = WebPMuxNew();
820 if (mux_single == NULL) {
821 err = WEBP_MUX_MEMORY_ERROR;
822 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n", err, ErrGet);
823 }
James Zerneec4b872012-01-07 12:44:01 -0800824 err = WebPMuxSetImage(mux_single, image.bytes_, image.size_,
825 alpha.bytes_, alpha.size_, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530826 if (err != WEBP_MUX_OK) {
827 ERROR_GOTO2("ERROR#%d: Could not create single image mux object.\n", err,
828 ErrGet);
829 }
830 ok = WriteWebP(mux_single, config->output_);
831
832 ErrGet:
833 WebPMuxDelete(mux_single);
834 return ok;
835}
836
837// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700838static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530839 WebPMux* mux = NULL;
James Zerneec4b872012-01-07 12:44:01 -0800840 WebPData webpdata;
James Zern0f7820e2012-01-24 14:08:27 -0800841 uint8_t* data = NULL;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530842 uint32_t size = 0;
843 uint32_t x_offset = 0;
844 uint32_t y_offset = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530845 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530846 int index = 0;
847 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700848 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530849
James Zern04e84cf2011-11-04 15:20:08 -0700850 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530851 case ACTION_GET:
852 ok = ReadFile(config->input_, &mux);
853 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700854 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530855 case FEATURE_FRM:
856 ok = GetFrameTile(mux, config, 1);
857 break;
858
859 case FEATURE_TILE:
860 ok = GetFrameTile(mux, config, 0);
861 break;
862
863 case FEATURE_ICCP:
James Zerneec4b872012-01-07 12:44:01 -0800864 err = WebPMuxGetColorProfile(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530865 if (err != WEBP_MUX_OK) {
866 ERROR_GOTO2("ERROR#%d: Could not get color profile.\n", err, Err2);
867 }
James Zern0f7820e2012-01-24 14:08:27 -0800868 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530869 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530870 case FEATURE_XMP:
James Zerneec4b872012-01-07 12:44:01 -0800871 err = WebPMuxGetMetadata(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530872 if (err != WEBP_MUX_OK) {
873 ERROR_GOTO2("ERROR#%d: Could not get XMP metadata.\n", err, Err2);
874 }
James Zern0f7820e2012-01-24 14:08:27 -0800875 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530876 break;
877
878 default:
879 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
880 break;
881 }
882 break;
883
884 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700885 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530886 case FEATURE_FRM:
887 mux = WebPMuxNew();
888 if (mux == NULL) {
889 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
890 WEBP_MUX_MEMORY_ERROR, Err2);
891 }
892 for (index = 0; index < feature->arg_count_; ++index) {
893 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
James Zern0f7820e2012-01-24 14:08:27 -0800894 const long num = strtol(feature->args_[index].params_, NULL, 10);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530895 if (num < 0) {
896 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530897 }
James Zern0f7820e2012-01-24 14:08:27 -0800898 err = WebPMuxSetLoopCount(mux, num);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530899 if (err != WEBP_MUX_OK) {
900 ERROR_GOTO2("ERROR#%d: Could not set loop count.\n", err, Err2);
901 }
902 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
James Zern0f7820e2012-01-24 14:08:27 -0800903 WebPData image, alpha;
904 uint32_t duration;
905 ok = ReadImage(feature->args_[index].filename_, &image, &alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530906 if (!ok) goto Err2;
907 ok = ParseFrameArgs(feature->args_[index].params_,
908 &x_offset, &y_offset, &duration);
909 if (!ok) {
James Zern0f7820e2012-01-24 14:08:27 -0800910 WebPDataFree(&image);
911 WebPDataFree(&alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530912 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
913 }
James Zern0f7820e2012-01-24 14:08:27 -0800914 err = WebPMuxAddFrame(mux, 0, image.bytes_, image.size_,
915 alpha.bytes_, alpha.size_,
Urvang Joshic398f592011-11-22 14:40:41 +0530916 x_offset, y_offset, duration, 1);
James Zern0f7820e2012-01-24 14:08:27 -0800917 WebPDataFree(&image);
918 WebPDataFree(&alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530919 if (err != WEBP_MUX_OK) {
920 ERROR_GOTO3("ERROR#%d: Could not add a frame at index %d.\n",
921 err, index, Err2);
922 }
923 } else {
924 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
925 }
926 }
927 break;
928
929 case FEATURE_TILE:
930 mux = WebPMuxNew();
931 if (mux == NULL) {
932 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
933 WEBP_MUX_MEMORY_ERROR, Err2);
934 }
935 for (index = 0; index < feature->arg_count_; ++index) {
James Zern0f7820e2012-01-24 14:08:27 -0800936 WebPData image, alpha;
937 ok = ReadImage(feature->args_[index].filename_, &image, &alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530938 if (!ok) goto Err2;
939 ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
940 &y_offset);
941 if (!ok) {
James Zern0f7820e2012-01-24 14:08:27 -0800942 WebPDataFree(&image);
943 WebPDataFree(&alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530944 ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
945 }
James Zern0f7820e2012-01-24 14:08:27 -0800946 err = WebPMuxAddTile(mux, 0, image.bytes_, image.size_,
947 alpha.bytes_, alpha.size_,
Urvang Joshic398f592011-11-22 14:40:41 +0530948 x_offset, y_offset, 1);
James Zern0f7820e2012-01-24 14:08:27 -0800949 WebPDataFree(&image);
950 WebPDataFree(&alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530951 if (err != WEBP_MUX_OK) {
952 ERROR_GOTO3("ERROR#%d: Could not add a tile at index %d.\n",
953 err, index, Err2);
954 }
955 }
956 break;
957
958 case FEATURE_ICCP:
959 ok = ReadFile(config->input_, &mux);
960 if (!ok) goto Err2;
James Zern0f7820e2012-01-24 14:08:27 -0800961 ok = ReadData(feature->args_[0].filename_, &data, &size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530962 if (!ok) goto Err2;
963 err = WebPMuxSetColorProfile(mux, data, size, 1);
964 free((void*)data);
965 if (err != WEBP_MUX_OK) {
966 ERROR_GOTO2("ERROR#%d: Could not set color profile.\n", err, Err2);
967 }
968 break;
969
970 case FEATURE_XMP:
971 ok = ReadFile(config->input_, &mux);
972 if (!ok) goto Err2;
James Zern0f7820e2012-01-24 14:08:27 -0800973 ok = ReadData(feature->args_[0].filename_, &data, &size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530974 if (!ok) goto Err2;
975 err = WebPMuxSetMetadata(mux, data, size, 1);
James Zern0f7820e2012-01-24 14:08:27 -0800976 free(data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530977 if (err != WEBP_MUX_OK) {
978 ERROR_GOTO2("ERROR#%d: Could not set XMP metadata.\n", err, Err2);
979 }
980 break;
981
982 default:
983 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
984 break;
985 }
986 ok = WriteWebP(mux, config->output_);
987 break;
988
989 case ACTION_STRIP:
990 ok = ReadFile(config->input_, &mux);
991 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700992 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530993 case FEATURE_ICCP:
994 err = WebPMuxDeleteColorProfile(mux);
995 if (err != WEBP_MUX_OK) {
996 ERROR_GOTO2("ERROR#%d: Could not delete color profile.\n", err,
997 Err2);
998 }
999 break;
1000 case FEATURE_XMP:
1001 err = WebPMuxDeleteMetadata(mux);
1002 if (err != WEBP_MUX_OK) {
1003 ERROR_GOTO2("ERROR#%d: Could not delete XMP metadata.\n", err,
1004 Err2);
1005 }
1006 break;
1007 default:
1008 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1009 break;
1010 }
1011 ok = WriteWebP(mux, config->output_);
1012 break;
1013
1014 case ACTION_INFO:
1015 ok = ReadFile(config->input_, &mux);
1016 if (!ok) goto Err2;
1017 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1018 break;
1019
1020 default:
1021 assert(0); // Invalid action.
1022 break;
1023 }
1024
1025 Err2:
1026 WebPMuxDelete(mux);
1027 return ok;
1028}
1029
1030//------------------------------------------------------------------------------
1031// Main.
1032
1033int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301034 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -08001035 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301036 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -08001037 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301038 } else {
1039 PrintHelp();
1040 }
1041 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -08001042 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +05301043}
1044
1045//------------------------------------------------------------------------------