//------------------------------------------------------------------------------ // WebPPicture //------------------------------------------------------------------------------ static int DummyWriter(byte* data, uint data_size, WebPPicture* picture) { // The following are to prevent 'unused variable' error message. (void)data; (void)data_size; (void)picture; return 1; }
// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them // into 'dst'. Mark 'dst' as not owning any memory. 'src' can be null. static void WebPPictureGrabSpecs(WebPPicture* src, WebPPicture* dst) { if (src != null) *dst = *src; dst.y = dst.u = dst.v = null; dst.u0 = dst.v0 = null; dst.a = null; }
int WebPPictureRescale(WebPPicture* pic, int width, int height) { WebPPicture tmp; int prev_width, prev_height; int* work; if (pic == null) return 0; prev_width = pic.width; prev_height = pic.height; // if width is unspecified, scale original proportionally to height ratio. if (width == 0) { width = (prev_width * height + prev_height / 2) / prev_height; } // if height is unspecified, scale original proportionally to width ratio. if (height == 0) { height = (prev_height * width + prev_width / 2) / prev_width; } // Check if the overall dimensions still make sense. if (width <= 0 || height <= 0) return 0; WebPPictureGrabSpecs(pic, &tmp); tmp.width = width; tmp.height = height; if (!WebPPictureAlloc(&tmp)) return 0; work = (int*)malloc(2 * width * sizeof(int)); if (work == null) { WebPPictureFree(&tmp); return 0; } RescalePlane(pic.y, prev_width, prev_height, pic.y_stride, tmp.y, width, height, tmp.y_stride, work); RescalePlane(pic.u, HALVE(prev_width), HALVE(prev_height), pic.uv_stride, tmp.u, HALVE(width), HALVE(height), tmp.uv_stride, work); RescalePlane(pic.v, HALVE(prev_width), HALVE(prev_height), pic.uv_stride, tmp.v, HALVE(width), HALVE(height), tmp.uv_stride, work); if (tmp.a != null) { RescalePlane(pic.a, prev_width, prev_height, pic.a_stride, tmp.a, width, height, tmp.a_stride, work); } #if WEBP_EXPERIMENTAL_FEATURES if (tmp.u0 != null) { int s = 1; if ((tmp.colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { s = 2; } RescalePlane( pic.u0, (prev_width + s / 2) / s, prev_height, pic.uv0_stride, tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work); RescalePlane( pic.v0, (prev_width + s / 2) / s, prev_height, pic.uv0_stride, tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work); } #endif WebPPictureFree(pic); free(work); *pic = tmp; return 1; }
//------------------------------------------------------------------------------ // WebPPicture //------------------------------------------------------------------------------ int WebPPictureAlloc(ref WebPPicture picture) { if (picture != null) { WebPEncCSP uv_csp = picture.colorspace & WEBP_CSP_UV_MASK; int has_alpha = picture.colorspace & WEBP_CSP_ALPHA_BIT; int width = picture.width; int height = picture.height; int y_stride = width; int uv_width = HALVE(width); int uv_height = HALVE(height); int uv_stride = uv_width; int uv0_stride = 0; int a_width, a_stride; ulong y_size, uv_size, uv0_size, a_size, total_size; byte* mem; // U/V switch (uv_csp) { case WEBP_YUV420: break; #if WEBP_EXPERIMENTAL_FEATURES case WEBP_YUV400: // for now, we'll just reset the U/V samples break; case WEBP_YUV422: uv0_stride = uv_width; break; case WEBP_YUV444: uv0_stride = width; break; #endif default: return 0; } uv0_size = height * uv0_stride; // alpha a_width = has_alpha ? width : 0; a_stride = a_width; y_size = (ulong)y_stride * height; uv_size = (ulong)uv_stride * uv_height; a_size = (ulong)a_stride * height; total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size; // Security and validation checks if (width <= 0 || height <= 0 || // check for luma/alpha param error uv_width < 0 || uv_height < 0 || // check for u/v param error y_size >= (1ULL << 40) || // check for reasonable global size (uint)total_size != total_size) { // check for overflow on 32bit return 0; } picture.y_stride = y_stride; picture.uv_stride = uv_stride; picture.a_stride = a_stride; picture.uv0_stride = uv0_stride; WebPPictureFree(picture); // erase previous buffer mem = (byte*)malloc((uint)total_size); if (mem == null) return 0; picture.y = mem; mem += y_size; picture.u = mem; mem += uv_size; picture.v = mem; mem += uv_size; if (a_size) { picture.a = mem; mem += a_size; } if (uv0_size) { picture.u0 = mem; mem += uv0_size; picture.v0 = mem; mem += uv0_size; } } return 1; }
//------------------------------------------------------------------------------ // Picture cropping int WebPPictureCrop(WebPPicture* pic, int left, int top, int width, int height) { 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; WebPPictureGrabSpecs(pic, &tmp); tmp.width = width; tmp.height = height; if (!WebPPictureAlloc(&tmp)) return 0; { int y_offset = top * pic.y_stride + left; int uv_offset = (top / 2) * pic.uv_stride + left / 2; CopyPlane(pic.y + y_offset, pic.y_stride, tmp.y, tmp.y_stride, width, height); CopyPlane(pic.u + uv_offset, pic.uv_stride, tmp.u, tmp.uv_stride, HALVE(width), HALVE(height)); CopyPlane(pic.v + uv_offset, pic.uv_stride, tmp.v, tmp.uv_stride, HALVE(width), HALVE(height)); } if (tmp.a != null) { int a_offset = top * pic.a_stride + left; CopyPlane(pic.a + a_offset, pic.a_stride, tmp.a, tmp.a_stride, width, height); } #if WEBP_EXPERIMENTAL_FEATURES if (tmp.u0 != null) { int w = width; int l = left; if (tmp.colorspace == WEBP_YUV422) { w = HALVE(w); l = HALVE(l); } 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); } #endif WebPPictureFree(pic); *pic = tmp; return 1; }
int WebPPictureCopy(WebPPicture* src, WebPPicture* dst) { if (src == null || dst == null) return 0; if (src == dst) return 1; WebPPictureGrabSpecs(src, dst); if (!WebPPictureAlloc(dst)) return 0; CopyPlane(src.y, src.y_stride, dst.y, dst.y_stride, dst.width, dst.height); CopyPlane(src.u, src.uv_stride, dst.u, dst.uv_stride, HALVE(dst.width), HALVE(dst.height)); CopyPlane(src.v, src.uv_stride, dst.v, dst.uv_stride, HALVE(dst.width), HALVE(dst.height)); if (dst.a != null) { CopyPlane(src.a, src.a_stride, dst.a, dst.a_stride, dst.width, dst.height); } #if WEBP_EXPERIMENTAL_FEATURES if (dst.u0 != null) { int uv0_width = src.width; if ((dst.colorspace & WEBP_CSP_UV_MASK) == WEBP_YUV422) { uv0_width = HALVE(uv0_width); } CopyPlane(src.u0, src.uv0_stride, dst.u0, dst.uv0_stride, uv0_width, dst.height); CopyPlane(src.v0, src.uv0_stride, dst.v0, dst.uv0_stride, uv0_width, dst.height); } #endif return 1; }
// Release memory owned by 'picture'. void WebPPictureFree(WebPPicture* picture) { if (picture != null) { free(picture.y); WebPPictureGrabSpecs(null, picture); } }
// Memory scaling with dimensions: // memory (bytes) ~= 2.25 * w + 0.0625 * w * h // // Typical memory footprint (768x510 picture) // Memory used: // encoder: 33919 // block cache: 2880 // info: 3072 // preds: 24897 // top samples: 1623 // non-zero: 196 // lf-stats: 2048 // total: 68635 // Transcient object sizes: // VP8EncIterator: 352 // VP8ModeScore: 912 // VP8SegmentInfo: 532 // VP8Proba: 31032 // LFStats: 2048 // Picture size (yuv): 589824 static VP8Encoder* InitEncoder(WebPConfig* config, WebPPicture* picture) { int use_filter = (config.filter_strength > 0) || (config.autofilter > 0); int mb_w = (picture.width + 15) >> 4; int mb_h = (picture.height + 15) >> 4; int preds_w = 4 * mb_w + 1; int preds_h = 4 * mb_h + 1; uint preds_size = preds_w * preds_h * sizeof(byte); int top_stride = mb_w * 16; uint nz_size = (mb_w + 1) * sizeof(uint); uint cache_size = (3 * YUV_SIZE + PRED_SIZE) * sizeof(byte); uint info_size = mb_w * mb_h * sizeof(VP8MBInfo); uint samples_size = (2 * top_stride + // top-luma/u/v 16 + 16 + 16 + 8 + 1 + // left y/u/v 2 * ALIGN_CST) // align all * sizeof(byte); uint lf_stats_size = config.autofilter ? sizeof(LFStats) + ALIGN_CST : 0; VP8Encoder* enc; byte* mem; uint size = sizeof(VP8Encoder) + ALIGN_CST // main struct + cache_size // working caches + info_size // modes info + preds_size // prediction modes + samples_size // top/left samples + nz_size // coeff context bits + lf_stats_size; // autofilter stats #if PRINT_MEMORY_INFO printf("===================================\n"); printf("Memory used:\n" " encoder: %ld\n" " block cache: %ld\n" " info: %ld\n" " preds: %ld\n" " top samples: %ld\n" " non-zero: %ld\n" " lf-stats: %ld\n" " total: %ld\n", sizeof(VP8Encoder) + ALIGN_CST, cache_size, info_size, preds_size, samples_size, nz_size, lf_stats_size, size); printf("Transcient object sizes:\n" " VP8EncIterator: %ld\n" " VP8ModeScore: %ld\n" " VP8SegmentInfo: %ld\n" " VP8Proba: %ld\n" " LFStats: %ld\n", sizeof(VP8EncIterator), sizeof(VP8ModeScore), sizeof(VP8SegmentInfo), sizeof(VP8Proba), sizeof(LFStats)); printf("Picture size (yuv): %ld\n", mb_w * mb_h * 384 * sizeof(byte)); printf("===================================\n"); #endif mem = (byte*)malloc(size); if (mem == null) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); return null; } enc = (VP8Encoder*)mem; mem = (byte*)DO_ALIGN(mem + sizeof(*enc)); memset(enc, 0, sizeof(*enc)); enc.num_parts_ = 1 << config.partitions; enc.mb_w_ = mb_w; enc.mb_h_ = mb_h; enc.preds_w_ = preds_w; enc.yuv_in_ = (byte*)mem; mem += YUV_SIZE; enc.yuv_out_ = (byte*)mem; mem += YUV_SIZE; enc.yuv_out2_ = (byte*)mem; mem += YUV_SIZE; enc.yuv_p_ = (byte*)mem; mem += PRED_SIZE; enc.mb_info_ = (VP8MBInfo*)mem; mem += info_size; enc.preds_ = ((byte*)mem) + 1 + enc.preds_w_; mem += preds_w * preds_h * sizeof(byte); enc.nz_ = 1 + (uint*)mem; mem += nz_size; enc.lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : null; mem += lf_stats_size; // top samples (all 16-aligned) mem = (byte*)DO_ALIGN(mem); enc.y_top_ = (byte*)mem; enc.uv_top_ = enc.y_top_ + top_stride; mem += 2 * top_stride; mem = (byte*)DO_ALIGN(mem + 1); enc.y_left_ = (byte*)mem; mem += 16 + 16; enc.u_left_ = (byte*)mem; mem += 16; enc.v_left_ = (byte*)mem; mem += 8; enc.config_ = config; enc.profile_ = use_filter ? ((config.filter_type == 1) ? 0 : 1) : 2; enc.pic_ = picture; enc.percent_ = 0; MapConfigToTools(enc); VP8EncDspInit(); VP8DefaultProbas(enc); ResetSegmentHeader(enc); ResetFilterHeader(enc); ResetBoundaryPredictions(enc); VP8EncInitAlpha(enc); #if WEBP_EXPERIMENTAL_FEATURES VP8EncInitLayer(enc); #endif return enc; }
int WebPPictureInitInternal(WebPPicture* picture, int version) { if (version != WEBP_ENCODER_ABI_VERSION) { return 0; // caller/system version mismatch! } if (picture) { memset(picture, 0, sizeof(*picture)); picture.writer = DummyWriter; WebPEncodingSetError(picture, VP8_ENC_OK); } return 1; }
int WebPEncodingSetError(WebPPicture* pic, WebPEncodingError error) { assert((int)error < VP8_ENC_ERROR_LAST); assert((int)error >= VP8_ENC_OK); pic.error_code = error; return 0; }
//------------------------------------------------------------------------------ int WebPEncode(WebPConfig* config, WebPPicture* pic) { VP8Encoder* enc; int ok; 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 WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); if (pic.width <= 0 || pic.height <= 0) return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); if (pic.y == null || pic.u == null || pic.v == null) return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); if (pic.width > WEBP_MAX_DIMENSION || pic.height > WEBP_MAX_DIMENSION) return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); enc = InitEncoder(config, pic); if (enc == null) return 0; // pic.error is already set. // Note: each of the tasks below account for 20% in the progress report. ok = VP8EncAnalyze(enc) && VP8StatLoop(enc) && VP8EncLoop(enc) && VP8EncFinishAlpha(enc) #if WEBP_EXPERIMENTAL_FEATURES && VP8EncFinishLayer(enc) #endif && VP8EncWrite(enc); StoreStats(enc); if (!ok) { VP8EncFreeBitWriters(enc); } DeleteEncoder(enc); return ok; }
static int PutPaddingByte(WebPPicture* pic) { byte pad_byte[1] = { 0 };