blob: f64cfd801c016dd94ee32a89a6f8f491dde9592d [file] [log] [blame]
James Zern061263a2012-05-11 16:00:57 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2//
James Zernd6406142013-06-06 23:05:58 -07003// Use of this source code is governed by a BSD-style license
4// that can be found in the COPYING file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
James Zern061263a2012-05-11 16:00:57 -07008// -----------------------------------------------------------------------------
9//
10// Utility functions used by the example programs.
11//
12
13#include "./example_util.h"
James Zern4aaf4632014-08-29 19:07:17 -070014
15#if defined(_WIN32)
16#include <fcntl.h> // for _O_BINARY
17#include <io.h> // for _setmode()
18#endif
James Zern061263a2012-05-11 16:00:57 -070019#include <stdio.h>
20#include <stdlib.h>
skal2bcad892014-03-12 19:32:16 +010021#include <string.h>
James Zern061263a2012-05-11 16:00:57 -070022
James Zern4a0e7392014-04-22 19:33:22 -070023#include "webp/decode.h"
24#include "./stopwatch.h"
25
James Zern40b3a612014-09-10 23:35:48 -070026//------------------------------------------------------------------------------
27// String parsing
28
29uint32_t ExUtilGetUInt(const char* const v, int base, int* const error) {
30 char* end = NULL;
31 const uint32_t n = (v != NULL) ? (uint32_t)strtoul(v, &end, base) : 0u;
32 if (end == v && error != NULL && !*error) {
33 *error = 1;
34 fprintf(stderr, "Error! '%s' is not an integer.\n",
35 (v != NULL) ? v : "(null)");
36 }
37 return n;
38}
39
40int ExUtilGetInt(const char* const v, int base, int* const error) {
41 return (int)ExUtilGetUInt(v, base, error);
42}
43
44float ExUtilGetFloat(const char* const v, int* const error) {
45 char* end = NULL;
46 const float f = (v != NULL) ? (float)strtod(v, &end) : 0.f;
47 if (end == v && error != NULL && !*error) {
48 *error = 1;
49 fprintf(stderr, "Error! '%s' is not a floating point number.\n",
50 (v != NULL) ? v : "(null)");
51 }
52 return f;
53}
54
James Zern061263a2012-05-11 16:00:57 -070055// -----------------------------------------------------------------------------
56// File I/O
57
James Zern4aaf4632014-08-29 19:07:17 -070058FILE* ExUtilSetBinaryMode(FILE* file) {
59#if defined(_WIN32)
60 if (_setmode(_fileno(file), _O_BINARY) == -1) {
61 fprintf(stderr, "Failed to reopen file in O_BINARY mode.\n");
62 return NULL;
63 }
64#endif
65 return file;
66}
skal2bcad892014-03-12 19:32:16 +010067
68int ExUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
James Zern4aaf4632014-08-29 19:07:17 -070069 static const size_t kBlockSize = 16384; // default initial size
skal2bcad892014-03-12 19:32:16 +010070 size_t max_size = 0;
71 size_t size = 0;
72 uint8_t* input = NULL;
73
74 if (data == NULL || data_size == NULL) return 0;
75 *data = NULL;
76 *data_size = 0;
77
James Zern6ecd5bf2014-08-29 19:16:54 -070078 if (!ExUtilSetBinaryMode(stdin)) return 0;
79
skal2bcad892014-03-12 19:32:16 +010080 while (!feof(stdin)) {
81 // We double the buffer size each time and read as much as possible.
82 const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
83 void* const new_data = realloc(input, max_size + extra_size);
84 if (new_data == NULL) goto Error;
85 input = (uint8_t*)new_data;
86 max_size += extra_size;
87 size += fread(input + size, 1, extra_size, stdin);
88 if (size < max_size) break;
89 }
90 if (ferror(stdin)) goto Error;
91 *data = input;
92 *data_size = size;
93 return 1;
94
95 Error:
96 free(input);
97 fprintf(stderr, "Could not read from stdin\n");
98 return 0;
99}
100
James Zern061263a2012-05-11 16:00:57 -0700101int ExUtilReadFile(const char* const file_name,
102 const uint8_t** data, size_t* data_size) {
103 int ok;
104 void* file_data;
105 size_t file_size;
106 FILE* in;
skal2bcad892014-03-12 19:32:16 +0100107 const int from_stdin = (file_name == NULL) || !strcmp(file_name, "-");
James Zern061263a2012-05-11 16:00:57 -0700108
skal2bcad892014-03-12 19:32:16 +0100109 if (from_stdin) return ExUtilReadFromStdin(data, data_size);
110
111 if (data == NULL || data_size == NULL) return 0;
James Zern061263a2012-05-11 16:00:57 -0700112 *data = NULL;
113 *data_size = 0;
114
115 in = fopen(file_name, "rb");
116 if (in == NULL) {
117 fprintf(stderr, "cannot open input file '%s'\n", file_name);
118 return 0;
119 }
120 fseek(in, 0, SEEK_END);
121 file_size = ftell(in);
122 fseek(in, 0, SEEK_SET);
123 file_data = malloc(file_size);
124 if (file_data == NULL) return 0;
125 ok = (fread(file_data, file_size, 1, in) == 1);
126 fclose(in);
127
128 if (!ok) {
James Zern14d42af2013-03-20 16:59:35 -0700129 fprintf(stderr, "Could not read %d bytes of data from file %s\n",
130 (int)file_size, file_name);
James Zern061263a2012-05-11 16:00:57 -0700131 free(file_data);
132 return 0;
133 }
134 *data = (uint8_t*)file_data;
135 *data_size = file_size;
136 return 1;
137}
138
Urvang Joshie9a15a32012-11-07 14:41:15 -0800139int ExUtilWriteFile(const char* const file_name,
140 const uint8_t* data, size_t data_size) {
141 int ok;
142 FILE* out;
skal2bcad892014-03-12 19:32:16 +0100143 const int to_stdout = (file_name == NULL) || !strcmp(file_name, "-");
Urvang Joshie9a15a32012-11-07 14:41:15 -0800144
skal2bcad892014-03-12 19:32:16 +0100145 if (data == NULL) {
Urvang Joshie9a15a32012-11-07 14:41:15 -0800146 return 0;
147 }
skal2bcad892014-03-12 19:32:16 +0100148 out = to_stdout ? stdout : fopen(file_name, "wb");
Urvang Joshie9a15a32012-11-07 14:41:15 -0800149 if (out == NULL) {
150 fprintf(stderr, "Error! Cannot open output file '%s'\n", file_name);
151 return 0;
152 }
153 ok = (fwrite(data, data_size, 1, out) == 1);
skal2bcad892014-03-12 19:32:16 +0100154 if (out != stdout) fclose(out);
Urvang Joshie9a15a32012-11-07 14:41:15 -0800155 return ok;
156}
157
James Zern4a0e7392014-04-22 19:33:22 -0700158//------------------------------------------------------------------------------
159// WebP decoding
160
skal0b747b12014-07-21 15:44:43 +0200161static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
James Zern4a0e7392014-04-22 19:33:22 -0700162 "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
163 "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
164};
165
James Zern1b2fe142014-04-26 14:26:13 -0700166static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
167 if (config->input.has_animation) {
168 fprintf(stderr,
169 "Error! Decoding of an animated WebP file is not supported.\n"
170 " Use webpmux to extract the individual frames or\n"
171 " vwebp to view this image.\n");
172 }
173}
174
James Zern4a0e7392014-04-22 19:33:22 -0700175void ExUtilPrintWebPError(const char* const in_file, int status) {
176 fprintf(stderr, "Decoding of %s failed.\n", in_file);
177 fprintf(stderr, "Status: %d", status);
178 if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
179 fprintf(stderr, "(%s)", kStatusMessages[status]);
180 }
181 fprintf(stderr, "\n");
182}
183
184int ExUtilLoadWebP(const char* const in_file,
185 const uint8_t** data, size_t* data_size,
186 WebPBitstreamFeatures* bitstream) {
187 VP8StatusCode status;
Pascal Massiminof0b65c92014-04-26 01:10:52 -0700188 WebPBitstreamFeatures local_features;
James Zern4a0e7392014-04-22 19:33:22 -0700189 if (!ExUtilReadFile(in_file, data, data_size)) return 0;
190
Pascal Massiminof0b65c92014-04-26 01:10:52 -0700191 if (bitstream == NULL) {
192 bitstream = &local_features;
193 }
194
James Zern4a0e7392014-04-22 19:33:22 -0700195 status = WebPGetFeatures(*data, *data_size, bitstream);
196 if (status != VP8_STATUS_OK) {
197 free((void*)*data);
198 *data = NULL;
199 *data_size = 0;
200 ExUtilPrintWebPError(in_file, status);
201 return 0;
202 }
203 return 1;
204}
205
James Zern1b2fe142014-04-26 14:26:13 -0700206//------------------------------------------------------------------------------
207
James Zern4a0e7392014-04-22 19:33:22 -0700208VP8StatusCode ExUtilDecodeWebP(const uint8_t* const data, size_t data_size,
James Zern1b2fe142014-04-26 14:26:13 -0700209 int verbose, WebPDecoderConfig* const config) {
James Zern4a0e7392014-04-22 19:33:22 -0700210 Stopwatch stop_watch;
211 VP8StatusCode status = VP8_STATUS_OK;
James Zern1b2fe142014-04-26 14:26:13 -0700212 if (config == NULL) return VP8_STATUS_INVALID_PARAM;
James Zern4a0e7392014-04-22 19:33:22 -0700213
James Zern1b2fe142014-04-26 14:26:13 -0700214 PrintAnimationWarning(config);
James Zern4a0e7392014-04-22 19:33:22 -0700215
James Zern1b2fe142014-04-26 14:26:13 -0700216 StopwatchReset(&stop_watch);
James Zern4a0e7392014-04-22 19:33:22 -0700217
218 // Decoding call.
James Zern1b2fe142014-04-26 14:26:13 -0700219 status = WebPDecode(data, data_size, config);
220
221 if (verbose) {
222 const double decode_time = StopwatchReadAndReset(&stop_watch);
223 fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time);
224 }
225 return status;
226}
227
228VP8StatusCode ExUtilDecodeWebPIncremental(
229 const uint8_t* const data, size_t data_size,
230 int verbose, WebPDecoderConfig* const config) {
231 Stopwatch stop_watch;
232 VP8StatusCode status = VP8_STATUS_OK;
233 if (config == NULL) return VP8_STATUS_INVALID_PARAM;
234
235 PrintAnimationWarning(config);
236
237 StopwatchReset(&stop_watch);
238
239 // Decoding call.
240 {
James Zern4a0e7392014-04-22 19:33:22 -0700241 WebPIDecoder* const idec = WebPIDecode(data, data_size, config);
242 if (idec == NULL) {
243 fprintf(stderr, "Failed during WebPINewDecoder().\n");
244 return VP8_STATUS_OUT_OF_MEMORY;
245 } else {
246 status = WebPIUpdate(idec, data, data_size);
247 WebPIDelete(idec);
248 }
249 }
250
251 if (verbose) {
252 const double decode_time = StopwatchReadAndReset(&stop_watch);
253 fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time);
254 }
255 return status;
256}
257
258// -----------------------------------------------------------------------------