/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 | 10.9k | static void PrintAnimationWarning(const WebPDecoderConfig* const config) { |
44 | 10.9k | if (config->input.has_animation) { |
45 | 0 | fprintf(stderr, |
46 | 0 | "Error! Decoding of an animated WebP file is not supported.\n" |
47 | 0 | " Use webpmux to extract the individual frames or\n" |
48 | 0 | " vwebp to view this image.\n"); |
49 | 0 | } |
50 | 10.9k | } |
51 | | |
52 | 7.67k | void PrintWebPError(const char* const in_file, int status) { |
53 | 7.67k | WFPRINTF(stderr, "Decoding of %s failed.\n", (const W_CHAR*)in_file); |
54 | 7.67k | fprintf(stderr, "Status: %d", status); |
55 | 7.67k | if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) { |
56 | 7.67k | fprintf(stderr, "(%s)", kStatusMessages[status]); |
57 | 7.67k | } |
58 | 7.67k | fprintf(stderr, "\n"); |
59 | 7.67k | } |
60 | | |
61 | | int LoadWebP(const char* const in_file, const uint8_t** data, size_t* data_size, |
62 | 0 | WebPBitstreamFeatures* bitstream) { |
63 | 0 | VP8StatusCode status; |
64 | 0 | WebPBitstreamFeatures local_features; |
65 | 0 | if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0; |
66 | | |
67 | 0 | if (bitstream == NULL) { |
68 | 0 | bitstream = &local_features; |
69 | 0 | } |
70 | |
|
71 | 0 | status = WebPGetFeatures(*data, *data_size, bitstream); |
72 | 0 | if (status != VP8_STATUS_OK) { |
73 | 0 | WebPFree((void*)*data); |
74 | 0 | *data = NULL; |
75 | 0 | *data_size = 0; |
76 | 0 | PrintWebPError(in_file, status); |
77 | 0 | return 0; |
78 | 0 | } |
79 | 0 | return 1; |
80 | 0 | } |
81 | | |
82 | | //------------------------------------------------------------------------------ |
83 | | |
84 | | VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size, |
85 | 10.9k | WebPDecoderConfig* const config) { |
86 | 10.9k | if (config == NULL) return VP8_STATUS_INVALID_PARAM; |
87 | 10.9k | PrintAnimationWarning(config); |
88 | 10.9k | return WebPDecode(data, data_size, config); |
89 | 10.9k | } |
90 | | |
91 | | VP8StatusCode DecodeWebPIncremental(const uint8_t* const data, size_t data_size, |
92 | 0 | WebPDecoderConfig* const config) { |
93 | 0 | VP8StatusCode status = VP8_STATUS_OK; |
94 | 0 | if (config == NULL) return VP8_STATUS_INVALID_PARAM; |
95 | | |
96 | 0 | PrintAnimationWarning(config); |
97 | | |
98 | | // Decoding call. |
99 | 0 | { |
100 | 0 | WebPIDecoder* const idec = WebPIDecode(data, data_size, config); |
101 | 0 | if (idec == NULL) { |
102 | 0 | fprintf(stderr, "Failed during WebPIDecode().\n"); |
103 | 0 | return VP8_STATUS_OUT_OF_MEMORY; |
104 | 0 | } else { |
105 | 0 | status = WebPIUpdate(idec, data, data_size); |
106 | 0 | WebPIDelete(idec); |
107 | 0 | } |
108 | 0 | } |
109 | 0 | return status; |
110 | 0 | } |
111 | | |
112 | | // ----------------------------------------------------------------------------- |
113 | | // Metadata |
114 | | |
115 | | static int ExtractMetadata(const uint8_t* const data, size_t data_size, |
116 | 1.37k | Metadata* const metadata) { |
117 | 1.37k | WebPData webp_data = {data, data_size}; |
118 | 1.37k | WebPDemuxer* const demux = WebPDemux(&webp_data); |
119 | 1.37k | WebPChunkIterator chunk_iter; |
120 | 1.37k | uint32_t flags; |
121 | | |
122 | 1.37k | if (demux == NULL) return 0; |
123 | 1.37k | assert(metadata != NULL); |
124 | | |
125 | 235 | flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); |
126 | | |
127 | 235 | if ((flags & ICCP_FLAG) && WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter)) { |
128 | 0 | MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size, |
129 | 0 | &metadata->iccp); |
130 | 0 | WebPDemuxReleaseChunkIterator(&chunk_iter); |
131 | 0 | } |
132 | 235 | if ((flags & EXIF_FLAG) && WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) { |
133 | 0 | MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size, |
134 | 0 | &metadata->exif); |
135 | 0 | WebPDemuxReleaseChunkIterator(&chunk_iter); |
136 | 0 | } |
137 | 235 | if ((flags & XMP_FLAG) && WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter)) { |
138 | 0 | MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size, |
139 | 0 | &metadata->xmp); |
140 | 0 | WebPDemuxReleaseChunkIterator(&chunk_iter); |
141 | 0 | } |
142 | 235 | WebPDemuxDelete(demux); |
143 | 235 | return 1; |
144 | 1.37k | } |
145 | | |
146 | | // ----------------------------------------------------------------------------- |
147 | | |
148 | | int ReadWebP(const uint8_t* const data, size_t data_size, |
149 | 12.0k | WebPPicture* const pic, int keep_alpha, Metadata* const metadata) { |
150 | 12.0k | int ok = 0; |
151 | 12.0k | VP8StatusCode status = VP8_STATUS_OK; |
152 | 12.0k | WebPDecoderConfig config; |
153 | 12.0k | WebPDecBuffer* const output_buffer = &config.output; |
154 | 12.0k | WebPBitstreamFeatures* const bitstream = &config.input; |
155 | | |
156 | 12.0k | if (data == NULL || data_size == 0 || pic == NULL) return 0; |
157 | | |
158 | 12.0k | if (!WebPInitDecoderConfig(&config)) { |
159 | 0 | fprintf(stderr, "Library version mismatch!\n"); |
160 | 0 | return 0; |
161 | 0 | } |
162 | | |
163 | 12.0k | status = WebPGetFeatures(data, data_size, bitstream); |
164 | 12.0k | if (status != VP8_STATUS_OK) { |
165 | 186 | PrintWebPError("input data", status); |
166 | 186 | return 0; |
167 | 186 | } |
168 | | |
169 | 11.8k | do { |
170 | 11.8k | const int has_alpha = keep_alpha && bitstream->has_alpha; |
171 | 11.8k | uint64_t stride; |
172 | 11.8k | pic->width = bitstream->width; |
173 | 11.8k | pic->height = bitstream->height; |
174 | 11.8k | if (pic->use_argb) { |
175 | 5.86k | stride = (uint64_t)bitstream->width * 4; |
176 | 5.96k | } else { |
177 | 5.96k | stride = (uint64_t)bitstream->width * (has_alpha ? 5 : 3) / 2; |
178 | 5.96k | pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420; |
179 | 5.96k | } |
180 | | |
181 | 11.8k | if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, bitstream->height)) { |
182 | 5 | status = VP8_STATUS_OUT_OF_MEMORY; |
183 | 5 | break; |
184 | 5 | } |
185 | | |
186 | 11.8k | ok = WebPPictureAlloc(pic); |
187 | 11.8k | if (!ok) { |
188 | 865 | status = VP8_STATUS_OUT_OF_MEMORY; |
189 | 865 | break; |
190 | 865 | } |
191 | 10.9k | if (pic->use_argb) { |
192 | | #ifdef WORDS_BIGENDIAN |
193 | | output_buffer->colorspace = MODE_ARGB; |
194 | | #else |
195 | 5.44k | output_buffer->colorspace = MODE_BGRA; |
196 | 5.44k | #endif |
197 | 5.44k | output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb; |
198 | 5.44k | output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t); |
199 | 5.44k | output_buffer->u.RGBA.size = output_buffer->u.RGBA.stride * pic->height; |
200 | 5.51k | } else { |
201 | 5.51k | output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV; |
202 | 5.51k | output_buffer->u.YUVA.y = pic->y; |
203 | 5.51k | output_buffer->u.YUVA.u = pic->u; |
204 | 5.51k | output_buffer->u.YUVA.v = pic->v; |
205 | 5.51k | output_buffer->u.YUVA.a = has_alpha ? pic->a : NULL; |
206 | 5.51k | output_buffer->u.YUVA.y_stride = pic->y_stride; |
207 | 5.51k | output_buffer->u.YUVA.u_stride = pic->uv_stride; |
208 | 5.51k | output_buffer->u.YUVA.v_stride = pic->uv_stride; |
209 | 5.51k | output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0; |
210 | 5.51k | output_buffer->u.YUVA.y_size = pic->height * pic->y_stride; |
211 | 5.51k | output_buffer->u.YUVA.u_size = (pic->height + 1) / 2 * pic->uv_stride; |
212 | 5.51k | output_buffer->u.YUVA.v_size = (pic->height + 1) / 2 * pic->uv_stride; |
213 | 5.51k | output_buffer->u.YUVA.a_size = pic->height * pic->a_stride; |
214 | 5.51k | } |
215 | 10.9k | output_buffer->is_external_memory = 1; |
216 | | |
217 | 10.9k | status = DecodeWebP(data, data_size, &config); |
218 | 10.9k | ok = (status == VP8_STATUS_OK); |
219 | 10.9k | if (ok && !keep_alpha && pic->use_argb) { |
220 | | // Need to wipe out the alpha value, as requested. |
221 | 286 | int x, y; |
222 | 286 | uint32_t* argb = pic->argb; |
223 | 227k | for (y = 0; y < pic->height; ++y) { |
224 | 339M | for (x = 0; x < pic->width; ++x) argb[x] |= 0xff000000u; |
225 | 227k | argb += pic->argb_stride; |
226 | 227k | } |
227 | 286 | } |
228 | 10.9k | } while (0); // <- so we can 'break' out of the loop |
229 | | |
230 | 11.8k | if (status != VP8_STATUS_OK) { |
231 | 6.34k | PrintWebPError("input data", status); |
232 | 6.34k | ok = 0; |
233 | 6.34k | } |
234 | | |
235 | 11.8k | WebPFreeDecBuffer(output_buffer); |
236 | | |
237 | 11.8k | if (ok && metadata != NULL) { |
238 | 1.37k | ok = ExtractMetadata(data, data_size, metadata); |
239 | 1.37k | if (!ok) { |
240 | 1.14k | PrintWebPError("metadata", VP8_STATUS_BITSTREAM_ERROR); |
241 | 1.14k | } |
242 | 1.37k | } |
243 | 11.8k | if (!ok) WebPPictureFree(pic); |
244 | 11.8k | return ok; |
245 | 12.0k | } |
246 | | |
247 | | // ----------------------------------------------------------------------------- |