/src/libwebp/imageio/webpdec.c
Line | Count | Source |
1 | | // Copyright 2014 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // 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. |
8 | | // ----------------------------------------------------------------------------- |
9 | | // |
10 | | // WebP decode. |
11 | | |
12 | | #ifdef HAVE_CONFIG_H |
13 | | #include "webp/config.h" |
14 | | #endif |
15 | | |
16 | | #include <assert.h> |
17 | | #include <stdio.h> |
18 | | #include <stdlib.h> |
19 | | |
20 | | #include "../examples/unicode.h" |
21 | | #include "./imageio_util.h" |
22 | | #include "./metadata.h" |
23 | | #include "./webpdec.h" |
24 | | #include "webp/decode.h" |
25 | | #include "webp/demux.h" |
26 | | #include "webp/encode.h" |
27 | | #include "webp/mux_types.h" |
28 | | #include "webp/types.h" |
29 | | |
30 | | //------------------------------------------------------------------------------ |
31 | | // WebP decoding |
32 | | |
33 | | static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = { |
34 | | "OK", |
35 | | "OUT_OF_MEMORY", |
36 | | "INVALID_PARAM", |
37 | | "BITSTREAM_ERROR", |
38 | | "UNSUPPORTED_FEATURE", |
39 | | "SUSPENDED", |
40 | | "USER_ABORT", |
41 | | "NOT_ENOUGH_DATA"}; |
42 | | |
43 | 5.12k | static void PrintAnimationWarning(const WebPDecoderConfig* const config) { |
44 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
45 | | if (config->input.has_animation) { |
46 | | fprintf(stderr, |
47 | | "Error! Decoding of an animated WebP file is not supported.\n" |
48 | | " Use webpmux to extract the individual frames or\n" |
49 | | " vwebp to view this image.\n"); |
50 | | } |
51 | | #else |
52 | 5.12k | (void)config; |
53 | 5.12k | #endif |
54 | 5.12k | } |
55 | | |
56 | 5.60k | void PrintWebPError(const char* const in_file, int status) { |
57 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
58 | | WFPRINTF(stderr, "Decoding of %s failed.\n", (const W_CHAR*)in_file); |
59 | | fprintf(stderr, "Status: %d", status); |
60 | | if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) { |
61 | | fprintf(stderr, "(%s)", kStatusMessages[status]); |
62 | | } |
63 | | fprintf(stderr, "\n"); |
64 | | #else |
65 | 5.60k | (void)in_file; |
66 | 5.60k | (void)status; |
67 | 5.60k | #endif |
68 | 5.60k | } |
69 | | |
70 | | int LoadWebP(const char* const in_file, const uint8_t** data, size_t* data_size, |
71 | 0 | WebPBitstreamFeatures* bitstream) { |
72 | 0 | VP8StatusCode status; |
73 | 0 | WebPBitstreamFeatures local_features; |
74 | 0 | if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0; |
75 | | |
76 | 0 | if (bitstream == NULL) { |
77 | 0 | bitstream = &local_features; |
78 | 0 | } |
79 | |
|
80 | 0 | status = WebPGetFeatures(*data, *data_size, bitstream); |
81 | 0 | if (status != VP8_STATUS_OK) { |
82 | 0 | WebPFree((void*)*data); |
83 | 0 | *data = NULL; |
84 | 0 | *data_size = 0; |
85 | 0 | PrintWebPError(in_file, status); |
86 | 0 | return 0; |
87 | 0 | } |
88 | 0 | return 1; |
89 | 0 | } |
90 | | |
91 | | //------------------------------------------------------------------------------ |
92 | | |
93 | | VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size, |
94 | 5.12k | WebPDecoderConfig* const config) { |
95 | 5.12k | if (config == NULL) return VP8_STATUS_INVALID_PARAM; |
96 | 5.12k | PrintAnimationWarning(config); |
97 | 5.12k | return WebPDecode(data, data_size, config); |
98 | 5.12k | } |
99 | | |
100 | | VP8StatusCode DecodeWebPIncremental(const uint8_t* const data, size_t data_size, |
101 | 0 | WebPDecoderConfig* const config) { |
102 | 0 | VP8StatusCode status = VP8_STATUS_OK; |
103 | 0 | if (config == NULL) return VP8_STATUS_INVALID_PARAM; |
104 | | |
105 | 0 | PrintAnimationWarning(config); |
106 | | |
107 | | // Decoding call. |
108 | 0 | { |
109 | 0 | WebPIDecoder* const idec = WebPIDecode(data, data_size, config); |
110 | 0 | if (idec == NULL) { |
111 | 0 | fprintf(stderr, "Failed during WebPIDecode().\n"); |
112 | 0 | return VP8_STATUS_OUT_OF_MEMORY; |
113 | 0 | } else { |
114 | 0 | status = WebPIUpdate(idec, data, data_size); |
115 | 0 | WebPIDelete(idec); |
116 | 0 | } |
117 | 0 | } |
118 | 0 | return status; |
119 | 0 | } |
120 | | |
121 | | // ----------------------------------------------------------------------------- |
122 | | // Metadata |
123 | | |
124 | | static int ExtractMetadata(const uint8_t* const data, size_t data_size, |
125 | 1.26k | Metadata* const metadata) { |
126 | 1.26k | WebPData webp_data = {data, data_size}; |
127 | 1.26k | WebPDemuxer* const demux = WebPDemux(&webp_data); |
128 | 1.26k | WebPChunkIterator chunk_iter; |
129 | 1.26k | uint32_t flags; |
130 | 1.26k | int ok = 1; |
131 | | |
132 | 1.26k | if (demux == NULL) return 0; |
133 | 1.26k | assert(metadata != NULL); |
134 | | |
135 | 319 | flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); |
136 | | |
137 | 319 | if ((flags & ICCP_FLAG) && WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter)) { |
138 | 0 | ok = ok && MetadataCopy((const char*)chunk_iter.chunk.bytes, |
139 | 0 | chunk_iter.chunk.size, &metadata->iccp); |
140 | 0 | WebPDemuxReleaseChunkIterator(&chunk_iter); |
141 | 0 | } |
142 | 319 | if ((flags & EXIF_FLAG) && WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) { |
143 | 0 | ok = ok && MetadataCopy((const char*)chunk_iter.chunk.bytes, |
144 | 0 | chunk_iter.chunk.size, &metadata->exif); |
145 | 0 | WebPDemuxReleaseChunkIterator(&chunk_iter); |
146 | 0 | } |
147 | 319 | if ((flags & XMP_FLAG) && WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter)) { |
148 | 0 | ok = ok && MetadataCopy((const char*)chunk_iter.chunk.bytes, |
149 | 0 | chunk_iter.chunk.size, &metadata->xmp); |
150 | 0 | WebPDemuxReleaseChunkIterator(&chunk_iter); |
151 | 0 | } |
152 | 319 | WebPDemuxDelete(demux); |
153 | 319 | return ok; |
154 | 1.26k | } |
155 | | |
156 | | // ----------------------------------------------------------------------------- |
157 | | |
158 | | int ReadWebP(const uint8_t* const data, size_t data_size, |
159 | 5.92k | WebPPicture* const pic, int keep_alpha, Metadata* const metadata) { |
160 | 5.92k | int ok = 0; |
161 | 5.92k | VP8StatusCode status = VP8_STATUS_OK; |
162 | 5.92k | WebPDecoderConfig config; |
163 | 5.92k | WebPDecBuffer* const output_buffer = &config.output; |
164 | 5.92k | WebPBitstreamFeatures* const bitstream = &config.input; |
165 | | |
166 | 5.92k | if (data == NULL || data_size == 0 || pic == NULL) return 0; |
167 | | |
168 | 5.92k | if (!WebPInitDecoderConfig(&config)) { |
169 | 0 | fprintf(stderr, "Library version mismatch!\n"); |
170 | 0 | return 0; |
171 | 0 | } |
172 | | |
173 | 5.92k | status = WebPGetFeatures(data, data_size, bitstream); |
174 | 5.92k | if (status != VP8_STATUS_OK) { |
175 | 370 | PrintWebPError("input data", status); |
176 | 370 | return 0; |
177 | 370 | } |
178 | | |
179 | 5.55k | do { |
180 | 5.55k | const int has_alpha = keep_alpha && bitstream->has_alpha; |
181 | 5.55k | uint64_t stride; |
182 | 5.55k | pic->width = bitstream->width; |
183 | 5.55k | pic->height = bitstream->height; |
184 | 5.55k | if (pic->use_argb) { |
185 | 2.81k | stride = (uint64_t)bitstream->width * 4; |
186 | 2.81k | } else { |
187 | 2.73k | stride = (uint64_t)bitstream->width * (has_alpha ? 5 : 3) / 2; |
188 | 2.73k | pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; |
189 | 2.73k | } |
190 | | |
191 | 5.55k | if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, bitstream->height)) { |
192 | 0 | status = VP8_STATUS_OUT_OF_MEMORY; |
193 | 0 | break; |
194 | 0 | } |
195 | | |
196 | 5.55k | ok = WebPPictureAlloc(pic); |
197 | 5.55k | if (!ok) { |
198 | 428 | status = VP8_STATUS_OUT_OF_MEMORY; |
199 | 428 | break; |
200 | 428 | } |
201 | 5.12k | if (pic->use_argb) { |
202 | | #ifdef WORDS_BIGENDIAN |
203 | | output_buffer->colorspace = MODE_ARGB; |
204 | | #else |
205 | 2.61k | output_buffer->colorspace = MODE_BGRA; |
206 | 2.61k | #endif |
207 | 2.61k | output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb; |
208 | 2.61k | output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t); |
209 | 2.61k | output_buffer->u.RGBA.size = |
210 | 2.61k | (size_t)output_buffer->u.RGBA.stride * pic->height; |
211 | 2.61k | } else { |
212 | 2.50k | output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV; |
213 | 2.50k | output_buffer->u.YUVA.y = pic->y; |
214 | 2.50k | output_buffer->u.YUVA.u = pic->u; |
215 | 2.50k | output_buffer->u.YUVA.v = pic->v; |
216 | 2.50k | output_buffer->u.YUVA.a = has_alpha ? pic->a : NULL; |
217 | 2.50k | output_buffer->u.YUVA.y_stride = pic->y_stride; |
218 | 2.50k | output_buffer->u.YUVA.u_stride = pic->uv_stride; |
219 | 2.50k | output_buffer->u.YUVA.v_stride = pic->uv_stride; |
220 | 2.50k | output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0; |
221 | 2.50k | output_buffer->u.YUVA.y_size = (size_t)pic->height * pic->y_stride; |
222 | 2.50k | output_buffer->u.YUVA.u_size = |
223 | 2.50k | (size_t)(pic->height + 1) / 2 * pic->uv_stride; |
224 | 2.50k | output_buffer->u.YUVA.v_size = |
225 | 2.50k | (size_t)(pic->height + 1) / 2 * pic->uv_stride; |
226 | 2.50k | output_buffer->u.YUVA.a_size = (size_t)pic->height * pic->a_stride; |
227 | 2.50k | } |
228 | 5.12k | output_buffer->is_external_memory = 1; |
229 | | |
230 | 5.12k | status = DecodeWebP(data, data_size, &config); |
231 | 5.12k | ok = (status == VP8_STATUS_OK); |
232 | 5.12k | if (ok && !keep_alpha && pic->use_argb) { |
233 | | // Need to wipe out the alpha value, as requested. |
234 | 286 | int x, y; |
235 | 286 | uint32_t* argb = pic->argb; |
236 | 264k | for (y = 0; y < pic->height; ++y) { |
237 | 4.98M | for (x = 0; x < pic->width; ++x) argb[x] |= 0xff000000u; |
238 | 264k | argb += pic->argb_stride; |
239 | 264k | } |
240 | 286 | } |
241 | 5.12k | } while (0); // <- so we can 'break' out of the loop |
242 | | |
243 | 5.55k | if (status != VP8_STATUS_OK) { |
244 | 4.28k | PrintWebPError("input data", status); |
245 | 4.28k | ok = 0; |
246 | 4.28k | } |
247 | | |
248 | 5.55k | WebPFreeDecBuffer(output_buffer); |
249 | | |
250 | 5.55k | if (ok && metadata != NULL) { |
251 | 1.26k | ok = ExtractMetadata(data, data_size, metadata); |
252 | 1.26k | if (!ok) { |
253 | 943 | PrintWebPError("metadata", VP8_STATUS_BITSTREAM_ERROR); |
254 | 943 | } |
255 | 1.26k | } |
256 | 5.55k | if (!ok) WebPPictureFree(pic); |
257 | 5.55k | return ok; |
258 | 5.92k | } |
259 | | |
260 | | // ----------------------------------------------------------------------------- |