1cb93a386Sopenharmony_ci// Copyright 2011 Google Inc. All Rights Reserved. 2cb93a386Sopenharmony_ci// 3cb93a386Sopenharmony_ci// Use of this source code is governed by a BSD-style license 4cb93a386Sopenharmony_ci// that can be found in the COPYING file in the root of the source 5cb93a386Sopenharmony_ci// tree. An additional intellectual property rights grant can be found 6cb93a386Sopenharmony_ci// in the file PATENTS. All contributing project authors may 7cb93a386Sopenharmony_ci// be found in the AUTHORS file in the root of the source tree. 8cb93a386Sopenharmony_ci// ----------------------------------------------------------------------------- 9cb93a386Sopenharmony_ci// 10cb93a386Sopenharmony_ci// Simple command-line to create a WebP container file and to extract or strip 11cb93a386Sopenharmony_ci// relevant data from the container file. 12cb93a386Sopenharmony_ci// 13cb93a386Sopenharmony_ci// Authors: Vikas (vikaas.arora@gmail.com), 14cb93a386Sopenharmony_ci// Urvang (urvang@google.com) 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ci/* Usage examples: 17cb93a386Sopenharmony_ci 18cb93a386Sopenharmony_ci Create container WebP file: 19cb93a386Sopenharmony_ci webpmux -frame anim_1.webp +100+10+10 \ 20cb93a386Sopenharmony_ci -frame anim_2.webp +100+25+25+1 \ 21cb93a386Sopenharmony_ci -frame anim_3.webp +100+50+50+1 \ 22cb93a386Sopenharmony_ci -frame anim_4.webp +100 \ 23cb93a386Sopenharmony_ci -loop 10 -bgcolor 128,255,255,255 \ 24cb93a386Sopenharmony_ci -o out_animation_container.webp 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp 27cb93a386Sopenharmony_ci webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp 28cb93a386Sopenharmony_ci webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp 29cb93a386Sopenharmony_ci webpmux -set loop 1 in.webp -o out_looped.webp 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_ci Extract relevant data from WebP container file: 32cb93a386Sopenharmony_ci webpmux -get frame n in.webp -o out_frame.webp 33cb93a386Sopenharmony_ci webpmux -get icc in.webp -o image_profile.icc 34cb93a386Sopenharmony_ci webpmux -get exif in.webp -o image_metadata.exif 35cb93a386Sopenharmony_ci webpmux -get xmp in.webp -o image_metadata.xmp 36cb93a386Sopenharmony_ci 37cb93a386Sopenharmony_ci Strip data from WebP Container file: 38cb93a386Sopenharmony_ci webpmux -strip icc in.webp -o out.webp 39cb93a386Sopenharmony_ci webpmux -strip exif in.webp -o out.webp 40cb93a386Sopenharmony_ci webpmux -strip xmp in.webp -o out.webp 41cb93a386Sopenharmony_ci 42cb93a386Sopenharmony_ci Change duration of frame intervals: 43cb93a386Sopenharmony_ci webpmux -duration 150 in.webp -o out.webp 44cb93a386Sopenharmony_ci webpmux -duration 33,2 in.webp -o out.webp 45cb93a386Sopenharmony_ci webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_ci Misc: 48cb93a386Sopenharmony_ci webpmux -info in.webp 49cb93a386Sopenharmony_ci webpmux [ -h | -help ] 50cb93a386Sopenharmony_ci webpmux -version 51cb93a386Sopenharmony_ci webpmux argument_file_name 52cb93a386Sopenharmony_ci*/ 53cb93a386Sopenharmony_ci 54cb93a386Sopenharmony_ci#ifdef HAVE_CONFIG_H 55cb93a386Sopenharmony_ci#include "webp/config.h" 56cb93a386Sopenharmony_ci#endif 57cb93a386Sopenharmony_ci 58cb93a386Sopenharmony_ci#include <assert.h> 59cb93a386Sopenharmony_ci#include <stdio.h> 60cb93a386Sopenharmony_ci#include <stdlib.h> 61cb93a386Sopenharmony_ci#include <string.h> 62cb93a386Sopenharmony_ci#include "webp/decode.h" 63cb93a386Sopenharmony_ci#include "webp/mux.h" 64cb93a386Sopenharmony_ci#include "../examples/example_util.h" 65cb93a386Sopenharmony_ci#include "../imageio/imageio_util.h" 66cb93a386Sopenharmony_ci#include "./unicode.h" 67cb93a386Sopenharmony_ci 68cb93a386Sopenharmony_ci//------------------------------------------------------------------------------ 69cb93a386Sopenharmony_ci// Config object to parse command-line arguments. 70cb93a386Sopenharmony_ci 71cb93a386Sopenharmony_citypedef enum { 72cb93a386Sopenharmony_ci NIL_ACTION = 0, 73cb93a386Sopenharmony_ci ACTION_GET, 74cb93a386Sopenharmony_ci ACTION_SET, 75cb93a386Sopenharmony_ci ACTION_STRIP, 76cb93a386Sopenharmony_ci ACTION_INFO, 77cb93a386Sopenharmony_ci ACTION_HELP, 78cb93a386Sopenharmony_ci ACTION_DURATION 79cb93a386Sopenharmony_ci} ActionType; 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_citypedef enum { 82cb93a386Sopenharmony_ci NIL_SUBTYPE = 0, 83cb93a386Sopenharmony_ci SUBTYPE_ANMF, 84cb93a386Sopenharmony_ci SUBTYPE_LOOP, 85cb93a386Sopenharmony_ci SUBTYPE_BGCOLOR 86cb93a386Sopenharmony_ci} FeatureSubType; 87cb93a386Sopenharmony_ci 88cb93a386Sopenharmony_citypedef struct { 89cb93a386Sopenharmony_ci FeatureSubType subtype_; 90cb93a386Sopenharmony_ci const char* filename_; 91cb93a386Sopenharmony_ci const char* params_; 92cb93a386Sopenharmony_ci} FeatureArg; 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_citypedef enum { 95cb93a386Sopenharmony_ci NIL_FEATURE = 0, 96cb93a386Sopenharmony_ci FEATURE_EXIF, 97cb93a386Sopenharmony_ci FEATURE_XMP, 98cb93a386Sopenharmony_ci FEATURE_ICCP, 99cb93a386Sopenharmony_ci FEATURE_ANMF, 100cb93a386Sopenharmony_ci FEATURE_DURATION, 101cb93a386Sopenharmony_ci FEATURE_LOOP, 102cb93a386Sopenharmony_ci LAST_FEATURE 103cb93a386Sopenharmony_ci} FeatureType; 104cb93a386Sopenharmony_ci 105cb93a386Sopenharmony_cistatic const char* const kFourccList[LAST_FEATURE] = { 106cb93a386Sopenharmony_ci NULL, "EXIF", "XMP ", "ICCP", "ANMF" 107cb93a386Sopenharmony_ci}; 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_cistatic const char* const kDescriptions[LAST_FEATURE] = { 110cb93a386Sopenharmony_ci NULL, "EXIF metadata", "XMP metadata", "ICC profile", 111cb93a386Sopenharmony_ci "Animation frame" 112cb93a386Sopenharmony_ci}; 113cb93a386Sopenharmony_ci 114cb93a386Sopenharmony_citypedef struct { 115cb93a386Sopenharmony_ci CommandLineArguments cmd_args_; 116cb93a386Sopenharmony_ci 117cb93a386Sopenharmony_ci ActionType action_type_; 118cb93a386Sopenharmony_ci const char* input_; 119cb93a386Sopenharmony_ci const char* output_; 120cb93a386Sopenharmony_ci FeatureType type_; 121cb93a386Sopenharmony_ci FeatureArg* args_; 122cb93a386Sopenharmony_ci int arg_count_; 123cb93a386Sopenharmony_ci} Config; 124cb93a386Sopenharmony_ci 125cb93a386Sopenharmony_ci//------------------------------------------------------------------------------ 126cb93a386Sopenharmony_ci// Helper functions. 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_cistatic int CountOccurrences(const CommandLineArguments* const args, 129cb93a386Sopenharmony_ci const char* const arg) { 130cb93a386Sopenharmony_ci int i; 131cb93a386Sopenharmony_ci int num_occurences = 0; 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci for (i = 0; i < args->argc_; ++i) { 134cb93a386Sopenharmony_ci if (!strcmp(args->argv_[i], arg)) { 135cb93a386Sopenharmony_ci ++num_occurences; 136cb93a386Sopenharmony_ci } 137cb93a386Sopenharmony_ci } 138cb93a386Sopenharmony_ci return num_occurences; 139cb93a386Sopenharmony_ci} 140cb93a386Sopenharmony_ci 141cb93a386Sopenharmony_cistatic const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = { 142cb93a386Sopenharmony_ci "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA", 143cb93a386Sopenharmony_ci "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA" 144cb93a386Sopenharmony_ci}; 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_cistatic const char* ErrorString(WebPMuxError err) { 147cb93a386Sopenharmony_ci assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA); 148cb93a386Sopenharmony_ci return kErrorMessages[-err]; 149cb93a386Sopenharmony_ci} 150cb93a386Sopenharmony_ci 151cb93a386Sopenharmony_ci#define RETURN_IF_ERROR(ERR_MSG) \ 152cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { \ 153cb93a386Sopenharmony_ci fprintf(stderr, ERR_MSG); \ 154cb93a386Sopenharmony_ci return err; \ 155cb93a386Sopenharmony_ci } 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_ci#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2) \ 158cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { \ 159cb93a386Sopenharmony_ci fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \ 160cb93a386Sopenharmony_ci return err; \ 161cb93a386Sopenharmony_ci } 162cb93a386Sopenharmony_ci 163cb93a386Sopenharmony_ci#define ERROR_GOTO1(ERR_MSG, LABEL) \ 164cb93a386Sopenharmony_ci do { \ 165cb93a386Sopenharmony_ci fprintf(stderr, ERR_MSG); \ 166cb93a386Sopenharmony_ci ok = 0; \ 167cb93a386Sopenharmony_ci goto LABEL; \ 168cb93a386Sopenharmony_ci } while (0) 169cb93a386Sopenharmony_ci 170cb93a386Sopenharmony_ci#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \ 171cb93a386Sopenharmony_ci do { \ 172cb93a386Sopenharmony_ci fprintf(stderr, ERR_MSG, FORMAT_STR); \ 173cb93a386Sopenharmony_ci ok = 0; \ 174cb93a386Sopenharmony_ci goto LABEL; \ 175cb93a386Sopenharmony_ci } while (0) 176cb93a386Sopenharmony_ci 177cb93a386Sopenharmony_ci#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \ 178cb93a386Sopenharmony_ci do { \ 179cb93a386Sopenharmony_ci fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \ 180cb93a386Sopenharmony_ci ok = 0; \ 181cb93a386Sopenharmony_ci goto LABEL; \ 182cb93a386Sopenharmony_ci } while (0) 183cb93a386Sopenharmony_ci 184cb93a386Sopenharmony_cistatic WebPMuxError DisplayInfo(const WebPMux* mux) { 185cb93a386Sopenharmony_ci int width, height; 186cb93a386Sopenharmony_ci uint32_t flag; 187cb93a386Sopenharmony_ci 188cb93a386Sopenharmony_ci WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height); 189cb93a386Sopenharmony_ci assert(err == WEBP_MUX_OK); // As WebPMuxCreate() was successful earlier. 190cb93a386Sopenharmony_ci printf("Canvas size: %d x %d\n", width, height); 191cb93a386Sopenharmony_ci 192cb93a386Sopenharmony_ci err = WebPMuxGetFeatures(mux, &flag); 193cb93a386Sopenharmony_ci RETURN_IF_ERROR("Failed to retrieve features\n"); 194cb93a386Sopenharmony_ci 195cb93a386Sopenharmony_ci if (flag == 0) { 196cb93a386Sopenharmony_ci printf("No features present.\n"); 197cb93a386Sopenharmony_ci return err; 198cb93a386Sopenharmony_ci } 199cb93a386Sopenharmony_ci 200cb93a386Sopenharmony_ci // Print the features present. 201cb93a386Sopenharmony_ci printf("Features present:"); 202cb93a386Sopenharmony_ci if (flag & ANIMATION_FLAG) printf(" animation"); 203cb93a386Sopenharmony_ci if (flag & ICCP_FLAG) printf(" ICC profile"); 204cb93a386Sopenharmony_ci if (flag & EXIF_FLAG) printf(" EXIF metadata"); 205cb93a386Sopenharmony_ci if (flag & XMP_FLAG) printf(" XMP metadata"); 206cb93a386Sopenharmony_ci if (flag & ALPHA_FLAG) printf(" transparency"); 207cb93a386Sopenharmony_ci printf("\n"); 208cb93a386Sopenharmony_ci 209cb93a386Sopenharmony_ci if (flag & ANIMATION_FLAG) { 210cb93a386Sopenharmony_ci const WebPChunkId id = WEBP_CHUNK_ANMF; 211cb93a386Sopenharmony_ci const char* const type_str = "frame"; 212cb93a386Sopenharmony_ci int nFrames; 213cb93a386Sopenharmony_ci 214cb93a386Sopenharmony_ci WebPMuxAnimParams params; 215cb93a386Sopenharmony_ci err = WebPMuxGetAnimationParams(mux, ¶ms); 216cb93a386Sopenharmony_ci assert(err == WEBP_MUX_OK); 217cb93a386Sopenharmony_ci printf("Background color : 0x%.8X Loop Count : %d\n", 218cb93a386Sopenharmony_ci params.bgcolor, params.loop_count); 219cb93a386Sopenharmony_ci 220cb93a386Sopenharmony_ci err = WebPMuxNumChunks(mux, id, &nFrames); 221cb93a386Sopenharmony_ci assert(err == WEBP_MUX_OK); 222cb93a386Sopenharmony_ci 223cb93a386Sopenharmony_ci printf("Number of %ss: %d\n", type_str, nFrames); 224cb93a386Sopenharmony_ci if (nFrames > 0) { 225cb93a386Sopenharmony_ci int i; 226cb93a386Sopenharmony_ci printf("No.: width height alpha x_offset y_offset "); 227cb93a386Sopenharmony_ci printf("duration dispose blend "); 228cb93a386Sopenharmony_ci printf("image_size compression\n"); 229cb93a386Sopenharmony_ci for (i = 1; i <= nFrames; i++) { 230cb93a386Sopenharmony_ci WebPMuxFrameInfo frame; 231cb93a386Sopenharmony_ci err = WebPMuxGetFrame(mux, i, &frame); 232cb93a386Sopenharmony_ci if (err == WEBP_MUX_OK) { 233cb93a386Sopenharmony_ci WebPBitstreamFeatures features; 234cb93a386Sopenharmony_ci const VP8StatusCode status = WebPGetFeatures( 235cb93a386Sopenharmony_ci frame.bitstream.bytes, frame.bitstream.size, &features); 236cb93a386Sopenharmony_ci assert(status == VP8_STATUS_OK); // Checked by WebPMuxCreate(). 237cb93a386Sopenharmony_ci (void)status; 238cb93a386Sopenharmony_ci printf("%3d: %5d %5d %5s %8d %8d ", i, features.width, 239cb93a386Sopenharmony_ci features.height, features.has_alpha ? "yes" : "no", 240cb93a386Sopenharmony_ci frame.x_offset, frame.y_offset); 241cb93a386Sopenharmony_ci { 242cb93a386Sopenharmony_ci const char* const dispose = 243cb93a386Sopenharmony_ci (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none" 244cb93a386Sopenharmony_ci : "background"; 245cb93a386Sopenharmony_ci const char* const blend = 246cb93a386Sopenharmony_ci (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no"; 247cb93a386Sopenharmony_ci printf("%8d %10s %5s ", frame.duration, dispose, blend); 248cb93a386Sopenharmony_ci } 249cb93a386Sopenharmony_ci printf("%10d %11s\n", (int)frame.bitstream.size, 250cb93a386Sopenharmony_ci (features.format == 1) ? "lossy" : 251cb93a386Sopenharmony_ci (features.format == 2) ? "lossless" : 252cb93a386Sopenharmony_ci "undefined"); 253cb93a386Sopenharmony_ci } 254cb93a386Sopenharmony_ci WebPDataClear(&frame.bitstream); 255cb93a386Sopenharmony_ci RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i); 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci } 258cb93a386Sopenharmony_ci } 259cb93a386Sopenharmony_ci 260cb93a386Sopenharmony_ci if (flag & ICCP_FLAG) { 261cb93a386Sopenharmony_ci WebPData icc_profile; 262cb93a386Sopenharmony_ci err = WebPMuxGetChunk(mux, "ICCP", &icc_profile); 263cb93a386Sopenharmony_ci assert(err == WEBP_MUX_OK); 264cb93a386Sopenharmony_ci printf("Size of the ICC profile data: %d\n", (int)icc_profile.size); 265cb93a386Sopenharmony_ci } 266cb93a386Sopenharmony_ci 267cb93a386Sopenharmony_ci if (flag & EXIF_FLAG) { 268cb93a386Sopenharmony_ci WebPData exif; 269cb93a386Sopenharmony_ci err = WebPMuxGetChunk(mux, "EXIF", &exif); 270cb93a386Sopenharmony_ci assert(err == WEBP_MUX_OK); 271cb93a386Sopenharmony_ci printf("Size of the EXIF metadata: %d\n", (int)exif.size); 272cb93a386Sopenharmony_ci } 273cb93a386Sopenharmony_ci 274cb93a386Sopenharmony_ci if (flag & XMP_FLAG) { 275cb93a386Sopenharmony_ci WebPData xmp; 276cb93a386Sopenharmony_ci err = WebPMuxGetChunk(mux, "XMP ", &xmp); 277cb93a386Sopenharmony_ci assert(err == WEBP_MUX_OK); 278cb93a386Sopenharmony_ci printf("Size of the XMP metadata: %d\n", (int)xmp.size); 279cb93a386Sopenharmony_ci } 280cb93a386Sopenharmony_ci 281cb93a386Sopenharmony_ci if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) { 282cb93a386Sopenharmony_ci WebPMuxFrameInfo image; 283cb93a386Sopenharmony_ci err = WebPMuxGetFrame(mux, 1, &image); 284cb93a386Sopenharmony_ci if (err == WEBP_MUX_OK) { 285cb93a386Sopenharmony_ci printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size); 286cb93a386Sopenharmony_ci } 287cb93a386Sopenharmony_ci WebPDataClear(&image.bitstream); 288cb93a386Sopenharmony_ci RETURN_IF_ERROR("Failed to retrieve the image\n"); 289cb93a386Sopenharmony_ci } 290cb93a386Sopenharmony_ci 291cb93a386Sopenharmony_ci return WEBP_MUX_OK; 292cb93a386Sopenharmony_ci} 293cb93a386Sopenharmony_ci 294cb93a386Sopenharmony_cistatic void PrintHelp(void) { 295cb93a386Sopenharmony_ci printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n"); 296cb93a386Sopenharmony_ci printf(" webpmux -set SET_OPTIONS INPUT -o OUTPUT\n"); 297cb93a386Sopenharmony_ci printf(" webpmux -duration DURATION_OPTIONS [-duration ...]\n"); 298cb93a386Sopenharmony_ci printf(" INPUT -o OUTPUT\n"); 299cb93a386Sopenharmony_ci printf(" webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n"); 300cb93a386Sopenharmony_ci printf(" webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]" 301cb93a386Sopenharmony_ci "\n"); 302cb93a386Sopenharmony_ci printf(" [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n"); 303cb93a386Sopenharmony_ci printf(" webpmux -info INPUT\n"); 304cb93a386Sopenharmony_ci printf(" webpmux [-h|-help]\n"); 305cb93a386Sopenharmony_ci printf(" webpmux -version\n"); 306cb93a386Sopenharmony_ci printf(" webpmux argument_file_name\n"); 307cb93a386Sopenharmony_ci 308cb93a386Sopenharmony_ci printf("\n"); 309cb93a386Sopenharmony_ci printf("GET_OPTIONS:\n"); 310cb93a386Sopenharmony_ci printf(" Extract relevant data:\n"); 311cb93a386Sopenharmony_ci printf(" icc get ICC profile\n"); 312cb93a386Sopenharmony_ci printf(" exif get EXIF metadata\n"); 313cb93a386Sopenharmony_ci printf(" xmp get XMP metadata\n"); 314cb93a386Sopenharmony_ci printf(" frame n get nth frame\n"); 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci printf("\n"); 317cb93a386Sopenharmony_ci printf("SET_OPTIONS:\n"); 318cb93a386Sopenharmony_ci printf(" Set color profile/metadata:\n"); 319cb93a386Sopenharmony_ci printf(" loop LOOP_COUNT set the loop count\n"); 320cb93a386Sopenharmony_ci printf(" icc file.icc set ICC profile\n"); 321cb93a386Sopenharmony_ci printf(" exif file.exif set EXIF metadata\n"); 322cb93a386Sopenharmony_ci printf(" xmp file.xmp set XMP metadata\n"); 323cb93a386Sopenharmony_ci printf(" where: 'file.icc' contains the ICC profile to be set,\n"); 324cb93a386Sopenharmony_ci printf(" 'file.exif' contains the EXIF metadata to be set\n"); 325cb93a386Sopenharmony_ci printf(" 'file.xmp' contains the XMP metadata to be set\n"); 326cb93a386Sopenharmony_ci 327cb93a386Sopenharmony_ci printf("\n"); 328cb93a386Sopenharmony_ci printf("DURATION_OPTIONS:\n"); 329cb93a386Sopenharmony_ci printf(" Set duration of selected frames:\n"); 330cb93a386Sopenharmony_ci printf(" duration set duration for each frames\n"); 331cb93a386Sopenharmony_ci printf(" duration,frame set duration of a particular frame\n"); 332cb93a386Sopenharmony_ci printf(" duration,start,end set duration of frames in the\n"); 333cb93a386Sopenharmony_ci printf(" interval [start,end])\n"); 334cb93a386Sopenharmony_ci printf(" where: 'duration' is the duration in milliseconds\n"); 335cb93a386Sopenharmony_ci printf(" 'start' is the start frame index\n"); 336cb93a386Sopenharmony_ci printf(" 'end' is the inclusive end frame index\n"); 337cb93a386Sopenharmony_ci printf(" The special 'end' value '0' means: last frame.\n"); 338cb93a386Sopenharmony_ci 339cb93a386Sopenharmony_ci printf("\n"); 340cb93a386Sopenharmony_ci printf("STRIP_OPTIONS:\n"); 341cb93a386Sopenharmony_ci printf(" Strip color profile/metadata:\n"); 342cb93a386Sopenharmony_ci printf(" icc strip ICC profile\n"); 343cb93a386Sopenharmony_ci printf(" exif strip EXIF metadata\n"); 344cb93a386Sopenharmony_ci printf(" xmp strip XMP metadata\n"); 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_ci printf("\n"); 347cb93a386Sopenharmony_ci printf("FRAME_OPTIONS(i):\n"); 348cb93a386Sopenharmony_ci printf(" Create animation:\n"); 349cb93a386Sopenharmony_ci printf(" file_i +di+[xi+yi[+mi[bi]]]\n"); 350cb93a386Sopenharmony_ci printf(" where: 'file_i' is the i'th animation frame (WebP format),\n"); 351cb93a386Sopenharmony_ci printf(" 'di' is the pause duration before next frame,\n"); 352cb93a386Sopenharmony_ci printf(" 'xi','yi' specify the image offset for this frame,\n"); 353cb93a386Sopenharmony_ci printf(" 'mi' is the dispose method for this frame (0 or 1),\n"); 354cb93a386Sopenharmony_ci printf(" 'bi' is the blending method for this frame (+b or -b)" 355cb93a386Sopenharmony_ci "\n"); 356cb93a386Sopenharmony_ci 357cb93a386Sopenharmony_ci printf("\n"); 358cb93a386Sopenharmony_ci printf("LOOP_COUNT:\n"); 359cb93a386Sopenharmony_ci printf(" Number of times to repeat the animation.\n"); 360cb93a386Sopenharmony_ci printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n"); 361cb93a386Sopenharmony_ci 362cb93a386Sopenharmony_ci printf("\n"); 363cb93a386Sopenharmony_ci printf("BACKGROUND_COLOR:\n"); 364cb93a386Sopenharmony_ci printf(" Background color of the canvas.\n"); 365cb93a386Sopenharmony_ci printf(" A,R,G,B\n"); 366cb93a386Sopenharmony_ci printf(" where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 " 367cb93a386Sopenharmony_ci "specifying\n"); 368cb93a386Sopenharmony_ci printf(" the Alpha, Red, Green and Blue component values " 369cb93a386Sopenharmony_ci "respectively\n"); 370cb93a386Sopenharmony_ci printf(" [Default: 255,255,255,255]\n"); 371cb93a386Sopenharmony_ci 372cb93a386Sopenharmony_ci printf("\nINPUT & OUTPUT are in WebP format.\n"); 373cb93a386Sopenharmony_ci 374cb93a386Sopenharmony_ci printf("\nNote: The nature of EXIF, XMP and ICC data is not checked"); 375cb93a386Sopenharmony_ci printf(" and is assumed to be\nvalid.\n"); 376cb93a386Sopenharmony_ci printf("\nNote: if a single file name is passed as the argument, the " 377cb93a386Sopenharmony_ci "arguments will be\n"); 378cb93a386Sopenharmony_ci printf("tokenized from this file. The file name must not start with " 379cb93a386Sopenharmony_ci "the character '-'.\n"); 380cb93a386Sopenharmony_ci} 381cb93a386Sopenharmony_ci 382cb93a386Sopenharmony_cistatic void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) { 383cb93a386Sopenharmony_ci if ((info->x_offset | info->y_offset) & 1) { 384cb93a386Sopenharmony_ci fprintf(stderr, "Warning: odd offsets will be snapped to even values" 385cb93a386Sopenharmony_ci " (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset, 386cb93a386Sopenharmony_ci info->x_offset & ~1, info->y_offset & ~1); 387cb93a386Sopenharmony_ci } 388cb93a386Sopenharmony_ci} 389cb93a386Sopenharmony_ci 390cb93a386Sopenharmony_cistatic int CreateMux(const char* const filename, WebPMux** mux) { 391cb93a386Sopenharmony_ci WebPData bitstream; 392cb93a386Sopenharmony_ci assert(mux != NULL); 393cb93a386Sopenharmony_ci if (!ExUtilReadFileToWebPData(filename, &bitstream)) return 0; 394cb93a386Sopenharmony_ci *mux = WebPMuxCreate(&bitstream, 1); 395cb93a386Sopenharmony_ci WebPDataClear(&bitstream); 396cb93a386Sopenharmony_ci if (*mux != NULL) return 1; 397cb93a386Sopenharmony_ci WFPRINTF(stderr, "Failed to create mux object from file %s.\n", 398cb93a386Sopenharmony_ci (const W_CHAR*)filename); 399cb93a386Sopenharmony_ci return 0; 400cb93a386Sopenharmony_ci} 401cb93a386Sopenharmony_ci 402cb93a386Sopenharmony_cistatic int WriteData(const char* filename, const WebPData* const webpdata) { 403cb93a386Sopenharmony_ci int ok = 0; 404cb93a386Sopenharmony_ci FILE* fout = WSTRCMP(filename, "-") ? WFOPEN(filename, "wb") 405cb93a386Sopenharmony_ci : ImgIoUtilSetBinaryMode(stdout); 406cb93a386Sopenharmony_ci if (fout == NULL) { 407cb93a386Sopenharmony_ci WFPRINTF(stderr, "Error opening output WebP file %s!\n", 408cb93a386Sopenharmony_ci (const W_CHAR*)filename); 409cb93a386Sopenharmony_ci return 0; 410cb93a386Sopenharmony_ci } 411cb93a386Sopenharmony_ci if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) { 412cb93a386Sopenharmony_ci WFPRINTF(stderr, "Error writing file %s!\n", (const W_CHAR*)filename); 413cb93a386Sopenharmony_ci } else { 414cb93a386Sopenharmony_ci WFPRINTF(stderr, "Saved file %s (%d bytes)\n", 415cb93a386Sopenharmony_ci (const W_CHAR*)filename, (int)webpdata->size); 416cb93a386Sopenharmony_ci ok = 1; 417cb93a386Sopenharmony_ci } 418cb93a386Sopenharmony_ci if (fout != stdout) fclose(fout); 419cb93a386Sopenharmony_ci return ok; 420cb93a386Sopenharmony_ci} 421cb93a386Sopenharmony_ci 422cb93a386Sopenharmony_cistatic int WriteWebP(WebPMux* const mux, const char* filename) { 423cb93a386Sopenharmony_ci int ok; 424cb93a386Sopenharmony_ci WebPData webp_data; 425cb93a386Sopenharmony_ci const WebPMuxError err = WebPMuxAssemble(mux, &webp_data); 426cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 427cb93a386Sopenharmony_ci fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err)); 428cb93a386Sopenharmony_ci return 0; 429cb93a386Sopenharmony_ci } 430cb93a386Sopenharmony_ci ok = WriteData(filename, &webp_data); 431cb93a386Sopenharmony_ci WebPDataClear(&webp_data); 432cb93a386Sopenharmony_ci return ok; 433cb93a386Sopenharmony_ci} 434cb93a386Sopenharmony_ci 435cb93a386Sopenharmony_cistatic WebPMux* DuplicateMuxHeader(const WebPMux* const mux) { 436cb93a386Sopenharmony_ci WebPMux* new_mux = WebPMuxNew(); 437cb93a386Sopenharmony_ci WebPMuxAnimParams p; 438cb93a386Sopenharmony_ci WebPMuxError err; 439cb93a386Sopenharmony_ci int i; 440cb93a386Sopenharmony_ci int ok = 1; 441cb93a386Sopenharmony_ci 442cb93a386Sopenharmony_ci if (new_mux == NULL) return NULL; 443cb93a386Sopenharmony_ci 444cb93a386Sopenharmony_ci err = WebPMuxGetAnimationParams(mux, &p); 445cb93a386Sopenharmony_ci if (err == WEBP_MUX_OK) { 446cb93a386Sopenharmony_ci err = WebPMuxSetAnimationParams(new_mux, &p); 447cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 448cb93a386Sopenharmony_ci ERROR_GOTO2("Error (%s) handling animation params.\n", 449cb93a386Sopenharmony_ci ErrorString(err), End); 450cb93a386Sopenharmony_ci } 451cb93a386Sopenharmony_ci } else { 452cb93a386Sopenharmony_ci /* it might not be an animation. Just keep moving. */ 453cb93a386Sopenharmony_ci } 454cb93a386Sopenharmony_ci 455cb93a386Sopenharmony_ci for (i = 1; i <= 3; ++i) { 456cb93a386Sopenharmony_ci WebPData metadata; 457cb93a386Sopenharmony_ci err = WebPMuxGetChunk(mux, kFourccList[i], &metadata); 458cb93a386Sopenharmony_ci if (err == WEBP_MUX_OK && metadata.size > 0) { 459cb93a386Sopenharmony_ci err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1); 460cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 461cb93a386Sopenharmony_ci ERROR_GOTO1("Error transferring metadata in DuplicateMux().", End); 462cb93a386Sopenharmony_ci } 463cb93a386Sopenharmony_ci } 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci 466cb93a386Sopenharmony_ci End: 467cb93a386Sopenharmony_ci if (!ok) { 468cb93a386Sopenharmony_ci WebPMuxDelete(new_mux); 469cb93a386Sopenharmony_ci new_mux = NULL; 470cb93a386Sopenharmony_ci } 471cb93a386Sopenharmony_ci return new_mux; 472cb93a386Sopenharmony_ci} 473cb93a386Sopenharmony_ci 474cb93a386Sopenharmony_cistatic int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) { 475cb93a386Sopenharmony_ci int dispose_method, unused; 476cb93a386Sopenharmony_ci char plus_minus, blend_method; 477cb93a386Sopenharmony_ci const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration, 478cb93a386Sopenharmony_ci &info->x_offset, &info->y_offset, &dispose_method, 479cb93a386Sopenharmony_ci &plus_minus, &blend_method, &unused); 480cb93a386Sopenharmony_ci switch (num_args) { 481cb93a386Sopenharmony_ci case 1: 482cb93a386Sopenharmony_ci info->x_offset = info->y_offset = 0; // fall through 483cb93a386Sopenharmony_ci case 3: 484cb93a386Sopenharmony_ci dispose_method = 0; // fall through 485cb93a386Sopenharmony_ci case 4: 486cb93a386Sopenharmony_ci plus_minus = '+'; 487cb93a386Sopenharmony_ci blend_method = 'b'; // fall through 488cb93a386Sopenharmony_ci case 6: 489cb93a386Sopenharmony_ci break; 490cb93a386Sopenharmony_ci case 2: 491cb93a386Sopenharmony_ci case 5: 492cb93a386Sopenharmony_ci default: 493cb93a386Sopenharmony_ci return 0; 494cb93a386Sopenharmony_ci } 495cb93a386Sopenharmony_ci 496cb93a386Sopenharmony_ci WarnAboutOddOffset(info); 497cb93a386Sopenharmony_ci 498cb93a386Sopenharmony_ci // Note: The validity of the following conversion is checked by 499cb93a386Sopenharmony_ci // WebPMuxPushFrame(). 500cb93a386Sopenharmony_ci info->dispose_method = (WebPMuxAnimDispose)dispose_method; 501cb93a386Sopenharmony_ci 502cb93a386Sopenharmony_ci if (blend_method != 'b') return 0; 503cb93a386Sopenharmony_ci if (plus_minus != '-' && plus_minus != '+') return 0; 504cb93a386Sopenharmony_ci info->blend_method = 505cb93a386Sopenharmony_ci (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND; 506cb93a386Sopenharmony_ci return 1; 507cb93a386Sopenharmony_ci} 508cb93a386Sopenharmony_ci 509cb93a386Sopenharmony_cistatic int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) { 510cb93a386Sopenharmony_ci uint32_t a, r, g, b; 511cb93a386Sopenharmony_ci if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0; 512cb93a386Sopenharmony_ci if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0; 513cb93a386Sopenharmony_ci *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0); 514cb93a386Sopenharmony_ci return 1; 515cb93a386Sopenharmony_ci} 516cb93a386Sopenharmony_ci 517cb93a386Sopenharmony_ci//------------------------------------------------------------------------------ 518cb93a386Sopenharmony_ci// Clean-up. 519cb93a386Sopenharmony_ci 520cb93a386Sopenharmony_cistatic void DeleteConfig(Config* const config) { 521cb93a386Sopenharmony_ci if (config != NULL) { 522cb93a386Sopenharmony_ci free(config->args_); 523cb93a386Sopenharmony_ci ExUtilDeleteCommandLineArguments(&config->cmd_args_); 524cb93a386Sopenharmony_ci memset(config, 0, sizeof(*config)); 525cb93a386Sopenharmony_ci } 526cb93a386Sopenharmony_ci} 527cb93a386Sopenharmony_ci 528cb93a386Sopenharmony_ci//------------------------------------------------------------------------------ 529cb93a386Sopenharmony_ci// Parsing. 530cb93a386Sopenharmony_ci 531cb93a386Sopenharmony_ci// Basic syntactic checks on the command-line arguments. 532cb93a386Sopenharmony_ci// Returns 1 on valid, 0 otherwise. 533cb93a386Sopenharmony_ci// Also fills up num_feature_args to be number of feature arguments given. 534cb93a386Sopenharmony_ci// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5). 535cb93a386Sopenharmony_cistatic int ValidateCommandLine(const CommandLineArguments* const cmd_args, 536cb93a386Sopenharmony_ci int* num_feature_args) { 537cb93a386Sopenharmony_ci int num_frame_args; 538cb93a386Sopenharmony_ci int num_loop_args; 539cb93a386Sopenharmony_ci int num_bgcolor_args; 540cb93a386Sopenharmony_ci int num_durations_args; 541cb93a386Sopenharmony_ci int ok = 1; 542cb93a386Sopenharmony_ci 543cb93a386Sopenharmony_ci assert(num_feature_args != NULL); 544cb93a386Sopenharmony_ci *num_feature_args = 0; 545cb93a386Sopenharmony_ci 546cb93a386Sopenharmony_ci // Simple checks. 547cb93a386Sopenharmony_ci if (CountOccurrences(cmd_args, "-get") > 1) { 548cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate); 549cb93a386Sopenharmony_ci } 550cb93a386Sopenharmony_ci if (CountOccurrences(cmd_args, "-set") > 1) { 551cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate); 552cb93a386Sopenharmony_ci } 553cb93a386Sopenharmony_ci if (CountOccurrences(cmd_args, "-strip") > 1) { 554cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate); 555cb93a386Sopenharmony_ci } 556cb93a386Sopenharmony_ci if (CountOccurrences(cmd_args, "-info") > 1) { 557cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate); 558cb93a386Sopenharmony_ci } 559cb93a386Sopenharmony_ci if (CountOccurrences(cmd_args, "-o") > 1) { 560cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate); 561cb93a386Sopenharmony_ci } 562cb93a386Sopenharmony_ci 563cb93a386Sopenharmony_ci // Compound checks. 564cb93a386Sopenharmony_ci num_frame_args = CountOccurrences(cmd_args, "-frame"); 565cb93a386Sopenharmony_ci num_loop_args = CountOccurrences(cmd_args, "-loop"); 566cb93a386Sopenharmony_ci num_bgcolor_args = CountOccurrences(cmd_args, "-bgcolor"); 567cb93a386Sopenharmony_ci num_durations_args = CountOccurrences(cmd_args, "-duration"); 568cb93a386Sopenharmony_ci 569cb93a386Sopenharmony_ci if (num_loop_args > 1) { 570cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate); 571cb93a386Sopenharmony_ci } 572cb93a386Sopenharmony_ci if (num_bgcolor_args > 1) { 573cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate); 574cb93a386Sopenharmony_ci } 575cb93a386Sopenharmony_ci 576cb93a386Sopenharmony_ci if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) { 577cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Loop count and background color are relevant only in " 578cb93a386Sopenharmony_ci "case of animation.\n", ErrValidate); 579cb93a386Sopenharmony_ci } 580cb93a386Sopenharmony_ci if (num_durations_args > 0 && num_frame_args != 0) { 581cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n", 582cb93a386Sopenharmony_ci ErrValidate); 583cb93a386Sopenharmony_ci } 584cb93a386Sopenharmony_ci 585cb93a386Sopenharmony_ci assert(ok == 1); 586cb93a386Sopenharmony_ci if (num_durations_args > 0) { 587cb93a386Sopenharmony_ci *num_feature_args = num_durations_args; 588cb93a386Sopenharmony_ci } else if (num_frame_args == 0) { 589cb93a386Sopenharmony_ci // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action). 590cb93a386Sopenharmony_ci *num_feature_args = 1; 591cb93a386Sopenharmony_ci } else { 592cb93a386Sopenharmony_ci // Multiple arguments ('set' action for animation) 593cb93a386Sopenharmony_ci *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args; 594cb93a386Sopenharmony_ci } 595cb93a386Sopenharmony_ci 596cb93a386Sopenharmony_ci ErrValidate: 597cb93a386Sopenharmony_ci return ok; 598cb93a386Sopenharmony_ci} 599cb93a386Sopenharmony_ci 600cb93a386Sopenharmony_ci#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION) 601cb93a386Sopenharmony_ci 602cb93a386Sopenharmony_ci#define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE) 603cb93a386Sopenharmony_ci 604cb93a386Sopenharmony_ci#define CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL) \ 605cb93a386Sopenharmony_ci if (argc < i + (NUM)) { \ 606cb93a386Sopenharmony_ci fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \ 607cb93a386Sopenharmony_ci goto LABEL; \ 608cb93a386Sopenharmony_ci } 609cb93a386Sopenharmony_ci 610cb93a386Sopenharmony_ci#define CHECK_NUM_ARGS_AT_MOST(NUM, LABEL) \ 611cb93a386Sopenharmony_ci if (argc > i + (NUM)) { \ 612cb93a386Sopenharmony_ci fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \ 613cb93a386Sopenharmony_ci goto LABEL; \ 614cb93a386Sopenharmony_ci } 615cb93a386Sopenharmony_ci 616cb93a386Sopenharmony_ci#define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL) \ 617cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL); \ 618cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_MOST(NUM, LABEL); 619cb93a386Sopenharmony_ci 620cb93a386Sopenharmony_ci// Parses command-line arguments to fill up config object. Also performs some 621cb93a386Sopenharmony_ci// semantic checks. unicode_argv contains wchar_t arguments or is null. 622cb93a386Sopenharmony_cistatic int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) { 623cb93a386Sopenharmony_ci int i = 0; 624cb93a386Sopenharmony_ci int feature_arg_index = 0; 625cb93a386Sopenharmony_ci int ok = 1; 626cb93a386Sopenharmony_ci int argc = config->cmd_args_.argc_; 627cb93a386Sopenharmony_ci const char* const* argv = config->cmd_args_.argv_; 628cb93a386Sopenharmony_ci // Unicode file paths will be used if available. 629cb93a386Sopenharmony_ci const char* const* wargv = 630cb93a386Sopenharmony_ci (unicode_argv != NULL) ? (const char**)(unicode_argv + 1) : argv; 631cb93a386Sopenharmony_ci 632cb93a386Sopenharmony_ci while (i < argc) { 633cb93a386Sopenharmony_ci FeatureArg* const arg = &config->args_[feature_arg_index]; 634cb93a386Sopenharmony_ci if (argv[i][0] == '-') { // One of the action types or output. 635cb93a386Sopenharmony_ci if (!strcmp(argv[i], "-set")) { 636cb93a386Sopenharmony_ci if (ACTION_IS_NIL) { 637cb93a386Sopenharmony_ci config->action_type_ = ACTION_SET; 638cb93a386Sopenharmony_ci } else { 639cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); 640cb93a386Sopenharmony_ci } 641cb93a386Sopenharmony_ci ++i; 642cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-duration")) { 643cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); 644cb93a386Sopenharmony_ci if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) { 645cb93a386Sopenharmony_ci config->action_type_ = ACTION_DURATION; 646cb93a386Sopenharmony_ci } else { 647cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); 648cb93a386Sopenharmony_ci } 649cb93a386Sopenharmony_ci if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) { 650cb93a386Sopenharmony_ci config->type_ = FEATURE_DURATION; 651cb93a386Sopenharmony_ci } else { 652cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse); 653cb93a386Sopenharmony_ci } 654cb93a386Sopenharmony_ci arg->params_ = argv[i + 1]; 655cb93a386Sopenharmony_ci ++feature_arg_index; 656cb93a386Sopenharmony_ci i += 2; 657cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-get")) { 658cb93a386Sopenharmony_ci if (ACTION_IS_NIL) { 659cb93a386Sopenharmony_ci config->action_type_ = ACTION_GET; 660cb93a386Sopenharmony_ci } else { 661cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); 662cb93a386Sopenharmony_ci } 663cb93a386Sopenharmony_ci ++i; 664cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-strip")) { 665cb93a386Sopenharmony_ci if (ACTION_IS_NIL) { 666cb93a386Sopenharmony_ci config->action_type_ = ACTION_STRIP; 667cb93a386Sopenharmony_ci config->arg_count_ = 0; 668cb93a386Sopenharmony_ci } else { 669cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); 670cb93a386Sopenharmony_ci } 671cb93a386Sopenharmony_ci ++i; 672cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-frame")) { 673cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_LEAST(3, ErrParse); 674cb93a386Sopenharmony_ci if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) { 675cb93a386Sopenharmony_ci config->action_type_ = ACTION_SET; 676cb93a386Sopenharmony_ci } else { 677cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); 678cb93a386Sopenharmony_ci } 679cb93a386Sopenharmony_ci if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) { 680cb93a386Sopenharmony_ci config->type_ = FEATURE_ANMF; 681cb93a386Sopenharmony_ci } else { 682cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse); 683cb93a386Sopenharmony_ci } 684cb93a386Sopenharmony_ci arg->subtype_ = SUBTYPE_ANMF; 685cb93a386Sopenharmony_ci arg->filename_ = argv[i + 1]; 686cb93a386Sopenharmony_ci arg->params_ = argv[i + 2]; 687cb93a386Sopenharmony_ci ++feature_arg_index; 688cb93a386Sopenharmony_ci i += 3; 689cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) { 690cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); 691cb93a386Sopenharmony_ci if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) { 692cb93a386Sopenharmony_ci config->action_type_ = ACTION_SET; 693cb93a386Sopenharmony_ci } else { 694cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); 695cb93a386Sopenharmony_ci } 696cb93a386Sopenharmony_ci if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) { 697cb93a386Sopenharmony_ci config->type_ = FEATURE_ANMF; 698cb93a386Sopenharmony_ci } else { 699cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse); 700cb93a386Sopenharmony_ci } 701cb93a386Sopenharmony_ci arg->subtype_ = 702cb93a386Sopenharmony_ci !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR; 703cb93a386Sopenharmony_ci arg->params_ = argv[i + 1]; 704cb93a386Sopenharmony_ci ++feature_arg_index; 705cb93a386Sopenharmony_ci i += 2; 706cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-o")) { 707cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); 708cb93a386Sopenharmony_ci config->output_ = wargv[i + 1]; 709cb93a386Sopenharmony_ci i += 2; 710cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-info")) { 711cb93a386Sopenharmony_ci CHECK_NUM_ARGS_EXACTLY(2, ErrParse); 712cb93a386Sopenharmony_ci if (config->action_type_ != NIL_ACTION) { 713cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse); 714cb93a386Sopenharmony_ci } else { 715cb93a386Sopenharmony_ci config->action_type_ = ACTION_INFO; 716cb93a386Sopenharmony_ci config->arg_count_ = 0; 717cb93a386Sopenharmony_ci config->input_ = wargv[i + 1]; 718cb93a386Sopenharmony_ci } 719cb93a386Sopenharmony_ci i += 2; 720cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) { 721cb93a386Sopenharmony_ci PrintHelp(); 722cb93a386Sopenharmony_ci DeleteConfig(config); 723cb93a386Sopenharmony_ci LOCAL_FREE((W_CHAR** const)unicode_argv); 724cb93a386Sopenharmony_ci exit(0); 725cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "-version")) { 726cb93a386Sopenharmony_ci const int version = WebPGetMuxVersion(); 727cb93a386Sopenharmony_ci printf("%d.%d.%d\n", 728cb93a386Sopenharmony_ci (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); 729cb93a386Sopenharmony_ci DeleteConfig(config); 730cb93a386Sopenharmony_ci LOCAL_FREE((W_CHAR** const)unicode_argv); 731cb93a386Sopenharmony_ci exit(0); 732cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "--")) { 733cb93a386Sopenharmony_ci if (i < argc - 1) { 734cb93a386Sopenharmony_ci ++i; 735cb93a386Sopenharmony_ci if (config->input_ == NULL) { 736cb93a386Sopenharmony_ci config->input_ = wargv[i]; 737cb93a386Sopenharmony_ci } else { 738cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n", 739cb93a386Sopenharmony_ci argv[i], ErrParse); 740cb93a386Sopenharmony_ci } 741cb93a386Sopenharmony_ci } 742cb93a386Sopenharmony_ci break; 743cb93a386Sopenharmony_ci } else { 744cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse); 745cb93a386Sopenharmony_ci } 746cb93a386Sopenharmony_ci } else { // One of the feature types or input. 747cb93a386Sopenharmony_ci if (ACTION_IS_NIL) { 748cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n", 749cb93a386Sopenharmony_ci ErrParse); 750cb93a386Sopenharmony_ci } 751cb93a386Sopenharmony_ci if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") || 752cb93a386Sopenharmony_ci !strcmp(argv[i], "xmp")) { 753cb93a386Sopenharmony_ci if (FEATURETYPE_IS_NIL) { 754cb93a386Sopenharmony_ci config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP : 755cb93a386Sopenharmony_ci (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP; 756cb93a386Sopenharmony_ci } else { 757cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse); 758cb93a386Sopenharmony_ci } 759cb93a386Sopenharmony_ci if (config->action_type_ == ACTION_SET) { 760cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); 761cb93a386Sopenharmony_ci arg->filename_ = wargv[i + 1]; 762cb93a386Sopenharmony_ci ++feature_arg_index; 763cb93a386Sopenharmony_ci i += 2; 764cb93a386Sopenharmony_ci } else { 765cb93a386Sopenharmony_ci ++i; 766cb93a386Sopenharmony_ci } 767cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "frame") && 768cb93a386Sopenharmony_ci (config->action_type_ == ACTION_GET)) { 769cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); 770cb93a386Sopenharmony_ci config->type_ = FEATURE_ANMF; 771cb93a386Sopenharmony_ci arg->params_ = argv[i + 1]; 772cb93a386Sopenharmony_ci ++feature_arg_index; 773cb93a386Sopenharmony_ci i += 2; 774cb93a386Sopenharmony_ci } else if (!strcmp(argv[i], "loop") && 775cb93a386Sopenharmony_ci (config->action_type_ == ACTION_SET)) { 776cb93a386Sopenharmony_ci CHECK_NUM_ARGS_AT_LEAST(2, ErrParse); 777cb93a386Sopenharmony_ci config->type_ = FEATURE_LOOP; 778cb93a386Sopenharmony_ci arg->params_ = argv[i + 1]; 779cb93a386Sopenharmony_ci ++feature_arg_index; 780cb93a386Sopenharmony_ci i += 2; 781cb93a386Sopenharmony_ci } else { // Assume input file. 782cb93a386Sopenharmony_ci if (config->input_ == NULL) { 783cb93a386Sopenharmony_ci config->input_ = wargv[i]; 784cb93a386Sopenharmony_ci } else { 785cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n", 786cb93a386Sopenharmony_ci argv[i], ErrParse); 787cb93a386Sopenharmony_ci } 788cb93a386Sopenharmony_ci ++i; 789cb93a386Sopenharmony_ci } 790cb93a386Sopenharmony_ci } 791cb93a386Sopenharmony_ci } 792cb93a386Sopenharmony_ci ErrParse: 793cb93a386Sopenharmony_ci return ok; 794cb93a386Sopenharmony_ci} 795cb93a386Sopenharmony_ci 796cb93a386Sopenharmony_ci// Additional checks after config is filled. 797cb93a386Sopenharmony_cistatic int ValidateConfig(Config* const config) { 798cb93a386Sopenharmony_ci int ok = 1; 799cb93a386Sopenharmony_ci 800cb93a386Sopenharmony_ci // Action. 801cb93a386Sopenharmony_ci if (ACTION_IS_NIL) { 802cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2); 803cb93a386Sopenharmony_ci } 804cb93a386Sopenharmony_ci 805cb93a386Sopenharmony_ci // Feature type. 806cb93a386Sopenharmony_ci if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) { 807cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2); 808cb93a386Sopenharmony_ci } 809cb93a386Sopenharmony_ci 810cb93a386Sopenharmony_ci // Input file. 811cb93a386Sopenharmony_ci if (config->input_ == NULL) { 812cb93a386Sopenharmony_ci if (config->action_type_ != ACTION_SET) { 813cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2); 814cb93a386Sopenharmony_ci } else if (config->type_ != FEATURE_ANMF) { 815cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2); 816cb93a386Sopenharmony_ci } 817cb93a386Sopenharmony_ci } 818cb93a386Sopenharmony_ci 819cb93a386Sopenharmony_ci // Output file. 820cb93a386Sopenharmony_ci if (config->output_ == NULL && config->action_type_ != ACTION_INFO) { 821cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2); 822cb93a386Sopenharmony_ci } 823cb93a386Sopenharmony_ci 824cb93a386Sopenharmony_ci ErrValidate2: 825cb93a386Sopenharmony_ci return ok; 826cb93a386Sopenharmony_ci} 827cb93a386Sopenharmony_ci 828cb93a386Sopenharmony_ci// Create config object from command-line arguments. 829cb93a386Sopenharmony_cistatic int InitializeConfig(int argc, const char* argv[], Config* const config, 830cb93a386Sopenharmony_ci const W_CHAR** const unicode_argv) { 831cb93a386Sopenharmony_ci int num_feature_args = 0; 832cb93a386Sopenharmony_ci int ok; 833cb93a386Sopenharmony_ci 834cb93a386Sopenharmony_ci memset(config, 0, sizeof(*config)); 835cb93a386Sopenharmony_ci 836cb93a386Sopenharmony_ci ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_); 837cb93a386Sopenharmony_ci if (!ok) return 0; 838cb93a386Sopenharmony_ci 839cb93a386Sopenharmony_ci // Validate command-line arguments. 840cb93a386Sopenharmony_ci if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) { 841cb93a386Sopenharmony_ci ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1); 842cb93a386Sopenharmony_ci } 843cb93a386Sopenharmony_ci 844cb93a386Sopenharmony_ci config->arg_count_ = num_feature_args; 845cb93a386Sopenharmony_ci config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_)); 846cb93a386Sopenharmony_ci if (config->args_ == NULL) { 847cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1); 848cb93a386Sopenharmony_ci } 849cb93a386Sopenharmony_ci 850cb93a386Sopenharmony_ci // Parse command-line. 851cb93a386Sopenharmony_ci if (!ParseCommandLine(config, unicode_argv) || !ValidateConfig(config)) { 852cb93a386Sopenharmony_ci ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1); 853cb93a386Sopenharmony_ci } 854cb93a386Sopenharmony_ci 855cb93a386Sopenharmony_ci Err1: 856cb93a386Sopenharmony_ci return ok; 857cb93a386Sopenharmony_ci} 858cb93a386Sopenharmony_ci 859cb93a386Sopenharmony_ci#undef ACTION_IS_NIL 860cb93a386Sopenharmony_ci#undef FEATURETYPE_IS_NIL 861cb93a386Sopenharmony_ci#undef CHECK_NUM_ARGS_AT_LEAST 862cb93a386Sopenharmony_ci#undef CHECK_NUM_ARGS_AT_MOST 863cb93a386Sopenharmony_ci#undef CHECK_NUM_ARGS_EXACTLY 864cb93a386Sopenharmony_ci 865cb93a386Sopenharmony_ci//------------------------------------------------------------------------------ 866cb93a386Sopenharmony_ci// Processing. 867cb93a386Sopenharmony_ci 868cb93a386Sopenharmony_cistatic int GetFrame(const WebPMux* mux, const Config* config) { 869cb93a386Sopenharmony_ci WebPMuxError err = WEBP_MUX_OK; 870cb93a386Sopenharmony_ci WebPMux* mux_single = NULL; 871cb93a386Sopenharmony_ci int num = 0; 872cb93a386Sopenharmony_ci int ok = 1; 873cb93a386Sopenharmony_ci int parse_error = 0; 874cb93a386Sopenharmony_ci const WebPChunkId id = WEBP_CHUNK_ANMF; 875cb93a386Sopenharmony_ci WebPMuxFrameInfo info; 876cb93a386Sopenharmony_ci WebPDataInit(&info.bitstream); 877cb93a386Sopenharmony_ci 878cb93a386Sopenharmony_ci num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error); 879cb93a386Sopenharmony_ci if (num < 0) { 880cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet); 881cb93a386Sopenharmony_ci } 882cb93a386Sopenharmony_ci if (parse_error) goto ErrGet; 883cb93a386Sopenharmony_ci 884cb93a386Sopenharmony_ci err = WebPMuxGetFrame(mux, num, &info); 885cb93a386Sopenharmony_ci if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND; 886cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 887cb93a386Sopenharmony_ci ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n", 888cb93a386Sopenharmony_ci ErrorString(err), num, ErrGet); 889cb93a386Sopenharmony_ci } 890cb93a386Sopenharmony_ci 891cb93a386Sopenharmony_ci mux_single = WebPMuxNew(); 892cb93a386Sopenharmony_ci if (mux_single == NULL) { 893cb93a386Sopenharmony_ci err = WEBP_MUX_MEMORY_ERROR; 894cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n", 895cb93a386Sopenharmony_ci ErrorString(err), ErrGet); 896cb93a386Sopenharmony_ci } 897cb93a386Sopenharmony_ci err = WebPMuxSetImage(mux_single, &info.bitstream, 1); 898cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 899cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n", 900cb93a386Sopenharmony_ci ErrorString(err), ErrGet); 901cb93a386Sopenharmony_ci } 902cb93a386Sopenharmony_ci 903cb93a386Sopenharmony_ci ok = WriteWebP(mux_single, config->output_); 904cb93a386Sopenharmony_ci 905cb93a386Sopenharmony_ci ErrGet: 906cb93a386Sopenharmony_ci WebPDataClear(&info.bitstream); 907cb93a386Sopenharmony_ci WebPMuxDelete(mux_single); 908cb93a386Sopenharmony_ci return ok && !parse_error; 909cb93a386Sopenharmony_ci} 910cb93a386Sopenharmony_ci 911cb93a386Sopenharmony_ci// Read and process config. 912cb93a386Sopenharmony_cistatic int Process(const Config* config) { 913cb93a386Sopenharmony_ci WebPMux* mux = NULL; 914cb93a386Sopenharmony_ci WebPData chunk; 915cb93a386Sopenharmony_ci WebPMuxError err = WEBP_MUX_OK; 916cb93a386Sopenharmony_ci int ok = 1; 917cb93a386Sopenharmony_ci 918cb93a386Sopenharmony_ci switch (config->action_type_) { 919cb93a386Sopenharmony_ci case ACTION_GET: { 920cb93a386Sopenharmony_ci ok = CreateMux(config->input_, &mux); 921cb93a386Sopenharmony_ci if (!ok) goto Err2; 922cb93a386Sopenharmony_ci switch (config->type_) { 923cb93a386Sopenharmony_ci case FEATURE_ANMF: 924cb93a386Sopenharmony_ci ok = GetFrame(mux, config); 925cb93a386Sopenharmony_ci break; 926cb93a386Sopenharmony_ci 927cb93a386Sopenharmony_ci case FEATURE_ICCP: 928cb93a386Sopenharmony_ci case FEATURE_EXIF: 929cb93a386Sopenharmony_ci case FEATURE_XMP: 930cb93a386Sopenharmony_ci err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk); 931cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 932cb93a386Sopenharmony_ci ERROR_GOTO3("ERROR (%s): Could not get the %s.\n", 933cb93a386Sopenharmony_ci ErrorString(err), kDescriptions[config->type_], Err2); 934cb93a386Sopenharmony_ci } 935cb93a386Sopenharmony_ci ok = WriteData(config->output_, &chunk); 936cb93a386Sopenharmony_ci break; 937cb93a386Sopenharmony_ci 938cb93a386Sopenharmony_ci default: 939cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2); 940cb93a386Sopenharmony_ci break; 941cb93a386Sopenharmony_ci } 942cb93a386Sopenharmony_ci break; 943cb93a386Sopenharmony_ci } 944cb93a386Sopenharmony_ci case ACTION_SET: { 945cb93a386Sopenharmony_ci switch (config->type_) { 946cb93a386Sopenharmony_ci case FEATURE_ANMF: { 947cb93a386Sopenharmony_ci int i; 948cb93a386Sopenharmony_ci WebPMuxAnimParams params = { 0xFFFFFFFF, 0 }; 949cb93a386Sopenharmony_ci mux = WebPMuxNew(); 950cb93a386Sopenharmony_ci if (mux == NULL) { 951cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n", 952cb93a386Sopenharmony_ci ErrorString(WEBP_MUX_MEMORY_ERROR), Err2); 953cb93a386Sopenharmony_ci } 954cb93a386Sopenharmony_ci for (i = 0; i < config->arg_count_; ++i) { 955cb93a386Sopenharmony_ci switch (config->args_[i].subtype_) { 956cb93a386Sopenharmony_ci case SUBTYPE_BGCOLOR: { 957cb93a386Sopenharmony_ci uint32_t bgcolor; 958cb93a386Sopenharmony_ci ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor); 959cb93a386Sopenharmony_ci if (!ok) { 960cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Could not parse the background color \n", 961cb93a386Sopenharmony_ci Err2); 962cb93a386Sopenharmony_ci } 963cb93a386Sopenharmony_ci params.bgcolor = bgcolor; 964cb93a386Sopenharmony_ci break; 965cb93a386Sopenharmony_ci } 966cb93a386Sopenharmony_ci case SUBTYPE_LOOP: { 967cb93a386Sopenharmony_ci int parse_error = 0; 968cb93a386Sopenharmony_ci const int loop_count = 969cb93a386Sopenharmony_ci ExUtilGetInt(config->args_[i].params_, 10, &parse_error); 970cb93a386Sopenharmony_ci if (loop_count < 0 || loop_count > 65535) { 971cb93a386Sopenharmony_ci // Note: This is only a 'necessary' condition for loop_count 972cb93a386Sopenharmony_ci // to be valid. The 'sufficient' conditioned in checked in 973cb93a386Sopenharmony_ci // WebPMuxSetAnimationParams() method called later. 974cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Loop count must be in the range 0 to " 975cb93a386Sopenharmony_ci "65535.\n", Err2); 976cb93a386Sopenharmony_ci } 977cb93a386Sopenharmony_ci ok = !parse_error; 978cb93a386Sopenharmony_ci if (!ok) goto Err2; 979cb93a386Sopenharmony_ci params.loop_count = loop_count; 980cb93a386Sopenharmony_ci break; 981cb93a386Sopenharmony_ci } 982cb93a386Sopenharmony_ci case SUBTYPE_ANMF: { 983cb93a386Sopenharmony_ci WebPMuxFrameInfo frame; 984cb93a386Sopenharmony_ci frame.id = WEBP_CHUNK_ANMF; 985cb93a386Sopenharmony_ci ok = ExUtilReadFileToWebPData(config->args_[i].filename_, 986cb93a386Sopenharmony_ci &frame.bitstream); 987cb93a386Sopenharmony_ci if (!ok) goto Err2; 988cb93a386Sopenharmony_ci ok = ParseFrameArgs(config->args_[i].params_, &frame); 989cb93a386Sopenharmony_ci if (!ok) { 990cb93a386Sopenharmony_ci WebPDataClear(&frame.bitstream); 991cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Could not parse frame properties.\n", 992cb93a386Sopenharmony_ci Err2); 993cb93a386Sopenharmony_ci } 994cb93a386Sopenharmony_ci err = WebPMuxPushFrame(mux, &frame, 1); 995cb93a386Sopenharmony_ci WebPDataClear(&frame.bitstream); 996cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 997cb93a386Sopenharmony_ci ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d." 998cb93a386Sopenharmony_ci "\n", ErrorString(err), i, Err2); 999cb93a386Sopenharmony_ci } 1000cb93a386Sopenharmony_ci break; 1001cb93a386Sopenharmony_ci } 1002cb93a386Sopenharmony_ci default: { 1003cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2); 1004cb93a386Sopenharmony_ci break; 1005cb93a386Sopenharmony_ci } 1006cb93a386Sopenharmony_ci } 1007cb93a386Sopenharmony_ci } 1008cb93a386Sopenharmony_ci err = WebPMuxSetAnimationParams(mux, ¶ms); 1009cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 1010cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n", 1011cb93a386Sopenharmony_ci ErrorString(err), Err2); 1012cb93a386Sopenharmony_ci } 1013cb93a386Sopenharmony_ci break; 1014cb93a386Sopenharmony_ci } 1015cb93a386Sopenharmony_ci 1016cb93a386Sopenharmony_ci case FEATURE_ICCP: 1017cb93a386Sopenharmony_ci case FEATURE_EXIF: 1018cb93a386Sopenharmony_ci case FEATURE_XMP: { 1019cb93a386Sopenharmony_ci ok = CreateMux(config->input_, &mux); 1020cb93a386Sopenharmony_ci if (!ok) goto Err2; 1021cb93a386Sopenharmony_ci ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk); 1022cb93a386Sopenharmony_ci if (!ok) goto Err2; 1023cb93a386Sopenharmony_ci err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1); 1024cb93a386Sopenharmony_ci WebPDataClear(&chunk); 1025cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 1026cb93a386Sopenharmony_ci ERROR_GOTO3("ERROR (%s): Could not set the %s.\n", 1027cb93a386Sopenharmony_ci ErrorString(err), kDescriptions[config->type_], Err2); 1028cb93a386Sopenharmony_ci } 1029cb93a386Sopenharmony_ci break; 1030cb93a386Sopenharmony_ci } 1031cb93a386Sopenharmony_ci case FEATURE_LOOP: { 1032cb93a386Sopenharmony_ci WebPMuxAnimParams params = { 0xFFFFFFFF, 0 }; 1033cb93a386Sopenharmony_ci int parse_error = 0; 1034cb93a386Sopenharmony_ci const int loop_count = 1035cb93a386Sopenharmony_ci ExUtilGetInt(config->args_[0].params_, 10, &parse_error); 1036cb93a386Sopenharmony_ci if (loop_count < 0 || loop_count > 65535 || parse_error) { 1037cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Loop count must be in the range 0 to 65535.\n", 1038cb93a386Sopenharmony_ci Err2); 1039cb93a386Sopenharmony_ci } 1040cb93a386Sopenharmony_ci ok = CreateMux(config->input_, &mux); 1041cb93a386Sopenharmony_ci if (!ok) goto Err2; 1042cb93a386Sopenharmony_ci ok = (WebPMuxGetAnimationParams(mux, ¶ms) == WEBP_MUX_OK); 1043cb93a386Sopenharmony_ci if (!ok) { 1044cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n", 1045cb93a386Sopenharmony_ci Err2); 1046cb93a386Sopenharmony_ci } 1047cb93a386Sopenharmony_ci params.loop_count = loop_count; 1048cb93a386Sopenharmony_ci err = WebPMuxSetAnimationParams(mux, ¶ms); 1049cb93a386Sopenharmony_ci ok = (err == WEBP_MUX_OK); 1050cb93a386Sopenharmony_ci if (!ok) { 1051cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n", 1052cb93a386Sopenharmony_ci ErrorString(err), Err2); 1053cb93a386Sopenharmony_ci } 1054cb93a386Sopenharmony_ci break; 1055cb93a386Sopenharmony_ci } 1056cb93a386Sopenharmony_ci default: { 1057cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2); 1058cb93a386Sopenharmony_ci break; 1059cb93a386Sopenharmony_ci } 1060cb93a386Sopenharmony_ci } 1061cb93a386Sopenharmony_ci ok = WriteWebP(mux, config->output_); 1062cb93a386Sopenharmony_ci break; 1063cb93a386Sopenharmony_ci } 1064cb93a386Sopenharmony_ci case ACTION_DURATION: { 1065cb93a386Sopenharmony_ci int num_frames; 1066cb93a386Sopenharmony_ci ok = CreateMux(config->input_, &mux); 1067cb93a386Sopenharmony_ci if (!ok) goto Err2; 1068cb93a386Sopenharmony_ci err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames); 1069cb93a386Sopenharmony_ci ok = (err == WEBP_MUX_OK); 1070cb93a386Sopenharmony_ci if (!ok) { 1071cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2); 1072cb93a386Sopenharmony_ci } 1073cb93a386Sopenharmony_ci if (num_frames == 0) { 1074cb93a386Sopenharmony_ci fprintf(stderr, "Doesn't look like the source is animated. " 1075cb93a386Sopenharmony_ci "Skipping duration setting.\n"); 1076cb93a386Sopenharmony_ci ok = WriteWebP(mux, config->output_); 1077cb93a386Sopenharmony_ci if (!ok) goto Err2; 1078cb93a386Sopenharmony_ci } else { 1079cb93a386Sopenharmony_ci int i; 1080cb93a386Sopenharmony_ci int* durations = NULL; 1081cb93a386Sopenharmony_ci WebPMux* new_mux = DuplicateMuxHeader(mux); 1082cb93a386Sopenharmony_ci if (new_mux == NULL) goto Err2; 1083cb93a386Sopenharmony_ci durations = (int*)WebPMalloc((size_t)num_frames * sizeof(*durations)); 1084cb93a386Sopenharmony_ci if (durations == NULL) goto Err2; 1085cb93a386Sopenharmony_ci for (i = 0; i < num_frames; ++i) durations[i] = -1; 1086cb93a386Sopenharmony_ci 1087cb93a386Sopenharmony_ci // Parse intervals to process. 1088cb93a386Sopenharmony_ci for (i = 0; i < config->arg_count_; ++i) { 1089cb93a386Sopenharmony_ci int k; 1090cb93a386Sopenharmony_ci int args[3]; 1091cb93a386Sopenharmony_ci int duration, start, end; 1092cb93a386Sopenharmony_ci const int nb_args = ExUtilGetInts(config->args_[i].params_, 1093cb93a386Sopenharmony_ci 10, 3, args); 1094cb93a386Sopenharmony_ci ok = (nb_args >= 1); 1095cb93a386Sopenharmony_ci if (!ok) goto Err3; 1096cb93a386Sopenharmony_ci duration = args[0]; 1097cb93a386Sopenharmony_ci if (duration < 0) { 1098cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3); 1099cb93a386Sopenharmony_ci } 1100cb93a386Sopenharmony_ci 1101cb93a386Sopenharmony_ci if (nb_args == 1) { // only duration is present -> use full interval 1102cb93a386Sopenharmony_ci start = 1; 1103cb93a386Sopenharmony_ci end = num_frames; 1104cb93a386Sopenharmony_ci } else { 1105cb93a386Sopenharmony_ci start = args[1]; 1106cb93a386Sopenharmony_ci if (start <= 0) { 1107cb93a386Sopenharmony_ci start = 1; 1108cb93a386Sopenharmony_ci } else if (start > num_frames) { 1109cb93a386Sopenharmony_ci start = num_frames; 1110cb93a386Sopenharmony_ci } 1111cb93a386Sopenharmony_ci end = (nb_args >= 3) ? args[2] : start; 1112cb93a386Sopenharmony_ci if (end == 0 || end > num_frames) end = num_frames; 1113cb93a386Sopenharmony_ci } 1114cb93a386Sopenharmony_ci 1115cb93a386Sopenharmony_ci for (k = start; k <= end; ++k) { 1116cb93a386Sopenharmony_ci assert(k >= 1 && k <= num_frames); 1117cb93a386Sopenharmony_ci durations[k - 1] = duration; 1118cb93a386Sopenharmony_ci } 1119cb93a386Sopenharmony_ci } 1120cb93a386Sopenharmony_ci 1121cb93a386Sopenharmony_ci // Apply non-negative durations to their destination frames. 1122cb93a386Sopenharmony_ci for (i = 1; i <= num_frames; ++i) { 1123cb93a386Sopenharmony_ci WebPMuxFrameInfo frame; 1124cb93a386Sopenharmony_ci err = WebPMuxGetFrame(mux, i, &frame); 1125cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) { 1126cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3); 1127cb93a386Sopenharmony_ci } 1128cb93a386Sopenharmony_ci if (durations[i - 1] >= 0) frame.duration = durations[i - 1]; 1129cb93a386Sopenharmony_ci err = WebPMuxPushFrame(new_mux, &frame, 1); 1130cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 1131cb93a386Sopenharmony_ci ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3); 1132cb93a386Sopenharmony_ci } 1133cb93a386Sopenharmony_ci WebPDataClear(&frame.bitstream); 1134cb93a386Sopenharmony_ci } 1135cb93a386Sopenharmony_ci WebPMuxDelete(mux); 1136cb93a386Sopenharmony_ci ok = WriteWebP(new_mux, config->output_); 1137cb93a386Sopenharmony_ci mux = new_mux; // transfer for the WebPMuxDelete() call 1138cb93a386Sopenharmony_ci new_mux = NULL; 1139cb93a386Sopenharmony_ci 1140cb93a386Sopenharmony_ci Err3: 1141cb93a386Sopenharmony_ci WebPFree(durations); 1142cb93a386Sopenharmony_ci WebPMuxDelete(new_mux); 1143cb93a386Sopenharmony_ci if (!ok) goto Err2; 1144cb93a386Sopenharmony_ci } 1145cb93a386Sopenharmony_ci break; 1146cb93a386Sopenharmony_ci } 1147cb93a386Sopenharmony_ci case ACTION_STRIP: { 1148cb93a386Sopenharmony_ci ok = CreateMux(config->input_, &mux); 1149cb93a386Sopenharmony_ci if (!ok) goto Err2; 1150cb93a386Sopenharmony_ci if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF || 1151cb93a386Sopenharmony_ci config->type_ == FEATURE_XMP) { 1152cb93a386Sopenharmony_ci err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]); 1153cb93a386Sopenharmony_ci if (err != WEBP_MUX_OK) { 1154cb93a386Sopenharmony_ci ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n", 1155cb93a386Sopenharmony_ci ErrorString(err), kDescriptions[config->type_], Err2); 1156cb93a386Sopenharmony_ci } 1157cb93a386Sopenharmony_ci } else { 1158cb93a386Sopenharmony_ci ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2); 1159cb93a386Sopenharmony_ci break; 1160cb93a386Sopenharmony_ci } 1161cb93a386Sopenharmony_ci ok = WriteWebP(mux, config->output_); 1162cb93a386Sopenharmony_ci break; 1163cb93a386Sopenharmony_ci } 1164cb93a386Sopenharmony_ci case ACTION_INFO: { 1165cb93a386Sopenharmony_ci ok = CreateMux(config->input_, &mux); 1166cb93a386Sopenharmony_ci if (!ok) goto Err2; 1167cb93a386Sopenharmony_ci ok = (DisplayInfo(mux) == WEBP_MUX_OK); 1168cb93a386Sopenharmony_ci break; 1169cb93a386Sopenharmony_ci } 1170cb93a386Sopenharmony_ci default: { 1171cb93a386Sopenharmony_ci assert(0); // Invalid action. 1172cb93a386Sopenharmony_ci break; 1173cb93a386Sopenharmony_ci } 1174cb93a386Sopenharmony_ci } 1175cb93a386Sopenharmony_ci 1176cb93a386Sopenharmony_ci Err2: 1177cb93a386Sopenharmony_ci WebPMuxDelete(mux); 1178cb93a386Sopenharmony_ci return ok; 1179cb93a386Sopenharmony_ci} 1180cb93a386Sopenharmony_ci 1181cb93a386Sopenharmony_ci//------------------------------------------------------------------------------ 1182cb93a386Sopenharmony_ci// Main. 1183cb93a386Sopenharmony_ci 1184cb93a386Sopenharmony_ciint main(int argc, const char* argv[]) { 1185cb93a386Sopenharmony_ci Config config; 1186cb93a386Sopenharmony_ci int ok; 1187cb93a386Sopenharmony_ci 1188cb93a386Sopenharmony_ci INIT_WARGV(argc, argv); 1189cb93a386Sopenharmony_ci 1190cb93a386Sopenharmony_ci ok = InitializeConfig(argc - 1, argv + 1, &config, GET_WARGV_OR_NULL()); 1191cb93a386Sopenharmony_ci if (ok) { 1192cb93a386Sopenharmony_ci ok = Process(&config); 1193cb93a386Sopenharmony_ci } else { 1194cb93a386Sopenharmony_ci PrintHelp(); 1195cb93a386Sopenharmony_ci } 1196cb93a386Sopenharmony_ci DeleteConfig(&config); 1197cb93a386Sopenharmony_ci FREE_WARGV_AND_RETURN(!ok); 1198cb93a386Sopenharmony_ci} 1199cb93a386Sopenharmony_ci 1200cb93a386Sopenharmony_ci//------------------------------------------------------------------------------ 1201