diff --git a/PIL/WebPImagePlugin.py b/PIL/WebPImagePlugin.py index 78a7a5319..8e5d71e31 100644 --- a/PIL/WebPImagePlugin.py +++ b/PIL/WebPImagePlugin.py @@ -56,6 +56,26 @@ def _save(im, fp, filename): quality = im.encoderinfo.get("quality", 80) icc_profile = im.encoderinfo.get("icc_profile", "") exif = im.encoderinfo.get("exif", "") + preset = im.encoderinfo.get("preset", 0) # 0:default 1:pict 2:photo 3:draw 4:icon 5:text + method = im.encoderinfo.get("method", 4) + target_size = im.encoderinfo.get("target_size", 0) + target_PSNR = im.encoderinfo.get("target_PSNR", 0.0) + segments = im.encoderinfo.get("segments", 4) + sns_strength = im.encoderinfo.get("sns_strength", 50) + filter_strength = im.encoderinfo.get("filter_strength", 60) + filter_sharpness = im.encoderinfo.get("filter_sharpness", 0) + filter_type = im.encoderinfo.get("filter_type", 1) # 0:simple 1:strong + autofilter = im.encoderinfo.get("autofilter", False) + alpha_compression = im.encoderinfo.get("alpha_compression", True) + alpha_filtering = im.encoderinfo.get("alpha_filtering", 1) # 0:none 1:fast 2:best + alpha_quality = im.encoderinfo.get("alpha_quality", 100) + pass_count = im.encoderinfo.get("pass", 1) # 1-10 + preprocessing = im.encoderinfo.get("preprocessing", 0) # 0:none 1:segment-smooth 2:pseudo-random dithering + partitions = im.encoderinfo.get("partitions", 0) # 0-3 + partition_limit = im.encoderinfo.get("partition_limit", 0) # 0-100 + emulate_jpeg_size = im.encoderinfo.get("emulate_jpeg_size", False) + thread_level = im.encoderinfo.get("thread_level", 0) + low_memory = im.encoderinfo.get("low_memory", False) data = _webp.WebPEncode( im.tobytes(), @@ -65,7 +85,27 @@ def _save(im, fp, filename): float(quality), im.mode, icc_profile, - exif + exif, + preset, + method, + target_size, + float(target_PSNR), + segments, + sns_strength, + filter_strength, + filter_sharpness, + filter_type, + autofilter, + alpha_compression, + alpha_filtering, + alpha_quality, + pass_count, + preprocessing, + partitions, + partition_limit, + emulate_jpeg_size, + thread_level, + low_memory ) if data is None: raise IOError("cannot write file as WEBP (encoder returned None)") diff --git a/_webp.c b/_webp.c index a8c6d40af..b18532002 100644 --- a/_webp.c +++ b/_webp.c @@ -9,12 +9,85 @@ #include #endif -PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) -{ + +struct option { int width; int height; int lossless; - float quality_factor; + float quality; + int preset; + int method; + int target_size; + float target_PSNR; + int segments; + int sns_strength; + int filter_strength; + int filter_sharpness; + int filter_type; + int autofilter; + int alpha_compression; + int alpha_filtering; + int alpha_quality; + int pass; + int preprocessing; + int partitions; + int partition_limit; + int emulate_jpeg_size; + int thread_level; + int low_memory; +} opt; + +int getSizeAfterEncode(struct option *opt, uint8_t** rgb, uint8_t** output, int is_rgba) { + WebPConfig config; + if (!WebPConfigPreset(&config, opt->preset, opt->quality)) return 0; + config.lossless = opt->lossless; + if (opt->preset == 0) { + config.method = opt->method; + config.target_size = opt->target_size; + config.target_PSNR = opt->target_PSNR; + config.segments = opt->segments; + config.sns_strength = opt->sns_strength; + config.filter_strength = opt->filter_strength; + config.filter_sharpness = opt->filter_sharpness; + config.filter_type = opt->filter_type; + config.autofilter = opt->autofilter; + config.alpha_compression = opt->alpha_compression; + config.alpha_filtering = opt->alpha_filtering; + config.alpha_quality = opt->alpha_quality; + config.pass = opt->pass; + config.preprocessing = opt->preprocessing; + config.partitions = opt->partitions; + config.partition_limit = opt->partition_limit; + config.emulate_jpeg_size = opt->emulate_jpeg_size; + config.thread_level = opt->thread_level; + config.low_memory = opt->low_memory; + } + + WebPPicture pic; + WebPPictureInit(&pic); + + WebPMemoryWriter wrt; + WebPMemoryWriterInit(&wrt); + pic.use_argb = !!opt->lossless; + pic.writer = WebPMemoryWrite; + pic.custom_ptr = &wrt; + pic.width = opt->width; + pic.height = opt->height; + + if (is_rgba) { + if (!WebPPictureImportRGBA(&pic, *rgb, 4* opt->width)) return 0; + } else { + if (!WebPPictureImportRGB(&pic, *rgb, 3* opt->width)) return 0; + } + + WebPEncode(&config, &pic); + WebPPictureFree(&pic); + (*output) = wrt.mem; + return wrt.size; +} + +PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) +{ uint8_t *rgb; uint8_t *icc_bytes; uint8_t *exif_bytes; @@ -22,37 +95,60 @@ PyObject* WebPEncode_wrapper(PyObject* self, PyObject* args) char *mode; Py_ssize_t size; Py_ssize_t icc_size; - Py_ssize_t exif_size; + Py_ssize_t exif_size; size_t ret_size; - if (!PyArg_ParseTuple(args, "s#iiifss#s#", - (char**)&rgb, &size, &width, &height, &lossless, &quality_factor, &mode, - &icc_bytes, &icc_size, &exif_bytes, &exif_size)) { + if (!PyArg_ParseTuple(args, "s#iiifss#s#iiifiiiiiiiiiiiiiiii", + (char**)&rgb, &size, + &opt.width, &opt.height, &opt.lossless, + &opt.quality, + &mode, + &icc_bytes, &icc_size, &exif_bytes, &exif_size, + &opt.preset, &opt.method, &opt.target_size, &opt.target_PSNR, + &opt.segments, &opt.sns_strength, + &opt.filter_strength, &opt.filter_sharpness, &opt.filter_type, + &opt.autofilter, + &opt.alpha_compression, &opt.alpha_filtering, &opt.alpha_quality, + &opt.pass, &opt.preprocessing, &opt.partitions, &opt.partition_limit, + &opt.emulate_jpeg_size, &opt.thread_level, &opt.low_memory + )) { Py_RETURN_NONE; } if (strcmp(mode, "RGBA")==0){ - if (size < width * height * 4){ + if (size < opt.width * opt.height * 4){ Py_RETURN_NONE; } #if WEBP_ENCODER_ABI_VERSION >= 0x0100 - if (lossless) { - ret_size = WebPEncodeLosslessRGBA(rgb, width, height, 4* width, &output); + if (opt.lossless) { + ret_size = getSizeAfterEncode(&opt, &rgb, &output, 1); + if (!ret_size) { + Py_RETURN_NONE; + } } else #endif { - ret_size = WebPEncodeRGBA(rgb, width, height, 4* width, quality_factor, &output); + ret_size = getSizeAfterEncode(&opt, &rgb, &output, 1); + if (!ret_size) { + Py_RETURN_NONE; + } } } else if (strcmp(mode, "RGB")==0){ - if (size < width * height * 3){ + if (size < opt.width * opt.height * 3){ Py_RETURN_NONE; } #if WEBP_ENCODER_ABI_VERSION >= 0x0100 - if (lossless) { - ret_size = WebPEncodeLosslessRGB(rgb, width, height, 3* width, &output); + if (opt.lossless) { + ret_size = getSizeAfterEncode(&opt, &rgb, &output, 0); + if (!ret_size) { + Py_RETURN_NONE; + } } else #endif { - ret_size = WebPEncodeRGB(rgb, width, height, 3* width, quality_factor, &output); + ret_size = getSizeAfterEncode(&opt, &rgb, &output, 0); + if (!ret_size) { + Py_RETURN_NONE; + } } } else { Py_RETURN_NONE;