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