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