diff --git a/examples/cwebp.c b/examples/cwebp.c index 4c5af54b..bd60929c 100644 --- a/examples/cwebp.c +++ b/examples/cwebp.c @@ -1023,9 +1023,12 @@ int main(int argc, const char *argv[]) { if (verbose) { StopwatchReadAndReset(&stop_watch); } - if (crop != 0 && !WebPPictureCrop(&picture, crop_x, crop_y, crop_w, crop_h)) { - fprintf(stderr, "Error! Cannot crop picture\n"); - goto Error; + if (crop != 0) { + // We use self-cropping using a view. + if (!WebPPictureView(&picture, crop_x, crop_y, crop_w, crop_h, &picture)) { + fprintf(stderr, "Error! Cannot crop picture\n"); + goto Error; + } } if ((resize_w | resize_h) > 0) { if (!WebPPictureRescale(&picture, resize_w, resize_h)) { diff --git a/src/enc/picture.c b/src/enc/picture.c index 826800ec..082f72b4 100644 --- a/src/enc/picture.c +++ b/src/enc/picture.c @@ -21,6 +21,7 @@ extern "C" { #endif #define HALVE(x) (((x) + 1) >> 1) +#define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP)) //------------------------------------------------------------------------------ // WebPPicture @@ -86,6 +87,9 @@ int WebPPictureAlloc(WebPPicture* const picture) { mem = (uint8_t*)malloc((size_t)total_size); if (mem == NULL) return 0; + picture->memory_ = (void*)mem; + + // TODO(skal): we could align the y/u/v planes and adjust stride. picture->y = mem; mem += y_size; @@ -105,6 +109,7 @@ int WebPPictureAlloc(WebPPicture* const picture) { mem += uv0_size; } } else { + void* memory; const uint64_t argb_size = (uint64_t)width * height; const uint64_t total_size = argb_size * sizeof(*picture->argb); if (width <= 0 || height <= 0 || @@ -113,8 +118,12 @@ int WebPPictureAlloc(WebPPicture* const picture) { return 0; } WebPPictureFree(picture); // erase previous buffer - picture->argb = (uint32_t*)malloc((size_t)total_size); - if (picture->argb == NULL) return 0; + memory = malloc((size_t)total_size); + if (memory == NULL) return 0; + picture->memory_argb_ = memory; + + // TODO(skal): align plane to cache line? + picture->argb = (uint32_t*)memory; picture->argb_stride = width; } } @@ -125,18 +134,21 @@ int WebPPictureAlloc(WebPPicture* const picture) { // into 'dst'. Mark 'dst' as not owning any memory. 'src' can be NULL. static void WebPPictureGrabSpecs(const WebPPicture* const src, WebPPicture* const dst) { + assert(dst != NULL); if (src != NULL) *dst = *src; dst->y = dst->u = dst->v = NULL; dst->u0 = dst->v0 = NULL; dst->a = NULL; dst->argb = NULL; + dst->memory_ = NULL; + dst->memory_argb_ = NULL; } // Release memory owned by 'picture'. void WebPPictureFree(WebPPicture* const picture) { if (picture != NULL) { - free(picture->y); - free(picture->argb); + free(picture->memory_); + free(picture->memory_argb_); WebPPictureGrabSpecs(NULL, picture); } } @@ -154,6 +166,30 @@ static void CopyPlane(const uint8_t* src, int src_stride, } } +// Adjust top-left corner to chroma sample position. +static void SnapTopLeftPosition(const WebPPicture* const pic, + int* const left, int* const top) { + if (!pic->use_argb_input) { + const int is_yuv422 = IS_YUV_CSP(pic->colorspace, WEBP_YUV422); + if (IS_YUV_CSP(pic->colorspace, WEBP_YUV420) || is_yuv422) { + *left &= ~1; + if (!is_yuv422) *top &= ~1; + } + } +} + +// Adjust top-left corner and verify that the sub-rectangle is valid. +static int AdjustAndCheckRectangle(const WebPPicture* const pic, + int* const left, int* const top, + int width, int height) { + SnapTopLeftPosition(pic, left, top); + if ((*left) < 0 || (*top) < 0) return 0; + if (width <= 0 || height <= 0) return 0; + if ((*left) + width > pic->width) return 0; + if ((*top) + height > pic->height) return 0; + return 1; +} + int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { if (src == NULL || dst == NULL) return 0; if (src == dst) return 1; @@ -175,7 +211,7 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { #ifdef WEBP_EXPERIMENTAL_FEATURES if (dst->u0 != NULL) { int uv0_width = src->width; - if ((dst->colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { + if (IS_YUV_CSP(dst->colorspace, WEBP_YUV422)) { uv0_width = HALVE(uv0_width); } CopyPlane(src->u0, src->uv0_stride, @@ -192,6 +228,48 @@ int WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst) { return 1; } +int WebPPictureIsView(const WebPPicture* const picture) { + if (picture == NULL) return 0; + if (picture->use_argb_input) { + return (picture->memory_argb_ == NULL); + } + return (picture->memory_ == NULL); +} + +int WebPPictureView(const WebPPicture* const src, + int left, int top, int width, int height, + WebPPicture* const dst) { + if (src == NULL || dst == NULL) return 0; + + // verify rectangle position. + if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0; + + if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'. + WebPPictureGrabSpecs(src, dst); + } + dst->width = width; + dst->height = height; + if (!src->use_argb_input) { + dst->y = src->y + top * src->y_stride + left; + dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1); + dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1); + if (src->a != NULL) { + dst->a = src->a + top * src->a_stride + left; + } +#ifdef WEBP_EXPERIMENTAL_FEATURES + if (src->u0 != NULL) { + const int left_pos = + IS_YUV_CSP(dst->colorspace, WEBP_YUV422) ? (left >> 1) : left; + dst->u0 = src->u0 + top * src->uv0_stride + left_pos; + dst->v0 = src->v0 + top * src->uv0_stride + left_pos; + } +#endif + } else { + dst->argb = src->argb + top * src->argb_stride + left; + } + return 1; +} + //------------------------------------------------------------------------------ // Picture cropping @@ -200,9 +278,7 @@ int WebPPictureCrop(WebPPicture* const pic, WebPPicture tmp; if (pic == NULL) return 0; - if (width <= 0 || height <= 0) return 0; - if (left < 0 || ((left + width + 1) & ~1) > pic->width) return 0; - if (top < 0 || ((top + height + 1) & ~1) > pic->height) return 0; + if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0; WebPPictureGrabSpecs(pic, &tmp); tmp.width = width; @@ -227,15 +303,15 @@ int WebPPictureCrop(WebPPicture* const pic, #ifdef WEBP_EXPERIMENTAL_FEATURES if (tmp.u0 != NULL) { int w = width; - int l = left; - if (tmp.colorspace == WEBP_YUV422) { + int left_pos = left; + if (IS_YUV_CSP(tmp.colorspace, WEBP_YUV422)) { w = HALVE(w); - l = HALVE(l); + left_pos = HALVE(left_pos); } - CopyPlane(pic->u0 + top * pic->uv0_stride + l, pic->uv0_stride, - tmp.u0, tmp.uv0_stride, w, l); - CopyPlane(pic->v0 + top * pic->uv0_stride + l, pic->uv0_stride, - tmp.v0, tmp.uv0_stride, w, l); + CopyPlane(pic->u0 + top * pic->uv0_stride + left_pos, pic->uv0_stride, + tmp.u0, tmp.uv0_stride, w, height); + CopyPlane(pic->v0 + top * pic->uv0_stride + left_pos, pic->uv0_stride, + tmp.v0, tmp.uv0_stride, w, height); } #endif } else { @@ -323,10 +399,7 @@ int WebPPictureRescale(WebPPicture* const pic, int width, int height) { } #ifdef WEBP_EXPERIMENTAL_FEATURES if (tmp.u0 != NULL) { - int s = 1; - if ((tmp.colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { - s = 2; - } + const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1; RescalePlane( pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1); diff --git a/src/webp/encode.h b/src/webp/encode.h index a9b054b7..837cff0b 100644 --- a/src/webp/encode.h +++ b/src/webp/encode.h @@ -95,8 +95,8 @@ WEBP_EXTERN(int) WebPConfigInitInternal( WebPConfig* const, WebPPreset, float, int); // Should always be called, to initialize a fresh WebPConfig structure before -// modification. Returns 0 in case of version mismatch. WebPConfigInit() must -// have succeeded before using the 'config' object. +// modification. Returns false in case of version mismatch. WebPConfigInit() +// must have succeeded before using the 'config' object. static WEBP_INLINE int WebPConfigInit(WebPConfig* const config) { return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f, WEBP_ENCODER_ABI_VERSION); @@ -105,14 +105,15 @@ static WEBP_INLINE int WebPConfigInit(WebPConfig* const config) { // This function will initialize the configuration according to a predefined // set of parameters (referred to by 'preset') and a given quality factor. // This function can be called as a replacement to WebPConfigInit(). Will -// return 0 in case of error. +// return false in case of error. static WEBP_INLINE int WebPConfigPreset(WebPConfig* const config, WebPPreset preset, float quality) { return WebPConfigInitInternal(config, preset, quality, WEBP_ENCODER_ABI_VERSION); } -// Returns 1 if all parameters are in valid range and the configuration is OK. +// Returns true if 'config' is non-NULL and all configuration parameters are +// within their valid ranges. WEBP_EXTERN(int) WebPValidateConfig(const WebPConfig* const config); //------------------------------------------------------------------------------ @@ -140,7 +141,7 @@ typedef struct { // used during callbacks (like progress-report e.g.). } WebPAuxStats; -// Signature for output function. Should return 1 if writing was successful. +// Signature for output function. Should return true if writing was successful. // data/data_size is the segment of data to write, and 'picture' is for // reference (and so one can make use of picture->custom_ptr). typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size, @@ -162,8 +163,9 @@ WEBP_EXTERN(void) WebPMemoryWriterInit(WebPMemoryWriter* const writer); WEBP_EXTERN(int) WebPMemoryWrite(const uint8_t* data, size_t data_size, const WebPPicture* const picture); -// Progress hook, called from time to time to report progress. It can return 0 -// to request an abort of the encoding process, or 1 otherwise if all is OK. +// Progress hook, called from time to time to report progress. It can return +// false to request an abort of the encoding process, or true otherwise if +// everything is OK. typedef int (*WebPProgressHook)(int percent, const WebPPicture* const picture); typedef enum { @@ -237,13 +239,17 @@ struct WebPPicture { int use_argb_input; // Flag for encoder to use argb pixels as input. uint32_t* argb; // Pointer to argb (32 bit) plane. int argb_stride; // This is stride in pixels units, not bytes. + + // private fields: + void* memory_; // row chunk of memory for yuva planes + void* memory_argb_; // and for argb too. }; // Internal, version-checked, entry point WEBP_EXTERN(int) WebPPictureInitInternal(WebPPicture* const, int); -// Should always be called, to initialize the structure. Returns 0 in case of -// version mismatch. WebPPictureInit() must have succeeded before using the +// Should always be called, to initialize the structure. Returns false in case +// of version mismatch. WebPPictureInit() must have succeeded before using the // 'picture' object. static WEBP_INLINE int WebPPictureInit(WebPPicture* const picture) { return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION); @@ -255,21 +261,23 @@ static WEBP_INLINE int WebPPictureInit(WebPPicture* const picture) { // Convenience allocation / deallocation based on picture->width/height: // Allocate y/u/v buffers as per colorspace/width/height specification. // Note! This function will free the previous buffer if needed. -// Returns 0 in case of memory error. +// Returns false in case of memory error. WEBP_EXTERN(int) WebPPictureAlloc(WebPPicture* const picture); -// Release memory allocated by WebPPictureAlloc() or WebPPictureImport*() -// Note that this function does _not_ free the memory pointed to by 'picture'. +// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*(). +// Note that this function does _not_ free the memory used by the 'picture' +// object itself. WEBP_EXTERN(void) WebPPictureFree(WebPPicture* const picture); -// Copy the pixels of *src into *dst, using WebPPictureAlloc. -// Returns 0 in case of memory allocation error. +// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return, +// *dst will fully own the copied pixels (this is not a view). +// Returns false in case of memory allocation error. WEBP_EXTERN(int) WebPPictureCopy(const WebPPicture* const src, WebPPicture* const dst); // Compute PSNR or SSIM distortion between two pictures. // Result is in dB, stores in result[] in the Y/U/V/Alpha/All order. -// Returns 0 in case of error (pic1 and pic2 don't have same dimension, ...) +// Returns false in case of error (pic1 and pic2 don't have same dimension, ...) // Warning: this function is rather CPU-intensive. WEBP_EXTERN(int) WebPPictureDistortion( const WebPPicture* const pic1, const WebPPicture* const pic2, @@ -277,11 +285,33 @@ WEBP_EXTERN(int) WebPPictureDistortion( float result[5]); // self-crops a picture to the rectangle defined by top/left/width/height. -// Returns 0 in case of memory allocation error, or if the rectangle is +// Returns false in case of memory allocation error, or if the rectangle is // outside of the source picture. +// The rectangle for the view is defined by the top-left corner pixel +// coordinates (left, top) as well as its width and height. This rectangle +// must be fully be comprised inside the 'src' source picture. If the source +// picture uses the YUV420 colorspace, the top and left coordinates will be +// snapped to even values. WEBP_EXTERN(int) WebPPictureCrop(WebPPicture* const picture, int left, int top, int width, int height); +// Extracts a view from 'src' picture into 'dst'. The rectangle for the view +// is defined by the top-left corner pixel coordinates (left, top) as well +// as its width and height. This rectangle must be fully be comprised inside +// the 'src' source picture. If the source picture uses the YUV420 colorspace, +// the top and left coordinates will be snapped to even values. +// Picture 'src' must out-live 'dst' picture. Self-extraction of view is allowed +// ('src' equal to 'dst') as a mean of fast-cropping (but note that doing so, +// the original dimension will be lost). +// Returns false in case of memory allocation error or invalid parameters. +WEBP_EXTERN(int) WebPPictureView(const WebPPicture* const src, + int left, int top, int width, int height, + WebPPicture* const dst); + +// Returns true if the 'picture' is actually a view and therefore does +// not own the memory for pixels. +WEBP_EXTERN(int) WebPPictureIsView(const WebPPicture* const picture); + // Rescale a picture to new dimension width x height. // Now gamma correction is applied. // Returns false in case of error (invalid parameter or insufficient memory). @@ -291,7 +321,7 @@ WEBP_EXTERN(int) WebPPictureRescale(WebPPicture* const pic, // Colorspace conversion function to import RGB samples. // Previous buffer will be free'd, if any. // *rgb buffer should have a size of at least height * rgb_stride. -// Returns 0 in case of memory error. +// Returns false in case of memory error. WEBP_EXTERN(int) WebPPictureImportRGB( WebPPicture* const picture, const uint8_t* const rgb, int rgb_stride); // Same, but for RGBA buffer.