diff --git a/examples/cwebp.c b/examples/cwebp.c index 085ca808..13bae46d 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -637,6 +637,21 @@ static void HelpLong(void) { printf("\n"); } +//----------------------------------------------------------------------------- +// Error messages + +static const char* const kErrorMessages[] = { + "OK", + "OUT_OF_MEMORY: Out of memory allocating objects", + "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer", + "NULL_PARAMETER: NULL parameter passed to function", + "INVALID_CONFIGURATION: configuration is invalid", + "BAD_DIMENSION: Bad picture dimension", + "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k", + "PARTITION_OVERFLOW: Partition is too big to fir 16M", + "BAD_WRITE: Picture writer returned an error" +}; + //----------------------------------------------------------------------------- int main(int argc, const char *argv[]) { @@ -833,6 +848,8 @@ int main(int argc, const char *argv[]) { } if (!WebPEncode(&config, &picture)) { fprintf(stderr, "Error! Cannot encode picture as WebP\n"); + fprintf(stderr, "Error code: %d (%s)\n", + picture.error_code, kErrorMessages[picture.error_code]); goto Error; } if (verbose) { diff --git a/src/enc/syntax.c b/src/enc/syntax.c index 073d72ee..8ab89338 100644 --- a/src/enc/syntax.c +++ b/src/enc/syntax.c @@ -39,21 +39,22 @@ static void PutLE32(uint8_t* const data, uint32_t val) { } static int PutHeader(int profile, size_t size0, size_t total_size, - const WebPPicture* const pic) { + WebPPicture* const pic) { uint8_t buf[KHEADER_SIZE]; uint8_t RIFF[KRIFF_SIZE] = { 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', ' ' }; uint32_t bits; - if (size0 >= MAX_PARTITION0_SIZE) { - return 0; // partition #0 is too big to fit + if (size0 >= MAX_PARTITION0_SIZE) { // partition #0 is too big to fit + return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION0_OVERFLOW); } PutLE32(RIFF + 4, total_size + KSIZE_OFFSET); PutLE32(RIFF + 16, total_size); - if (!pic->writer(RIFF, sizeof(RIFF), pic)) - return 0; + if (!pic->writer(RIFF, sizeof(RIFF), pic)) { + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); + } bits = 0 // keyframe (1b) | (profile << 1) // profile (3b) @@ -138,13 +139,13 @@ static void PutQuant(VP8BitWriter* const bw, // Partition sizes static int EmitPartitionsSize(const VP8Encoder* const enc, - const WebPPicture* const pic) { + WebPPicture* const pic) { uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)]; int p; for (p = 0; p < enc->num_parts_ - 1; ++p) { const size_t part_size = VP8BitWriterSize(enc->parts_ + p); if (part_size >= MAX_PARTITION_SIZE) { - return 0; // partition is too big to fit + return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW); } buf[3 * p + 0] = (part_size >> 0) & 0xff; buf[3 * p + 1] = (part_size >> 8) & 0xff; @@ -167,6 +168,7 @@ static int WriteExtensions(VP8Encoder* const enc) { const int kTrailerSize = 8; uint8_t buffer[kTrailerSize]; VP8BitWriter* const bw = &enc->bw_; + WebPPicture* const pic = enc->pic_; // Layer (bytes 0..3) PutLE24(buffer + 0, enc->layer_data_size_); @@ -176,7 +178,7 @@ static int WriteExtensions(VP8Encoder* const enc) { // append layer data to last partition if (!VP8BitWriterAppend(&enc->parts_[enc->num_parts_ - 1], enc->layer_data_, enc->layer_data_size_)) { - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY); } } // Alpha (bytes 4..6) @@ -184,17 +186,18 @@ static int WriteExtensions(VP8Encoder* const enc) { if (enc->alpha_data_size_ > 0) { assert(enc->has_alpha_); if (!VP8BitWriterAppend(bw, enc->alpha_data_, enc->alpha_data_size_)) { - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY); } } buffer[kTrailerSize - 1] = 0x01; // marker if (!VP8BitWriterAppend(bw, buffer, kTrailerSize)) { - return 0; + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY); } return 1; } -#endif + +#endif /* WEBP_EXPERIMENTAL_FEATURES */ //----------------------------------------------------------------------------- diff --git a/src/enc/vp8enci.h b/src/enc/vp8enci.h index 0f8d71f4..47607500 100644 --- a/src/enc/vp8enci.h +++ b/src/enc/vp8enci.h @@ -417,6 +417,9 @@ int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd); int VP8EncLoop(VP8Encoder* const enc); int VP8StatLoop(VP8Encoder* const enc); + // in webpenc.c +// Assign an error code to a picture. Return false for convenience. +int WebPEncodingSetError(WebPPicture* const pic, WebPEncodingError error); // in analysis.c // Compute susceptibility based on DCT-coeff histograms: // the higher, the "easier" the macroblock is to compress. diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index 8b0fe935..819dd639 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -9,6 +9,7 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include #include #include #include @@ -53,6 +54,7 @@ int WebPPictureInitInternal(WebPPicture* const picture, int version) { if (picture) { memset(picture, 0, sizeof(*picture)); picture->writer = DummyWriter; + WebPEncodingSetError(picture, VP8_ENC_OK); } return 1; } @@ -194,7 +196,10 @@ static VP8Encoder* InitEncoder(const WebPConfig* const config, printf("===================================\n"); #endif mem = (uint8_t*)malloc(size); - if (mem == NULL) return NULL; + if (mem == NULL) { + WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); + return NULL; + } enc = (VP8Encoder*)mem; mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc)); memset(enc, 0, sizeof(*enc)); @@ -296,25 +301,35 @@ static void StoreStats(VP8Encoder* const enc) { } } +int WebPEncodingSetError(WebPPicture* const pic, WebPEncodingError error) { + assert((int)error <= VP8_ENC_ERROR_BAD_WRITE); + assert((int)error >= VP8_ENC_OK); + pic->error_code = error; + return 0; +} + //----------------------------------------------------------------------------- int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { VP8Encoder* enc; int ok; - if (config == NULL || pic == NULL) - return 0; // bad params + if (pic == NULL) + return 0; + WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far + if (config == NULL) // bad params + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); if (!WebPValidateConfig(config)) - return 0; // invalid config. + return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); if (pic->width <= 0 || pic->height <= 0) - return 0; // invalid parameters + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); if (pic->y == NULL || pic->u == NULL || pic->v == NULL) - return 0; // invalid parameters + return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); if (pic->width >= MAX_DIMENSION || pic->height >= MAX_DIMENSION) - return 0; // image is too big + return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); enc = InitEncoder(config, pic); - if (enc == NULL) return 0; + if (enc == NULL) return 0; // pic->error is already set. ok = VP8EncAnalyze(enc) && VP8StatLoop(enc) && VP8EncLoop(enc) @@ -325,6 +340,7 @@ int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) { && VP8EncWrite(enc); StoreStats(enc); DeleteEncoder(enc); + return ok; } diff --git a/src/webp/encode.h b/src/webp/encode.h index 8b55605f..29dc5b53 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -146,6 +146,19 @@ typedef enum { WEBP_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present } WebPEncCSP; +// Encoding error conditions. +typedef enum { + VP8_ENC_OK = 0, + VP8_ENC_ERROR_OUT_OF_MEMORY, // memory error allocating objects + VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, // memory error while flushing bits + VP8_ENC_ERROR_NULL_PARAMETER, // a pointer parameter is NULL + VP8_ENC_ERROR_INVALID_CONFIGURATION, // configuration is invalid + VP8_ENC_ERROR_BAD_DIMENSION, // picture has invalid width/height + VP8_ENC_ERROR_PARTITION0_OVERFLOW, // partition is too bigger than 16M + VP8_ENC_ERROR_PARTITION_OVERFLOW, // partition is too bigger than 512k + VP8_ENC_ERROR_BAD_WRITE, // error while flushing bytes +} WebPEncodingError; + struct WebPPicture { // input WebPEncCSP colorspace; // colorspace: should be YUV420 for now (=Y'CbCr). @@ -175,6 +188,8 @@ struct WebPPicture { // original samples (for non-YUV420 modes) uint8_t *u0, *v0; int uv0_stride; + + WebPEncodingError error_code; // error code in case of problem. }; // Internal, version-checked, entry point @@ -238,6 +253,7 @@ int WebPPictureImportBGRA(WebPPicture* const picture, // 'picture' must be less than 16384x16384 in dimension, and the 'config' object // must be a valid one. // Returns false in case of error, true otherwise. +// In case of error, picture->error_code is updated accordingly. int WebPEncode(const WebPConfig* const config, WebPPicture* const picture); //-----------------------------------------------------------------------------