blob: f05178a06d6cc00ab0e04d5b199ed3564a4e2c56 [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"
James Zern061263a2012-05-11 16:00:57 -070056#include "./example_util.h"
Urvang Joshia4f32ca2011-09-30 11:07:01 +053057
Urvang Joshia4f32ca2011-09-30 11:07:01 +053058//------------------------------------------------------------------------------
59// Config object to parse command-line arguments.
60
61typedef enum {
62 NIL_ACTION = 0,
63 ACTION_GET,
64 ACTION_SET,
65 ACTION_STRIP,
66 ACTION_INFO,
67 ACTION_HELP
68} ActionType;
69
70typedef enum {
71 NIL_SUBTYPE = 0,
72 SUBTYPE_FRM,
73 SUBTYPE_LOOP
74} FeatureSubType;
75
76typedef struct {
77 FeatureSubType subtype_;
78 const char* filename_;
79 const char* params_;
80} FeatureArg;
81
82typedef enum {
83 NIL_FEATURE = 0,
84 FEATURE_XMP,
85 FEATURE_ICCP,
86 FEATURE_FRM,
87 FEATURE_TILE
88} FeatureType;
89
90typedef struct {
91 FeatureType type_;
92 FeatureArg* args_;
93 int arg_count_;
94} Feature;
95
96typedef struct {
97 ActionType action_type_;
98 const char* input_;
99 const char* output_;
100 Feature feature_;
101} WebPMuxConfig;
102
103//------------------------------------------------------------------------------
104// Helper functions.
105
James Zern04e84cf2011-11-04 15:20:08 -0700106static int CountOccurrences(const char* arglist[], int list_length,
107 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530108 int i;
109 int num_occurences = 0;
110
111 for (i = 0; i < list_length; ++i) {
112 if (!strcmp(arglist[i], arg)) {
113 ++num_occurences;
114 }
115 }
116 return num_occurences;
117}
118
119static int IsNotCompatible(int count1, int count2) {
James Zern04e84cf2011-11-04 15:20:08 -0700120 return (count1 > 0) != (count2 > 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530121}
122
James Zern0f7820e2012-01-24 14:08:27 -0800123// Allocate necessary storage for dst then copy the contents of src.
124// Returns 1 on success.
125static int WebPDataCopy(const WebPData* const src, WebPData* const dst) {
126 if (src == NULL || dst == NULL) return 0;
127
128 memset(dst, 0, sizeof(*dst));
129 if (src->bytes_ != NULL && src->size_ != 0) {
130 dst->bytes_ = (uint8_t*)malloc(src->size_);
131 if (dst->bytes_ == NULL) return 0;
132 memcpy((void*)dst->bytes_, src->bytes_, src->size_);
133 dst->size_ = src->size_;
134 }
135 return 1;
136}
137
James Zerna0b27362012-01-27 17:39:47 -0800138#define RETURN_IF_ERROR(ERR_MSG) \
139 if (err != WEBP_MUX_OK) { \
140 fprintf(stderr, ERR_MSG); \
141 return err; \
142 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530143
James Zerna0b27362012-01-27 17:39:47 -0800144#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
145 if (err != WEBP_MUX_OK) { \
146 fprintf(stderr, ERR_MSG, FORMAT_STR); \
147 return err; \
148 }
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530149
James Zerna0b27362012-01-27 17:39:47 -0800150#define ERROR_GOTO1(ERR_MSG, LABEL) \
151 do { \
152 fprintf(stderr, ERR_MSG); \
153 ok = 0; \
154 goto LABEL; \
155 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530156
James Zerna0b27362012-01-27 17:39:47 -0800157#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
158 do { \
159 fprintf(stderr, ERR_MSG, FORMAT_STR); \
160 ok = 0; \
161 goto LABEL; \
162 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530163
James Zerna0b27362012-01-27 17:39:47 -0800164#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
165 do { \
166 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
167 ok = 0; \
168 goto LABEL; \
169 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530170
171static WebPMuxError DisplayInfo(const WebPMux* mux) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530172 uint32_t flag;
173
174 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
175 RETURN_IF_ERROR("Failed to retrieve features\n");
176
177 if (flag == 0) {
178 fprintf(stderr, "No features present.\n");
179 return err;
180 }
181
182 // Print the features present.
James Zern974aaff2012-01-24 12:46:46 -0800183 printf("Features present:");
184 if (flag & ANIMATION_FLAG) printf(" animation");
185 if (flag & TILE_FLAG) printf(" tiling");
186 if (flag & ICCP_FLAG) printf(" icc profile");
187 if (flag & META_FLAG) printf(" metadata");
188 if (flag & ALPHA_FLAG) printf(" transparency");
189 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530190
191 if (flag & ANIMATION_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800192 int nFrames;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530193 uint32_t loop_count;
194 err = WebPMuxGetLoopCount(mux, &loop_count);
195 RETURN_IF_ERROR("Failed to retrieve loop count\n");
James Zern974aaff2012-01-24 12:46:46 -0800196 printf("Loop Count : %d\n", loop_count);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530197
Urvang Joshifbdcb7e2012-06-11 12:24:45 +0530198 err = WebPMuxNumChunks(mux, WEBP_CHUNK_FRAME, &nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530199 RETURN_IF_ERROR("Failed to retrieve number of frames\n");
200
James Zern974aaff2012-01-24 12:46:46 -0800201 printf("Number of frames: %d\n", nFrames);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530202 if (nFrames > 0) {
203 int i;
James Zern974aaff2012-01-24 12:46:46 -0800204 printf("No.: x_offset y_offset duration image_size");
205 if (flag & ALPHA_FLAG) printf(" alpha_size");
206 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530207 for (i = 1; i <= nFrames; i++) {
James Zern974aaff2012-01-24 12:46:46 -0800208 uint32_t x_offset, y_offset, duration;
James Zerneec4b872012-01-07 12:44:01 -0800209 WebPData image, alpha;
210 err = WebPMuxGetFrame(mux, i, &image, &alpha,
Urvang Joshic398f592011-11-22 14:40:41 +0530211 &x_offset, &y_offset, &duration);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530212 RETURN_IF_ERROR2("Failed to retrieve frame#%d\n", i);
James Zern95667b82012-04-18 17:13:34 -0700213 printf("%3d: %8d %8d %8d %10zu",
James Zern974aaff2012-01-24 12:46:46 -0800214 i, x_offset, y_offset, duration, image.size_);
James Zern95667b82012-04-18 17:13:34 -0700215 if (flag & ALPHA_FLAG) printf(" %10zu", alpha.size_);
James Zern974aaff2012-01-24 12:46:46 -0800216 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530217 }
218 }
219 }
220
221 if (flag & TILE_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800222 int nTiles;
Urvang Joshifbdcb7e2012-06-11 12:24:45 +0530223 err = WebPMuxNumChunks(mux, WEBP_CHUNK_TILE, &nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530224 RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
225
James Zern974aaff2012-01-24 12:46:46 -0800226 printf("Number of tiles: %d\n", nTiles);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530227 if (nTiles > 0) {
228 int i;
James Zern974aaff2012-01-24 12:46:46 -0800229 printf("No.: x_offset y_offset image_size");
230 if (flag & ALPHA_FLAG) printf(" alpha_size");
231 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530232 for (i = 1; i <= nTiles; i++) {
James Zern0f7820e2012-01-24 14:08:27 -0800233 uint32_t x_offset, y_offset;
James Zerneec4b872012-01-07 12:44:01 -0800234 WebPData image, alpha;
235 err = WebPMuxGetTile(mux, i, &image, &alpha, &x_offset, &y_offset);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530236 RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
James Zern95667b82012-04-18 17:13:34 -0700237 printf("%3d: %8d %8d %10zu",
James Zern974aaff2012-01-24 12:46:46 -0800238 i, x_offset, y_offset, image.size_);
James Zern95667b82012-04-18 17:13:34 -0700239 if (flag & ALPHA_FLAG) printf(" %10zu", alpha.size_);
James Zern974aaff2012-01-24 12:46:46 -0800240 printf("\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530241 }
242 }
243 }
244
245 if (flag & ICCP_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800246 WebPData icc_profile;
247 err = WebPMuxGetColorProfile(mux, &icc_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530248 RETURN_IF_ERROR("Failed to retrieve the color profile\n");
James Zern95667b82012-04-18 17:13:34 -0700249 printf("Size of the color profile data: %zu\n", icc_profile.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530250 }
251
252 if (flag & META_FLAG) {
James Zerneec4b872012-01-07 12:44:01 -0800253 WebPData metadata;
254 err = WebPMuxGetMetadata(mux, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530255 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
James Zern95667b82012-04-18 17:13:34 -0700256 printf("Size of the XMP metadata: %zu\n", metadata.size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530257 }
258
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530259 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
James Zerneec4b872012-01-07 12:44:01 -0800260 WebPData image, alpha;
261 err = WebPMuxGetImage(mux, &image, &alpha);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530262 RETURN_IF_ERROR("Failed to retrieve the image\n");
James Zern95667b82012-04-18 17:13:34 -0700263 printf("Size of the alpha data: %zu\n", alpha.size_);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530264 }
265
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530266 return WEBP_MUX_OK;
267}
268
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700269static void PrintHelp(void) {
James Zern974aaff2012-01-24 12:46:46 -0800270 printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
271 printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
272 printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
273 printf(" webpmux -tile TILE_OPTIONS [-tile...] -o OUTPUT\n");
274 printf(" webpmux -frame FRAME_OPTIONS [-frame...]");
275 printf(" -loop LOOP_COUNT -o OUTPUT\n");
276 printf(" webpmux -info INPUT\n");
277 printf(" webpmux [-h|-help]\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530278
James Zern974aaff2012-01-24 12:46:46 -0800279 printf("\n");
280 printf("GET_OPTIONS:\n");
281 printf(" Extract relevant data.\n");
282 printf(" icc Get ICCP Color profile.\n");
283 printf(" xmp Get XMP metadata.\n");
284 printf(" tile n Get nth tile.\n");
285 printf(" frame n Get nth frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530286
James Zern974aaff2012-01-24 12:46:46 -0800287 printf("\n");
288 printf("SET_OPTIONS:\n");
289 printf(" Set color profile/metadata.\n");
290 printf(" icc Set ICC Color profile.\n");
291 printf(" xmp Set XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530292
James Zern974aaff2012-01-24 12:46:46 -0800293 printf("\n");
294 printf("STRIP_OPTIONS:\n");
295 printf(" Strip color profile/metadata.\n");
296 printf(" icc Strip ICCP color profile.\n");
297 printf(" xmp Strip XMP metadata.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530298
James Zern974aaff2012-01-24 12:46:46 -0800299 printf("\n");
300 printf("TILE_OPTIONS(i):\n");
301 printf(" Create tiled image.\n");
302 printf(" file_i +xi+yi\n");
303 printf(" where: 'file_i' is the i'th tile (webp format),\n");
304 printf(" 'xi','yi' specify the image offset for this tile.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530305
James Zern974aaff2012-01-24 12:46:46 -0800306 printf("\n");
307 printf("FRAME_OPTIONS(i):\n");
308 printf(" Create animation.\n");
309 printf(" file_i +xi+yi+di\n");
310 printf(" where: 'file_i' is the i'th animation frame (webp format),\n");
311 printf(" 'xi','yi' specify the image offset for this frame.\n");
312 printf(" 'di' is the pause duration before next frame.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530313
James Zern974aaff2012-01-24 12:46:46 -0800314 printf("\nINPUT & OUTPUT are in webp format.\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530315}
316
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530317static int ReadFileToWebPData(const char* const filename,
318 WebPData* const webp_data) {
319 const uint8_t* data;
320 size_t size;
321 if (!ExUtilReadFile(filename, &data, &size)) return 0;
322 webp_data->bytes_ = data;
323 webp_data->size_ = size;
324 return 1;
325}
326
James Zern061263a2012-05-11 16:00:57 -0700327static int CreateMux(const char* const filename, WebPMux** mux) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530328 WebPData bitstream;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530329 assert(mux != NULL);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530330 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530331 *mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530332 free((void*)bitstream.bytes_);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530333 if (*mux != NULL) return 1;
334 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530335 return 0;
336}
337
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530338static int ReadImage(const char* filename,
James Zern0f7820e2012-01-24 14:08:27 -0800339 WebPData* const image_ptr, WebPData* const alpha_ptr) {
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530340 WebPData bitstream, image, alpha;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530341 WebPMux* mux;
342 WebPMuxError err;
343 int ok = 0;
344
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530345 if (!ReadFileToWebPData(filename, &bitstream)) return 0;
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530346 mux = WebPMuxCreate(&bitstream, 1);
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530347 free((void*)bitstream.bytes_);
Urvang Joshi6d5c7972012-06-07 13:45:06 +0530348 if (mux == NULL) {
349 fprintf(stderr, "Failed to create mux object from file %s.\n", filename);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530350 return 0;
351 }
James Zerneec4b872012-01-07 12:44:01 -0800352 err = WebPMuxGetImage(mux, &image, &alpha);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530353 if (err == WEBP_MUX_OK) {
James Zern0f7820e2012-01-24 14:08:27 -0800354 ok = 1;
355 ok &= WebPDataCopy(&image, image_ptr);
356 ok &= WebPDataCopy(&alpha, alpha_ptr);
357 if (!ok) {
James Zern95667b82012-04-18 17:13:34 -0700358 fprintf(stderr, "Error allocating storage for image (%zu bytes) "
359 "and alpha (%zu bytes) data\n", image.size_, alpha.size_);
Urvang Joshif1df5582012-06-07 11:04:57 +0530360 WebPDataClear(image_ptr);
361 WebPDataClear(alpha_ptr);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530362 }
363 } else {
364 fprintf(stderr, "Failed to extract image data from file %s. Error: %d\n",
365 filename, err);
366 }
367 WebPMuxDelete(mux);
368 return ok;
369}
370
James Zern0f7820e2012-01-24 14:08:27 -0800371static int WriteData(const char* filename, const WebPData* const webpdata) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530372 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700373 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530374 if (!fout) {
375 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
376 return 0;
377 }
James Zern0f7820e2012-01-24 14:08:27 -0800378 if (fwrite(webpdata->bytes_, webpdata->size_, 1, fout) != 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530379 fprintf(stderr, "Error writing file %s!\n", filename);
380 } else {
James Zern95667b82012-04-18 17:13:34 -0700381 fprintf(stderr, "Saved file %s (%zu bytes)\n", filename, webpdata->size_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530382 ok = 1;
383 }
384 if (fout != stdout) fclose(fout);
385 return ok;
386}
387
388static int WriteWebP(WebPMux* const mux, const char* filename) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530389 int ok;
Urvang Joshif1df5582012-06-07 11:04:57 +0530390 WebPData webp_data;
391 const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530392 if (err != WEBP_MUX_OK) {
393 fprintf(stderr, "Error (%d) assembling the WebP file.\n", err);
394 return 0;
395 }
Urvang Joshif1df5582012-06-07 11:04:57 +0530396 ok = WriteData(filename, &webp_data);
397 WebPDataClear(&webp_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530398 return ok;
399}
400
401static int ParseFrameArgs(const char* args, uint32_t* x_offset,
402 uint32_t* y_offset, uint32_t* duration) {
403 return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
404}
405
406static int ParseTileArgs(const char* args, uint32_t* x_offset,
407 uint32_t* y_offset) {
408 return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
409}
410
411//------------------------------------------------------------------------------
412// Clean-up.
413
414static void DeleteConfig(WebPMuxConfig* config) {
415 if (config != NULL) {
416 free(config->feature_.args_);
417 free(config);
418 }
419}
420
421//------------------------------------------------------------------------------
422// Parsing.
423
424// Basic syntactic checks on the command-line arguments.
425// Returns 1 on valid, 0 otherwise.
426// Also fills up num_feature_args to be number of feature arguments given.
427// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
428static int ValidateCommandLine(int argc, const char* argv[],
429 int* num_feature_args) {
430 int num_frame_args;
431 int num_tile_args;
432 int num_loop_args;
433 int ok = 1;
434
435 assert(num_feature_args != NULL);
436 *num_feature_args = 0;
437
438 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700439 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530440 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
441 }
James Zern04e84cf2011-11-04 15:20:08 -0700442 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530443 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
444 }
James Zern04e84cf2011-11-04 15:20:08 -0700445 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530446 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
447 }
James Zern04e84cf2011-11-04 15:20:08 -0700448 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530449 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
450 }
James Zern04e84cf2011-11-04 15:20:08 -0700451 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530452 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
453 }
454
455 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700456 num_frame_args = CountOccurrences(argv, argc, "-frame");
457 num_tile_args = CountOccurrences(argv, argc, "-tile");
458 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530459
460 if (num_loop_args > 1) {
461 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
462 }
463
464 if (IsNotCompatible(num_frame_args, num_loop_args)) {
465 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
466 ErrValidate);
467 }
468 if (num_frame_args > 0 && num_tile_args > 0) {
469 ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
470 "\n", ErrValidate);
471 }
472
473 assert(ok == 1);
474 if (num_frame_args == 0 && num_tile_args == 0) {
475 // Single argument ('set' action for XMP or ICCP, OR a 'get' action).
476 *num_feature_args = 1;
477 } else {
478 // Multiple arguments ('set' action for animation or tiling).
479 if (num_frame_args > 0) {
480 *num_feature_args = num_frame_args + num_loop_args;
481 } else {
482 *num_feature_args = num_tile_args;
483 }
484 }
485
486 ErrValidate:
487 return ok;
488}
489
490#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
491
492#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
493
494#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
495 if (argc < i + (NUM)) { \
496 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
497 goto LABEL; \
498 }
499
500#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
501 if (argc != i + (NUM)) { \
502 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
503 goto LABEL; \
504 }
505
506// Parses command-line arguments to fill up config object. Also performs some
507// semantic checks.
508static int ParseCommandLine(int argc, const char* argv[],
509 WebPMuxConfig* config) {
510 int i = 0;
511 int feature_arg_index = 0;
512 int ok = 1;
513
514 while (i < argc) {
515 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700516 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530517 if (argv[i][0] == '-') { // One of the action types or output.
518 if (!strcmp(argv[i], "-set")) {
519 if (ACTION_IS_NIL) {
520 config->action_type_ = ACTION_SET;
521 } else {
522 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
523 }
524 ++i;
525 } else if (!strcmp(argv[i], "-get")) {
526 if (ACTION_IS_NIL) {
527 config->action_type_ = ACTION_GET;
528 } else {
529 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
530 }
531 ++i;
532 } else if (!strcmp(argv[i], "-strip")) {
533 if (ACTION_IS_NIL) {
534 config->action_type_ = ACTION_STRIP;
535 feature->arg_count_ = 0;
536 } else {
537 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
538 }
539 ++i;
540 } else if (!strcmp(argv[i], "-frame")) {
541 CHECK_NUM_ARGS_LESS(3, ErrParse);
542 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
543 config->action_type_ = ACTION_SET;
544 } else {
545 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
546 }
547 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
548 feature->type_ = FEATURE_FRM;
549 } else {
550 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
551 }
552 arg->subtype_ = SUBTYPE_FRM;
553 arg->filename_ = argv[i + 1];
554 arg->params_ = argv[i + 2];
555 ++feature_arg_index;
556 i += 3;
557 } else if (!strcmp(argv[i], "-loop")) {
558 CHECK_NUM_ARGS_LESS(2, ErrParse);
559 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
560 config->action_type_ = ACTION_SET;
561 } else {
562 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
563 }
564 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
565 feature->type_ = FEATURE_FRM;
566 } else {
567 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
568 }
569 arg->subtype_ = SUBTYPE_LOOP;
570 arg->params_ = argv[i + 1];
571 ++feature_arg_index;
572 i += 2;
573 } else if (!strcmp(argv[i], "-tile")) {
574 CHECK_NUM_ARGS_LESS(3, ErrParse);
575 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
576 config->action_type_ = ACTION_SET;
577 } else {
578 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
579 }
580 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
581 feature->type_ = FEATURE_TILE;
582 } else {
583 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
584 }
585 arg->filename_ = argv[i + 1];
586 arg->params_ = argv[i + 2];
587 ++feature_arg_index;
588 i += 3;
589 } else if (!strcmp(argv[i], "-o")) {
590 CHECK_NUM_ARGS_LESS(2, ErrParse);
591 config->output_ = argv[i + 1];
592 i += 2;
593 } else if (!strcmp(argv[i], "-info")) {
594 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
595 if (config->action_type_ != NIL_ACTION) {
596 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
597 } else {
598 config->action_type_ = ACTION_INFO;
599 feature->arg_count_ = 0;
600 config->input_ = argv[i + 1];
601 }
602 i += 2;
603 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
604 PrintHelp();
605 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800606 exit(0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530607 } else {
608 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
609 }
610 } else { // One of the feature types or input.
611 if (ACTION_IS_NIL) {
612 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
613 ErrParse);
614 }
615 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
616 if (FEATURETYPE_IS_NIL) {
617 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
618 FEATURE_XMP;
619 } else {
620 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
621 }
622 if (config->action_type_ == ACTION_SET) {
623 CHECK_NUM_ARGS_LESS(2, ErrParse);
624 arg->filename_ = argv[i + 1];
625 ++feature_arg_index;
626 i += 2;
627 } else {
628 ++i;
629 }
630 } else if ((!strcmp(argv[i], "frame") ||
631 !strcmp(argv[i], "tile")) &&
632 (config->action_type_ == ACTION_GET)) {
633 CHECK_NUM_ARGS_LESS(2, ErrParse);
634 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
635 FEATURE_TILE;
636 arg->params_ = argv[i + 1];
637 ++feature_arg_index;
638 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700639 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530640 if (config->input_ == NULL) {
641 config->input_ = argv[i];
642 } else {
643 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
644 argv[i], ErrParse);
645 }
646 ++i;
647 }
648 }
649 }
650 ErrParse:
651 return ok;
652}
653
James Zern04e84cf2011-11-04 15:20:08 -0700654// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530655static int ValidateConfig(WebPMuxConfig* config) {
656 int ok = 1;
657 Feature* const feature = &config->feature_;
658
659 // Action.
660 if (ACTION_IS_NIL) {
661 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
662 }
663
664 // Feature type.
665 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
666 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
667 }
668
669 // Input file.
670 if (config->input_ == NULL) {
671 if (config->action_type_ != ACTION_SET) {
672 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
673 } else if (feature->type_ != FEATURE_FRM &&
674 feature->type_ != FEATURE_TILE) {
675 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
676 }
677 }
678
679 // Output file.
680 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
681 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
682 }
683
684 ErrValidate2:
685 return ok;
686}
687
688// Create config object from command-line arguments.
689static int InitializeConfig(int argc, const char* argv[],
690 WebPMuxConfig** config) {
691 int num_feature_args = 0;
692 int ok = 1;
693
694 assert(config != NULL);
695 *config = NULL;
696
697 // Validate command-line arguments.
698 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
699 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
700 }
701
702 // Allocate memory.
703 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
704 if (*config == NULL) {
705 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
706 }
707 (*config)->feature_.arg_count_ = num_feature_args;
708 (*config)->feature_.args_ =
709 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
710 if ((*config)->feature_.args_ == NULL) {
711 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
712 }
713
714 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700715 if (!ParseCommandLine(argc, argv, *config) ||
716 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530717 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
718 }
719
720 Err1:
721 return ok;
722}
723
724#undef ACTION_IS_NIL
725#undef FEATURETYPE_IS_NIL
726#undef CHECK_NUM_ARGS_LESS
727#undef CHECK_NUM_ARGS_MORE
728
729//------------------------------------------------------------------------------
730// Processing.
731
James Zern04e84cf2011-11-04 15:20:08 -0700732static int GetFrameTile(const WebPMux* mux,
733 const WebPMuxConfig* config, int isFrame) {
James Zerneec4b872012-01-07 12:44:01 -0800734 WebPData image, alpha;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530735 uint32_t x_offset = 0;
736 uint32_t y_offset = 0;
737 uint32_t duration = 0;
738 WebPMuxError err = WEBP_MUX_OK;
739 WebPMux* mux_single = NULL;
740 long num = 0;
741 int ok = 1;
742
743 num = strtol(config->feature_.args_[0].params_, NULL, 10);
744 if (num < 0) {
745 ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
746 }
747
748 if (isFrame) {
James Zerneec4b872012-01-07 12:44:01 -0800749 err = WebPMuxGetFrame(mux, num, &image, &alpha,
Urvang Joshic398f592011-11-22 14:40:41 +0530750 &x_offset, &y_offset, &duration);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530751 if (err != WEBP_MUX_OK) {
752 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
753 }
754 } else {
James Zerneec4b872012-01-07 12:44:01 -0800755 err = WebPMuxGetTile(mux, num, &image, &alpha, &x_offset, &y_offset);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530756 if (err != WEBP_MUX_OK) {
757 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
758 }
759 }
760
761 mux_single = WebPMuxNew();
762 if (mux_single == NULL) {
763 err = WEBP_MUX_MEMORY_ERROR;
764 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n", err, ErrGet);
765 }
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530766 err = WebPMuxSetImage(mux_single, &image, &alpha, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530767 if (err != WEBP_MUX_OK) {
768 ERROR_GOTO2("ERROR#%d: Could not create single image mux object.\n", err,
769 ErrGet);
770 }
771 ok = WriteWebP(mux_single, config->output_);
772
773 ErrGet:
774 WebPMuxDelete(mux_single);
775 return ok;
776}
777
778// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700779static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530780 WebPMux* mux = NULL;
James Zerneec4b872012-01-07 12:44:01 -0800781 WebPData webpdata;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530782 WebPData metadata, color_profile;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530783 uint32_t x_offset = 0;
784 uint32_t y_offset = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530785 WebPMuxError err = WEBP_MUX_OK;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530786 int index = 0;
787 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700788 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530789
James Zern04e84cf2011-11-04 15:20:08 -0700790 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530791 case ACTION_GET:
James Zern061263a2012-05-11 16:00:57 -0700792 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530793 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700794 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530795 case FEATURE_FRM:
796 ok = GetFrameTile(mux, config, 1);
797 break;
798
799 case FEATURE_TILE:
800 ok = GetFrameTile(mux, config, 0);
801 break;
802
803 case FEATURE_ICCP:
James Zerneec4b872012-01-07 12:44:01 -0800804 err = WebPMuxGetColorProfile(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530805 if (err != WEBP_MUX_OK) {
806 ERROR_GOTO2("ERROR#%d: Could not get color profile.\n", err, Err2);
807 }
James Zern0f7820e2012-01-24 14:08:27 -0800808 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530809 break;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530810 case FEATURE_XMP:
James Zerneec4b872012-01-07 12:44:01 -0800811 err = WebPMuxGetMetadata(mux, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530812 if (err != WEBP_MUX_OK) {
813 ERROR_GOTO2("ERROR#%d: Could not get XMP metadata.\n", err, Err2);
814 }
James Zern0f7820e2012-01-24 14:08:27 -0800815 ok = WriteData(config->output_, &webpdata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530816 break;
817
818 default:
819 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
820 break;
821 }
822 break;
823
824 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700825 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530826 case FEATURE_FRM:
827 mux = WebPMuxNew();
828 if (mux == NULL) {
829 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
830 WEBP_MUX_MEMORY_ERROR, Err2);
831 }
832 for (index = 0; index < feature->arg_count_; ++index) {
833 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
James Zern0f7820e2012-01-24 14:08:27 -0800834 const long num = strtol(feature->args_[index].params_, NULL, 10);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530835 if (num < 0) {
836 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530837 }
James Zern0f7820e2012-01-24 14:08:27 -0800838 err = WebPMuxSetLoopCount(mux, num);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530839 if (err != WEBP_MUX_OK) {
840 ERROR_GOTO2("ERROR#%d: Could not set loop count.\n", err, Err2);
841 }
842 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
James Zern0f7820e2012-01-24 14:08:27 -0800843 WebPData image, alpha;
844 uint32_t duration;
845 ok = ReadImage(feature->args_[index].filename_, &image, &alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530846 if (!ok) goto Err2;
847 ok = ParseFrameArgs(feature->args_[index].params_,
848 &x_offset, &y_offset, &duration);
849 if (!ok) {
Urvang Joshif1df5582012-06-07 11:04:57 +0530850 WebPDataClear(&image);
851 WebPDataClear(&alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530852 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
853 }
Urvang Joshi814a0632012-06-05 20:36:21 +0530854 err = WebPMuxSetFrame(mux, 0, &image, &alpha,
Urvang Joshic398f592011-11-22 14:40:41 +0530855 x_offset, y_offset, duration, 1);
Urvang Joshif1df5582012-06-07 11:04:57 +0530856 WebPDataClear(&image);
857 WebPDataClear(&alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530858 if (err != WEBP_MUX_OK) {
859 ERROR_GOTO3("ERROR#%d: Could not add a frame at index %d.\n",
860 err, index, Err2);
861 }
862 } else {
863 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
864 }
865 }
866 break;
867
868 case FEATURE_TILE:
869 mux = WebPMuxNew();
870 if (mux == NULL) {
871 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
872 WEBP_MUX_MEMORY_ERROR, Err2);
873 }
874 for (index = 0; index < feature->arg_count_; ++index) {
James Zern0f7820e2012-01-24 14:08:27 -0800875 WebPData image, alpha;
876 ok = ReadImage(feature->args_[index].filename_, &image, &alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530877 if (!ok) goto Err2;
878 ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
879 &y_offset);
880 if (!ok) {
Urvang Joshif1df5582012-06-07 11:04:57 +0530881 WebPDataClear(&image);
882 WebPDataClear(&alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530883 ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
884 }
Urvang Joshi814a0632012-06-05 20:36:21 +0530885 err = WebPMuxSetTile(mux, 0, &image, &alpha, x_offset, y_offset, 1);
Urvang Joshif1df5582012-06-07 11:04:57 +0530886 WebPDataClear(&image);
887 WebPDataClear(&alpha);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530888 if (err != WEBP_MUX_OK) {
889 ERROR_GOTO3("ERROR#%d: Could not add a tile at index %d.\n",
890 err, index, Err2);
891 }
892 }
893 break;
894
895 case FEATURE_ICCP:
James Zern061263a2012-05-11 16:00:57 -0700896 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530897 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530898 ok = ReadFileToWebPData(feature->args_[0].filename_, &color_profile);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530899 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530900 err = WebPMuxSetColorProfile(mux, &color_profile, 1);
901 free((void*)color_profile.bytes_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530902 if (err != WEBP_MUX_OK) {
903 ERROR_GOTO2("ERROR#%d: Could not set color profile.\n", err, Err2);
904 }
905 break;
906
907 case FEATURE_XMP:
James Zern061263a2012-05-11 16:00:57 -0700908 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530909 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530910 ok = ReadFileToWebPData(feature->args_[0].filename_, &metadata);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530911 if (!ok) goto Err2;
Urvang Joshi4fc4a472012-06-05 14:20:45 +0530912 err = WebPMuxSetMetadata(mux, &metadata, 1);
913 free((void*)metadata.bytes_);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530914 if (err != WEBP_MUX_OK) {
915 ERROR_GOTO2("ERROR#%d: Could not set XMP metadata.\n", err, Err2);
916 }
917 break;
918
919 default:
920 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
921 break;
922 }
923 ok = WriteWebP(mux, config->output_);
924 break;
925
926 case ACTION_STRIP:
James Zern061263a2012-05-11 16:00:57 -0700927 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530928 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700929 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530930 case FEATURE_ICCP:
931 err = WebPMuxDeleteColorProfile(mux);
932 if (err != WEBP_MUX_OK) {
933 ERROR_GOTO2("ERROR#%d: Could not delete color profile.\n", err,
934 Err2);
935 }
936 break;
937 case FEATURE_XMP:
938 err = WebPMuxDeleteMetadata(mux);
939 if (err != WEBP_MUX_OK) {
940 ERROR_GOTO2("ERROR#%d: Could not delete XMP metadata.\n", err,
941 Err2);
942 }
943 break;
944 default:
945 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
946 break;
947 }
948 ok = WriteWebP(mux, config->output_);
949 break;
950
951 case ACTION_INFO:
James Zern061263a2012-05-11 16:00:57 -0700952 ok = CreateMux(config->input_, &mux);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530953 if (!ok) goto Err2;
954 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
955 break;
956
957 default:
958 assert(0); // Invalid action.
959 break;
960 }
961
962 Err2:
963 WebPMuxDelete(mux);
964 return ok;
965}
966
967//------------------------------------------------------------------------------
968// Main.
969
970int main(int argc, const char* argv[]) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530971 WebPMuxConfig* config;
James Zern974aaff2012-01-24 12:46:46 -0800972 int ok = InitializeConfig(argc - 1, argv + 1, &config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530973 if (ok) {
James Zern974aaff2012-01-24 12:46:46 -0800974 ok = Process(config);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530975 } else {
976 PrintHelp();
977 }
978 DeleteConfig(config);
James Zern974aaff2012-01-24 12:46:46 -0800979 return !ok;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530980}
981
982//------------------------------------------------------------------------------